From 6ca07eca689b477e95b1bef17321aec75de93ae1 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 22 Mar 2019 12:40:12 +0800 Subject: [PATCH 001/486] esp_common: add version definitions in code and build system Closes https://github.com/espressif/esp-idf/issues/2482 Closes IDF-253 --- .gitlab-ci.yml | 12 ++++ .../esp_common/include/esp_idf_version.h | 58 +++++++++++++++++ components/esp_common/include/esp_system.h | 9 +-- docs/Doxyfile | 2 + docs/en/api-guides/build-system-cmake.rst | 1 + docs/en/api-guides/build-system.rst | 1 + docs/en/api-reference/system/system.rst | 16 +++++ docs/zh_CN/api-guides/build-system-cmake.rst | 1 + docs/zh_CN/api-guides/build-system.rst | 1 + make/common.mk | 3 + make/project.mk | 2 +- make/version.mk | 3 + tools/ci/check_idf_version.sh | 62 +++++++++++++++++++ tools/ci/executable-list.txt | 1 + tools/cmake/idf.cmake | 1 + tools/cmake/version.cmake | 3 + 16 files changed, 167 insertions(+), 9 deletions(-) create mode 100644 components/esp_common/include/esp_idf_version.h create mode 100644 make/version.mk create mode 100755 tools/ci/check_idf_version.sh create mode 100644 tools/cmake/version.cmake diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 48a9624ef3..6ca788c665 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -855,6 +855,18 @@ check_permissions: script: - tools/ci/check-executable.sh +check_version: + <<: *check_job_template + # Don't run this for feature/bugfix branches, so that it is possible to modify + # esp_idf_version.h in a branch before tagging the next version. + only: + - master + - /^release\/v/ + - /^v\d+\.\d+(\.\d+)?($|-)/ + script: + - export IDF_PATH=$PWD + - tools/ci/check_idf_version.sh + check_examples_cmake_make: <<: *check_job_template except: diff --git a/components/esp_common/include/esp_idf_version.h b/components/esp_common/include/esp_idf_version.h new file mode 100644 index 0000000000..52601ca8f6 --- /dev/null +++ b/components/esp_common/include/esp_idf_version.h @@ -0,0 +1,58 @@ +// Copyright 2019 Espressif Systems (Shanghai) PTE LTD +// +// 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 + +#ifdef __cplusplus +extern "C" { +#endif + +/** Major version number (X.x.x) */ +#define ESP_IDF_VERSION_MAJOR 4 +/** Minor version number (x.X.x) */ +#define ESP_IDF_VERSION_MINOR 0 +/** Patch version number (x.x.X) */ +#define ESP_IDF_VERSION_PATCH 0 + +/** + * Macro to convert IDF version number into an integer + * + * To be used in comparisons, such as ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0) + */ +#define ESP_IDF_VERSION_VAL(major, minor, patch) ((major << 16) | (minor << 8) | (patch)) + +/** + * Current IDF version, as an integer + * + * To be used in comparisons, such as ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0) + */ +#define ESP_IDF_VERSION ESP_IDF_VERSION_VAL(ESP_IDF_VERSION_MAJOR, \ + ESP_IDF_VERSION_MINOR, \ + ESP_IDF_VERSION_PATCH) + +/** + * Return full IDF version string, same as 'git describe' output. + * + * @note If you are printing the ESP-IDF version in a log file or other information, + * this function provides more information than using the numerical version macros. + * For example, numerical version macros don't differentiate between development, + * pre-release and release versions, but the output of this function does. + * + * @return constant string from IDF_VER + */ +const char* esp_get_idf_version(void); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_common/include/esp_system.h b/components/esp_common/include/esp_system.h index 97dc916222..13c8f198df 100644 --- a/components/esp_common/include/esp_system.h +++ b/components/esp_common/include/esp_system.h @@ -20,6 +20,7 @@ #include "esp_err.h" #include "esp_attr.h" #include "esp_bit_defs.h" +#include "esp_idf_version.h" #include "sdkconfig.h" @@ -311,14 +312,6 @@ esp_err_t esp_derive_local_mac(uint8_t* local_mac, const uint8_t* universal_mac) const char* system_get_sdk_version(void) __attribute__ ((deprecated)); /** @endcond */ -/** - * Get IDF version - * - * @return constant string from IDF_VER - */ -const char* esp_get_idf_version(void); - - /** * @brief Chip models */ diff --git a/docs/Doxyfile b/docs/Doxyfile index cd92c69096..0673440bdc 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -179,6 +179,8 @@ INPUT = \ ## Base MAC address ## NOTE: for line below header_file.inc is not used ../../components/esp_common/include/esp_system.h \ + ## IDF version + ../../components/esp_common/include/esp_idf_version.h \ ## ## ULP Coprocessor - API Guides ## diff --git a/docs/en/api-guides/build-system-cmake.rst b/docs/en/api-guides/build-system-cmake.rst index a3c068bf90..b524481221 100644 --- a/docs/en/api-guides/build-system-cmake.rst +++ b/docs/en/api-guides/build-system-cmake.rst @@ -350,6 +350,7 @@ from the component CMakeLists.txt: - ``PROJECT_DIR``: Absolute path of the project directory containing the project CMakeLists. Same as the ``CMAKE_SOURCE_DIR`` variable. - ``COMPONENTS``: Names of all components that are included in this build, formatted as a semicolon-delimited CMake list. - ``IDF_VER``: Git version of ESP-IDF (produced by ``git describe``) +- ``IDF_VERSION_MAJOR``, ``IDF_VERSION_MINOR``, ``IDF_VERSION_PATCH``: Components of ESP-IDF version, to be used in conditional expressions. Note that this information is less precise than that provided by ``IDF_VER`` variable. ``v4.0-dev-*``, ``v4.0-beta1``, ``v4.0-rc1`` and ``v4.0`` will all have the same values of ``IDF_VERSION_*`` variables, but different ``IDF_VER`` values. - ``IDF_TARGET``: Name of the target for which the project is being built. - ``PROJECT_VER``: Project version. diff --git a/docs/en/api-guides/build-system.rst b/docs/en/api-guides/build-system.rst index 8c85c65d3c..5f936c05f3 100644 --- a/docs/en/api-guides/build-system.rst +++ b/docs/en/api-guides/build-system.rst @@ -188,6 +188,7 @@ The following variables are set at the project level, but exported for use in th - ``CC``, ``LD``, ``AR``, ``OBJCOPY``: Full paths to each tool from the gcc xtensa cross-toolchain. - ``HOSTCC``, ``HOSTLD``, ``HOSTAR``: Full names of each tool from the host native toolchain. - ``IDF_VER``: ESP-IDF version, retrieved from either ``$(IDF_PATH)/version.txt`` file (if present) else using git command ``git describe``. Recommended format here is single liner that specifies major IDF release version, e.g. ``v2.0`` for a tagged release or ``v2.0-275-g0efaa4f`` for an arbitrary commit. Application can make use of this by calling :cpp:func:`esp_get_idf_version`. +- ``IDF_VERSION_MAJOR``, ``IDF_VERSION_MINOR``, ``IDF_VERSION_PATCH``: Components of ESP-IDF version, to be used in conditional expressions. Note that this information is less precise than that provided by ``IDF_VER`` variable. ``v4.0-dev-*``, ``v4.0-beta1``, ``v4.0-rc1`` and ``v4.0`` will all have the same values of ``ESP_IDF_VERSION_*`` variables, but different ``IDF_VER`` values. - ``PROJECT_VER``: Project version. * If ``PROJECT_VER`` variable is set in project Makefile file, its value will be used. diff --git a/docs/en/api-reference/system/system.rst b/docs/en/api-reference/system/system.rst index a15860e8a1..719452c3f5 100644 --- a/docs/en/api-reference/system/system.rst +++ b/docs/en/api-reference/system/system.rst @@ -109,6 +109,21 @@ SDK version :cpp:func:`esp_get_idf_version` returns a string describing the IDF version which was used to compile the application. This is the same value as the one available through ``IDF_VER`` variable of the build system. The version string generally has the format of ``git describe`` output. +To get the version at build time, additional version macros are provided. They can be used to enable or disable parts of the program depending on IDF version. + +* :c:macro:`ESP_IDF_VERSION_MAJOR`, :c:macro:`ESP_IDF_VERSION_MINOR`, :c:macro:`ESP_IDF_VERSION_PATCH` are defined to integers representing major, minor, and patch version. + +* :c:macro:`ESP_IDF_VERSION_VAL` and :c:macro:`ESP_IDF_VERSION` can be used when implementing version checks: + + .. code-block:: c + + #include "esp_idf_version.h" + + #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0) + // enable functionality present in IDF v4.0 + #endif + + App version ----------- Application version is stored in :cpp:class:`esp_app_desc_t` structure. It is located in DROM sector and has a fixed offset from the beginning of the binary file. @@ -125,5 +140,6 @@ API Reference ------------- .. include:: /_build/inc/esp_system.inc +.. include:: /_build/inc/esp_idf_version.inc diff --git a/docs/zh_CN/api-guides/build-system-cmake.rst b/docs/zh_CN/api-guides/build-system-cmake.rst index 96b46aa926..43935a3ae5 100644 --- a/docs/zh_CN/api-guides/build-system-cmake.rst +++ b/docs/zh_CN/api-guides/build-system-cmake.rst @@ -315,6 +315,7 @@ ESP-IDF 在搜索所有待构建的组件时,会按照 ``COMPONENT_DIRS`` 指 - ``COMPONENTS``:此次构建中包含的所有组件的名称,具体格式为用分号隔开的 CMake 列表。 - ``CONFIG_*``:项目配置中的每个值在 cmake 中都对应一个以 ``CONFIG_`` 开头的变量。更多详细信息请参阅 :doc:`Kconfig `。 - ``IDF_VER``:ESP-IDF 的 git 版本号,由 ``git describe`` 命令生成。 +- ``IDF_VERSION_MAJOR``, ``IDF_VERSION_MINOR``, ``IDF_VERSION_PATCH``: ESP-IDF 的组件版本,可用于条件表达式。请注意这些信息的精确度不如 ``IDF_VER`` 变量,版本号 ``v4.0-dev-*``, ``v4.0-beta1``, ``v4.0-rc1`` 和 ``v4.0`` 对应的 ``IDF_VERSION_*`` 变量值是相同的,但是 ``IDF_VER`` 的值是不同的。 - ``IDF_TARGET``:项目的硬件目标名称。 - ``PROJECT_VER``:项目版本号。 diff --git a/docs/zh_CN/api-guides/build-system.rst b/docs/zh_CN/api-guides/build-system.rst index 366f7d79f6..60bba24ef0 100644 --- a/docs/zh_CN/api-guides/build-system.rst +++ b/docs/zh_CN/api-guides/build-system.rst @@ -176,6 +176,7 @@ ESP-IDF 搜索组件时,会按照 ``COMPONENT_DIRS`` 指定的顺序依次进 - ``CC``,``LD``,``AR``,``OBJCOPY``: gcc xtensa 交叉编译工具链中每个工具的完整路径。 - ``HOSTCC``,``HOSTLD``,``HOSTAR``: 主机本地工具链中每个工具的全名。 - ``IDF_VER``: ESP-IDF 的版本号,可以通过检索 ``$(IDF_PATH)/version.txt`` 文件(假如存在的话)或者使用 git 命令 ``git describe`` 来获取。这里推荐的格式是在一行中指定主 IDF 的发布版本号,例如标记为 ``v2.0`` 的发布版本或者是标记任意一次提交记录的 ``v2.0-275-g0efaa4f``。应用程序可以通过调用 :cpp:func:`esp_get_idf_version` 函数来使用该变量。 +- ``IDF_VERSION_MAJOR``, ``IDF_VERSION_MINOR``, ``IDF_VERSION_PATCH``: ESP-IDF 的组件版本,可用于条件表达式。请注意这些信息的精确度不如 ``IDF_VER`` 变量,版本号 ``v4.0-dev-*``, ``v4.0-beta1``, ``v4.0-rc1`` 和 ``v4.0`` 对应的 ``IDF_VERSION_*`` 变量值是相同的,但是 ``IDF_VER`` 的值是不同的。 如果您在 ``component.mk`` 文件中修改这些变量,这并不会影响其它组件的构建,但可能会使您的组件变得难以构建或调试。 diff --git a/make/common.mk b/make/common.mk index 6280ca4a87..bb0000a101 100644 --- a/make/common.mk +++ b/make/common.mk @@ -38,6 +38,9 @@ ifdef CONFIG_SDK_MAKE_WARN_UNDEFINED_VARIABLES MAKEFLAGS += --warn-undefined-variables endif +# Get version variables +include $(IDF_PATH)/make/version.mk + # General make utilities # convenience variable for printing an 80 asterisk wide separator line diff --git a/make/project.mk b/make/project.mk index 2f1def70d2..bd375afaf6 100644 --- a/make/project.mk +++ b/make/project.mk @@ -126,7 +126,7 @@ export PROJECT_PATH endif # A list of the "common" makefiles, to use as a target dependency -COMMON_MAKEFILES := $(abspath $(IDF_PATH)/make/project.mk $(IDF_PATH)/make/common.mk $(IDF_PATH)/make/component_wrapper.mk $(firstword $(MAKEFILE_LIST))) +COMMON_MAKEFILES := $(abspath $(IDF_PATH)/make/project.mk $(IDF_PATH)/make/common.mk $(IDF_PATH)/make/version.mk $(IDF_PATH)/make/component_wrapper.mk $(firstword $(MAKEFILE_LIST))) export COMMON_MAKEFILES # The directory where we put all objects/libraries/binaries. The project Makefile can diff --git a/make/version.mk b/make/version.mk new file mode 100644 index 0000000000..9dcda422af --- /dev/null +++ b/make/version.mk @@ -0,0 +1,3 @@ +IDF_VERSION_MAJOR := 4 +IDF_VERSION_MINOR := 0 +IDF_VERSION_PATCH := 0 diff --git a/tools/ci/check_idf_version.sh b/tools/ci/check_idf_version.sh new file mode 100755 index 0000000000..36fdf003f2 --- /dev/null +++ b/tools/ci/check_idf_version.sh @@ -0,0 +1,62 @@ +#!/bin/bash + +set -u +set -e + +echo "Checking if IDF version in esp_idf_version.h matches 'git describe' output" + +function get_ver_from_header() { + # Get the 3rd word from '#define ESP_IDF_VERSION_X ' line. + grep -E "^#define ${1}" components/esp_common/include/esp_idf_version.h | awk '{print $3;}' +} + +function get_ver_from_make() { + grep -E "^${1} :=" make/version.mk | awk '{print $3;}' +} + +function get_ver_from_cmake() { + grep -E "^set\(${1}" tools/cmake/version.cmake | sed -En "s/set\(${1} ([0-9])\)/\1/p" +} + +header_ver_major=$(get_ver_from_header ESP_IDF_VERSION_MAJOR) +header_ver_minor=$(get_ver_from_header ESP_IDF_VERSION_MINOR) +header_ver_patch=$(get_ver_from_header ESP_IDF_VERSION_PATCH) +version_from_header="${header_ver_major}.${header_ver_minor}.${header_ver_patch}" + +make_ver_major=$(get_ver_from_make IDF_VERSION_MAJOR) +make_ver_minor=$(get_ver_from_make IDF_VERSION_MINOR) +make_ver_patch=$(get_ver_from_make IDF_VERSION_PATCH) +version_from_make="${make_ver_major}.${make_ver_minor}.${make_ver_patch}" + +cmake_ver_major=$(get_ver_from_cmake IDF_VERSION_MAJOR) +cmake_ver_minor=$(get_ver_from_cmake IDF_VERSION_MINOR) +cmake_ver_patch=$(get_ver_from_cmake IDF_VERSION_PATCH) +version_from_cmake="${cmake_ver_major}.${cmake_ver_minor}.${cmake_ver_patch}" + +git_desc=$(git describe --tags) +git_desc_regex="^v([0-9]+)\.([0-9]+)(\.([0-9]+))?.*$" +if [[ ! ${git_desc} =~ ${git_desc_regex} ]]; then + echo "Could not determine the version from 'git describe' output: ${git_desc}" + exit 1 +fi +version_from_git="${BASH_REMATCH[1]}.${BASH_REMATCH[2]}.${BASH_REMATCH[4]:-0}" + +echo "From esp_idf_version.h: ${version_from_header}" +echo "From version.mk: ${version_from_make}" +echo "From version.cmake: ${version_from_cmake}" +echo "From git describe: ${version_from_git}" + +if [[ "${version_from_header}" != "${version_from_git}" ]]; then + echo "esp_idf_version.h does not match 'git describe' output" + exit 1 +fi + +if [[ "${version_from_make}" != "${version_from_git}" ]]; then + echo "version.mk does not match 'git describe' output" + exit 1 +fi + +if [[ "${version_from_cmake}" != "${version_from_git}" ]]; then + echo "version.cmake does not match 'git describe' output" + exit 1 +fi diff --git a/tools/ci/executable-list.txt b/tools/ci/executable-list.txt index 14071ab2eb..49bb916268 100644 --- a/tools/ci/executable-list.txt +++ b/tools/ci/executable-list.txt @@ -21,6 +21,7 @@ docs/gen-version-specific-includes.py tools/ci/apply_bot_filter.py tools/ci/build_examples.sh tools/ci/build_examples_cmake.sh +tools/ci/check_idf_version.sh tools/ci/check-executable.sh tools/ci/check-line-endings.sh tools/ci/checkout_project_ref.py diff --git a/tools/cmake/idf.cmake b/tools/cmake/idf.cmake index 9d45fd9ddf..e97d83fcd9 100644 --- a/tools/cmake/idf.cmake +++ b/tools/cmake/idf.cmake @@ -38,6 +38,7 @@ if(NOT __idf_env_set) include(utilities) include(targets) include(ldgen) + include(version) __build_init("${idf_path}") diff --git a/tools/cmake/version.cmake b/tools/cmake/version.cmake new file mode 100644 index 0000000000..265ab33b7b --- /dev/null +++ b/tools/cmake/version.cmake @@ -0,0 +1,3 @@ +set(IDF_VERSION_MAJOR 4) +set(IDF_VERSION_MINOR 0) +set(IDF_VERSION_PATCH 0) From 6b08e8b44966fadba83e09cbc47f6f86bac03e51 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 20 May 2019 19:07:28 +0800 Subject: [PATCH 002/486] esp_timer: handle esp_timer_delete in timer task Closes https://github.com/espressif/esp-idf/issues/3458 --- components/esp32/test/test_esp_timer.c | 55 ++++++++++++++++++++++ components/esp_common/src/esp_timer.c | 63 ++++++++------------------ 2 files changed, 74 insertions(+), 44 deletions(-) diff --git a/components/esp32/test/test_esp_timer.c b/components/esp32/test/test_esp_timer.c index b46cca5608..e3444503a1 100644 --- a/components/esp32/test/test_esp_timer.c +++ b/components/esp32/test/test_esp_timer.c @@ -536,6 +536,61 @@ TEST_CASE("Can delete timer from callback", "[esp_timer]") vSemaphoreDelete(args.notify_from_timer_cb); } + +typedef struct { + SemaphoreHandle_t delete_start; + SemaphoreHandle_t delete_done; + SemaphoreHandle_t test_done; + esp_timer_handle_t timer; +} timer_delete_test_args_t; + +static void timer_delete_task(void* arg) +{ + timer_delete_test_args_t* args = (timer_delete_test_args_t*) arg; + xSemaphoreTake(args->delete_start, portMAX_DELAY); + printf("Deleting the timer\n"); + esp_timer_delete(args->timer); + printf("Timer deleted\n"); + xSemaphoreGive(args->delete_done); + vTaskDelete(NULL); +} + +static void timer_delete_test_callback(void* arg) +{ + timer_delete_test_args_t* args = (timer_delete_test_args_t*) arg; + printf("Timer callback called\n"); + xSemaphoreGive(args->delete_start); + xSemaphoreTake(args->delete_done, portMAX_DELAY); + printf("Callback complete\n"); + xSemaphoreGive(args->test_done); +} + +TEST_CASE("Can delete timer from a separate task, triggered from callback", "[esp_timer]") +{ + timer_delete_test_args_t args = { + .delete_start = xSemaphoreCreateBinary(), + .delete_done = xSemaphoreCreateBinary(), + .test_done = xSemaphoreCreateBinary(), + }; + + esp_timer_create_args_t timer_args = { + .callback = &timer_delete_test_callback, + .arg = &args + }; + esp_timer_handle_t timer; + TEST_ESP_OK(esp_timer_create(&timer_args, &timer)); + args.timer = timer; + + xTaskCreate(timer_delete_task, "deleter", 4096, &args, 5, NULL); + + esp_timer_start_once(timer, 100); + TEST_ASSERT(xSemaphoreTake(args.test_done, pdMS_TO_TICKS(1000))); + + vSemaphoreDelete(args.delete_done); + vSemaphoreDelete(args.delete_start); + vSemaphoreDelete(args.test_done); +} + TEST_CASE("esp_timer_impl_advance moves time base correctly", "[esp_timer]") { ref_clock_init(); diff --git a/components/esp_common/src/esp_timer.c b/components/esp_common/src/esp_timer.c index 616cc25458..82a2d0b85b 100644 --- a/components/esp_common/src/esp_timer.c +++ b/components/esp_common/src/esp_timer.c @@ -39,12 +39,17 @@ #endif #include "sys/queue.h" +#define EVENT_ID_DELETE_TIMER 0xF0DE1E1E + #define TIMER_EVENT_QUEUE_SIZE 16 struct esp_timer { uint64_t alarm; uint64_t period; - esp_timer_cb_t callback; + union { + esp_timer_cb_t callback; + uint32_t event_id; + }; void* arg; #if WITH_PROFILING const char* name; @@ -77,23 +82,18 @@ static LIST_HEAD(esp_timer_list, esp_timer) s_timers = // all the timers static LIST_HEAD(esp_inactive_timer_list, esp_timer) s_inactive_timers = LIST_HEAD_INITIALIZER(s_timers); -// used to keep track of the timer when executing the callback -static esp_timer_handle_t s_timer_in_callback; #endif // task used to dispatch timer callbacks static TaskHandle_t s_timer_task; // counting semaphore used to notify the timer task from ISR static SemaphoreHandle_t s_timer_semaphore; -// mutex which protects timers from deletion during callback execution -static SemaphoreHandle_t s_timer_delete_mutex; #if CONFIG_SPIRAM_USE_MALLOC -// memory for s_timer_semaphore and s_timer_delete_mutex +// memory for s_timer_semaphore static StaticQueue_t s_timer_semaphore_memory; -static StaticQueue_t s_timer_delete_mutex_memory; #endif -// lock protecting s_timers, s_inactive_timers, s_timer_in_callback +// lock protecting s_timers, s_inactive_timers static portMUX_TYPE s_timer_lock = portMUX_INITIALIZER_UNLOCKED; @@ -164,15 +164,10 @@ esp_err_t esp_timer_delete(esp_timer_handle_t timer) if (timer_armed(timer)) { return ESP_ERR_INVALID_STATE; } - xSemaphoreTakeRecursive(s_timer_delete_mutex, portMAX_DELAY); -#if WITH_PROFILING - if (timer == s_timer_in_callback) { - s_timer_in_callback = NULL; - } - timer_remove_inactive(timer); -#endif - free(timer); - xSemaphoreGiveRecursive(s_timer_delete_mutex); + timer->event_id = EVENT_ID_DELETE_TIMER; + timer->alarm = esp_timer_get_time() + 50; + timer->period = 0; + timer_insert(timer); return ESP_OK; } @@ -267,13 +262,17 @@ static void timer_process_alarm(esp_timer_dispatch_t dispatch_method) /* unused, provision to allow running callbacks from ISR */ (void) dispatch_method; - xSemaphoreTakeRecursive(s_timer_delete_mutex, portMAX_DELAY); timer_list_lock(); uint64_t now = esp_timer_impl_get_time(); esp_timer_handle_t it = LIST_FIRST(&s_timers); while (it != NULL && it->alarm < now) { LIST_REMOVE(it, list_entry); + if (it->event_id == EVENT_ID_DELETE_TIMER) { + free(it); + it = LIST_FIRST(&s_timers); + continue; + } if (it->period > 0) { it->alarm += it->period; timer_insert(it); @@ -285,21 +284,14 @@ static void timer_process_alarm(esp_timer_dispatch_t dispatch_method) } #if WITH_PROFILING uint64_t callback_start = now; - s_timer_in_callback = it; #endif timer_list_unlock(); (*it->callback)(it->arg); timer_list_lock(); now = esp_timer_impl_get_time(); #if WITH_PROFILING - /* The callback might have deleted the timer. - * If this happens, esp_timer_delete will set s_timer_in_callback - * to NULL. - */ - if (s_timer_in_callback) { - s_timer_in_callback->times_triggered++; - s_timer_in_callback->total_callback_run_time += now - callback_start; - } + it->times_triggered++; + it->total_callback_run_time += now - callback_start; #endif it = LIST_FIRST(&s_timers); } @@ -308,7 +300,6 @@ static void timer_process_alarm(esp_timer_dispatch_t dispatch_method) esp_timer_impl_set_alarm(first->alarm); } timer_list_unlock(); - xSemaphoreGiveRecursive(s_timer_delete_mutex); } static void timer_task(void* arg) @@ -356,18 +347,6 @@ esp_err_t esp_timer_init(void) goto out; } -#if CONFIG_SPIRAM_USE_MALLOC - memset(&s_timer_delete_mutex_memory, 0, sizeof(StaticQueue_t)); - s_timer_delete_mutex = xSemaphoreCreateRecursiveMutexStatic(&s_timer_delete_mutex_memory); -#else - s_timer_delete_mutex = xSemaphoreCreateRecursiveMutex(); -#endif - if (!s_timer_delete_mutex) { - err = ESP_ERR_NO_MEM; - goto out; - } - - int ret = xTaskCreatePinnedToCore(&timer_task, "esp_timer", ESP_TASK_TIMER_STACK, NULL, ESP_TASK_TIMER_PRIO, &s_timer_task, PRO_CPU_NUM); if (ret != pdPASS) { @@ -391,10 +370,6 @@ out: vSemaphoreDelete(s_timer_semaphore); s_timer_semaphore = NULL; } - if (s_timer_delete_mutex) { - vSemaphoreDelete(s_timer_delete_mutex); - s_timer_delete_mutex = NULL; - } return ESP_ERR_NO_MEM; } From e1db12993bcfe9528e2da7cd5d92d3d2493731bf Mon Sep 17 00:00:00 2001 From: Renz Christian Bagaporo Date: Wed, 8 May 2019 14:49:52 +0800 Subject: [PATCH 003/486] bootloader: pass legacy header config variable to subproject --- components/bootloader/project_include.cmake | 4 ++++ components/bootloader/subproject/CMakeLists.txt | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/components/bootloader/project_include.cmake b/components/bootloader/project_include.cmake index a4faae97e3..49e51b9cbb 100644 --- a/components/bootloader/project_include.cmake +++ b/components/bootloader/project_include.cmake @@ -63,6 +63,10 @@ if((NOT CONFIG_SECURE_BOOT_ENABLED) OR -DSECURE_BOOT_SIGNING_KEY=${secure_boot_signing_key} -DPYTHON_DEPS_CHECKED=1 -DEXTRA_COMPONENT_DIRS=${CMAKE_CURRENT_LIST_DIR} + # LEGACY_INCLUDE_COMMON_HEADERS has to be passed in via cache variable since + # the bootloader common component requirements depends on this and + # config variables are not available before project() call. + -DLEGACY_INCLUDE_COMMON_HEADERS=${CONFIG_LEGACY_INCLUDE_COMMON_HEADERS} INSTALL_COMMAND "" BUILD_ALWAYS 1 # no easy way around this... BUILD_BYPRODUCTS ${bootloader_binary_files} diff --git a/components/bootloader/subproject/CMakeLists.txt b/components/bootloader/subproject/CMakeLists.txt index 1f39199152..d6334ae155 100644 --- a/components/bootloader/subproject/CMakeLists.txt +++ b/components/bootloader/subproject/CMakeLists.txt @@ -19,7 +19,7 @@ set(COMPONENTS bootloader esptool_py partition_table soc bootloader_support log set(BOOTLOADER_BUILD 1) include("${IDF_PATH}/tools/cmake/project.cmake") set(common_req log esp_rom esp_common xtensa) -if (CONFIG_LEGACY_INCLUDE_COMMON_HEADERS) +if(LEGACY_INCLUDE_COMMON_HEADERS) list(APPEND common_req soc) endif() idf_build_set_property(__COMPONENT_REQUIRES_COMMON "${common_req}") From d68f1907ef7df254d84c9c6056c94c9271f49e49 Mon Sep 17 00:00:00 2001 From: chenjianqiang Date: Mon, 20 May 2019 15:26:52 +0800 Subject: [PATCH 004/486] bugfix(flash): improve flash dio read timing When flash work in DIO Mode, in order to ensure the fast read mode of flash is a fixed value, we merged the mode bits into address part, and the fast read mode value is 0 (the default value). --- components/bootloader_support/src/bootloader_init.c | 5 +++-- components/esp32/spiram_psram.c | 6 ++++-- components/esp_rom/include/esp32/rom/spi_flash.h | 3 ++- components/spi_flash/spi_flash_rom_patch.c | 1 + 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/components/bootloader_support/src/bootloader_init.c b/components/bootloader_support/src/bootloader_init.c index ca0f435df3..0353a3b6ac 100644 --- a/components/bootloader_support/src/bootloader_init.c +++ b/components/bootloader_support/src/bootloader_init.c @@ -321,10 +321,11 @@ static void IRAM_ATTR flash_gpio_configure(const esp_image_header_t* pfhdr) int drv = 2; switch (pfhdr->spi_mode) { case ESP_IMAGE_SPI_MODE_QIO: - spi_cache_dummy = SPI0_R_DIO_DUMMY_CYCLELEN; + spi_cache_dummy = SPI0_R_QIO_DUMMY_CYCLELEN; break; case ESP_IMAGE_SPI_MODE_DIO: - spi_cache_dummy = SPI0_R_DIO_DUMMY_CYCLELEN; //qio 3 + spi_cache_dummy = SPI0_R_DIO_DUMMY_CYCLELEN; + SET_PERI_REG_BITS(SPI_USER1_REG(0), SPI_USR_ADDR_BITLEN_V, SPI0_R_DIO_ADDR_BITSLEN, SPI_USR_ADDR_BITLEN_S); break; case ESP_IMAGE_SPI_MODE_QOUT: case ESP_IMAGE_SPI_MODE_DOUT: diff --git a/components/esp32/spiram_psram.c b/components/esp32/spiram_psram.c index f6cda3fa3b..fe0299c9bb 100644 --- a/components/esp32/spiram_psram.c +++ b/components/esp32/spiram_psram.c @@ -516,9 +516,11 @@ static void IRAM_ATTR psram_gpio_config(psram_io_t psram_io, psram_cache_mode_t { int spi_cache_dummy = 0; uint32_t rd_mode_reg = READ_PERI_REG(SPI_CTRL_REG(0)); - if (rd_mode_reg & (SPI_FREAD_QIO_M | SPI_FREAD_DIO_M)) { + if (rd_mode_reg & SPI_FREAD_QIO_M) { spi_cache_dummy = SPI0_R_QIO_DUMMY_CYCLELEN; - } else if (rd_mode_reg & (SPI_FREAD_QUAD_M | SPI_FREAD_DUAL_M)) { + } else if (rd_mode_reg & SPI_FREAD_DIO_M) { + spi_cache_dummy = SPI0_R_DIO_DUMMY_CYCLELEN; + } else if (rd_mode_reg & (SPI_FREAD_QUAD_M | SPI_FREAD_DUAL_M)) { spi_cache_dummy = SPI0_R_FAST_DUMMY_CYCLELEN; } else { spi_cache_dummy = SPI0_R_FAST_DUMMY_CYCLELEN; diff --git a/components/esp_rom/include/esp32/rom/spi_flash.h b/components/esp_rom/include/esp32/rom/spi_flash.h index ea25146d71..ea995e3499 100644 --- a/components/esp_rom/include/esp32/rom/spi_flash.h +++ b/components/esp_rom/include/esp32/rom/spi_flash.h @@ -90,7 +90,8 @@ extern "C" { #define SPI0_R_QIO_DUMMY_CYCLELEN 3 #define SPI0_R_QIO_ADDR_BITSLEN 31 #define SPI0_R_FAST_DUMMY_CYCLELEN 7 -#define SPI0_R_DIO_DUMMY_CYCLELEN 3 +#define SPI0_R_DIO_DUMMY_CYCLELEN 1 +#define SPI0_R_DIO_ADDR_BITSLEN 27 #define SPI0_R_FAST_ADDR_BITSLEN 23 #define SPI0_R_SIO_ADDR_BITSLEN 23 diff --git a/components/spi_flash/spi_flash_rom_patch.c b/components/spi_flash/spi_flash_rom_patch.c index aacc62a7f5..313a995143 100644 --- a/components/spi_flash/spi_flash_rom_patch.c +++ b/components/spi_flash/spi_flash_rom_patch.c @@ -324,6 +324,7 @@ static void spi_cache_mode_switch(uint32_t modebit) REG_SET_FIELD(SPI_USER2_REG(0), SPI_USR_COMMAND_VALUE, 0x6B); REG_SET_FIELD(SPI_USER1_REG(0), SPI_USR_DUMMY_CYCLELEN, SPI0_R_FAST_DUMMY_CYCLELEN + g_rom_spiflash_dummy_len_plus[0]); } else if ((modebit & SPI_FREAD_DIO)) { + REG_SET_FIELD(SPI_USER1_REG(0), SPI_USR_ADDR_BITLEN, SPI0_R_DIO_ADDR_BITSLEN); REG_SET_FIELD(SPI_USER1_REG(0), SPI_USR_DUMMY_CYCLELEN, SPI0_R_DIO_DUMMY_CYCLELEN + g_rom_spiflash_dummy_len_plus[0]); REG_SET_FIELD(SPI_USER2_REG(0), SPI_USR_COMMAND_VALUE, 0xBB); } else if ((modebit & SPI_FREAD_DUAL)) { From 05be37c87c17f6372bf5f5743aaea9e6dcc3e2f7 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 21 Feb 2019 15:02:29 +1100 Subject: [PATCH 005/486] idf_size: Support JSON output Pass -DOUTPUT_JSON=1 to get JSON formatted output from CMake targets --- docs/en/api-guides/build-system-cmake.rst | 2 +- tools/cmake/project.cmake | 13 +- tools/idf_size.py | 150 +- tools/test_idf_size/expected_output | 2632 +++++++++++++++++++++ tools/test_idf_size/test.sh | 14 +- tools/test_idf_size/test_idf_size.py | 17 +- 6 files changed, 2767 insertions(+), 61 deletions(-) diff --git a/docs/en/api-guides/build-system-cmake.rst b/docs/en/api-guides/build-system-cmake.rst index a3c068bf90..7af329f68e 100644 --- a/docs/en/api-guides/build-system-cmake.rst +++ b/docs/en/api-guides/build-system-cmake.rst @@ -97,7 +97,7 @@ Advanced Commands - ``idf.py app``, ``idf.py bootloader``, ``idf.py partition_table`` can be used to build only the app, bootloader, or partition table from the project as applicable. - There are matching commands ``idf.py app-flash``, etc. to flash only that single part of the project to the ESP32. - ``idf.py -p PORT erase_flash`` will use esptool.py to erase the ESP32's entire flash chip. -- ``idf.py size`` prints some size information about the app. ``size-components`` and ``size-files`` are similar commands which print more detailed per-component or per-source-file information, respectively. +- ``idf.py size`` prints some size information about the app. ``size-components`` and ``size-files`` are similar commands which print more detailed per-component or per-source-file information, respectively. If you define variable ``-DOUTPUT_JSON=1`` when running CMake (or ``idf.py``), the output will be formatted as JSON not as human readable text. - ``idf.py reconfigure`` re-runs CMake_ even if it doesn't seem to need re-running. This isn't necessary during normal usage, but can be useful after adding/removing files from the source tree, or when modifying CMake cache variables. For example, ``idf.py -DNAME='VALUE' reconfigure`` can be used to set variable ``NAME`` in CMake cache to value ``VALUE``. The order of multiple ``idf.py`` commands on the same invocation is not important, they will automatically be executed in the correct order for everything to take effect (ie building before flashing, erasing before flashing, etc.). diff --git a/tools/cmake/project.cmake b/tools/cmake/project.cmake index 3726fa0fb1..0f3d45f0d9 100644 --- a/tools/cmake/project.cmake +++ b/tools/cmake/project.cmake @@ -384,20 +384,27 @@ macro(project project_name) idf_build_get_property(idf_path IDF_PATH) idf_build_get_property(python PYTHON) + set(idf_size ${python} ${idf_path}/tools/idf_size.py) + if(DEFINED OUTPUT_JSON AND OUTPUT_JSON) + list(APPEND idf_size "--json") + endif() + # Add size targets, depend on map file, run idf_size.py add_custom_target(size DEPENDS ${project_elf} - COMMAND ${python} ${idf_path}/tools/idf_size.py ${mapfile} + COMMAND ${idf_size} ${mapfile} ) add_custom_target(size-files DEPENDS ${project_elf} - COMMAND ${python} ${idf_path}/tools/idf_size.py --files ${mapfile} + COMMAND ${idf_size} --files ${mapfile} ) add_custom_target(size-components DEPENDS ${project_elf} - COMMAND ${python} ${idf_path}/tools/idf_size.py --archives ${mapfile} + COMMAND ${idf_size} --archives ${mapfile} ) + unset(idf_size) + idf_build_executable(${project_elf}) __project_info("${test_components}") diff --git a/tools/idf_size.py b/tools/idf_size.py index 2fdb6a8096..e89312dd44 100755 --- a/tools/idf_size.py +++ b/tools/idf_size.py @@ -22,9 +22,13 @@ # from __future__ import print_function from __future__ import unicode_literals +from __future__ import division import argparse -import re +import collections +import json import os.path +import re +import sys DEFAULT_TOOLCHAIN_PREFIX = "xtensa-esp32-elf-" @@ -38,6 +42,12 @@ CHIP_SIZES = { } +def _json_dump(obj): + """ Pretty-print JSON object to stdout """ + json.dump(obj, sys.stdout, indent=4) + print('\n') + + def scan_to_header(f, header_line): """ Scan forward in a file until you reach 'header_line', then return """ for line in f: @@ -160,6 +170,11 @@ def main(): help="Triplet prefix to add before objdump executable", default=DEFAULT_TOOLCHAIN_PREFIX) + parser.add_argument( + '--json', + help="Output results as JSON", + action="store_true") + parser.add_argument( 'map_file', help='MAP file produced by linker', type=argparse.FileType('r')) @@ -176,20 +191,18 @@ def main(): args = parser.parse_args() memory_config, sections = load_map_data(args.map_file) - print_summary(memory_config, sections) + if not args.json or not (args.archives or args.files or args.archive_details): + print_summary(memory_config, sections, args.json) if args.archives: - print("Per-archive contributions to ELF file:") - print_detailed_sizes(sections, "archive", "Archive File") + print_detailed_sizes(sections, "archive", "Archive File", args.json) if args.files: - print("Per-file contributions to ELF file:") - print_detailed_sizes(sections, "file", "Object File") + print_detailed_sizes(sections, "file", "Object File", args.json) if args.archive_details: - print("Symbols within the archive:", args.archive_details, "(Not all symbols may be reported)") - print_archive_symbols(sections, args.archive_details) + print_archive_symbols(sections, args.archive_details, args.json) -def print_summary(memory_config, sections): +def print_summary(memory_config, sections, as_json=False): def get_size(section): try: return sections[section]["size"] @@ -202,40 +215,53 @@ def print_summary(memory_config, sections): used_data = get_size(".dram0.data") used_bss = get_size(".dram0.bss") used_dram = used_data + used_bss + try: + used_dram_ratio = used_dram / total_dram + except ZeroDivisionError: + used_dram_ratio = float('nan') used_iram = sum(get_size(s) for s in sections if s.startswith(".iram0")) + try: + used_iram_ratio = used_iram / total_iram + except ZeroDivisionError: + used_iram_ratio = float('nan') flash_code = get_size(".flash.text") flash_rodata = get_size(".flash.rodata") total_size = used_data + used_iram + flash_code + flash_rodata - print("Total sizes:") - print(" DRAM .data size: %7d bytes" % used_data) - print(" DRAM .bss size: %7d bytes" % used_bss) - print("Used static DRAM: %7d bytes (%7d available, %.1f%% used)" % - (used_dram, total_dram - used_dram, - 100.0 * used_dram / total_dram)) - print("Used static IRAM: %7d bytes (%7d available, %.1f%% used)" % - (used_iram, total_iram - used_iram, - 100.0 * used_iram / total_iram)) - print(" Flash code: %7d bytes" % flash_code) - print(" Flash rodata: %7d bytes" % flash_rodata) - print("Total image size:~%7d bytes (.bin may be padded larger)" % (total_size)) + if as_json: + _json_dump(collections.OrderedDict([ + ("dram_data", used_data), + ("dram_bss", used_bss), + ("used_dram", used_dram), + ("available_dram", total_dram - used_dram), + ("used_dram_ratio", used_dram_ratio), + ("used_iram", used_iram), + ("available_iram", total_iram - used_iram), + ("used_iram_ratio", used_iram_ratio), + ("flash_code", flash_code), + ("flash_rodata", flash_rodata), + ("total_size", total_size) + ])) + else: + print("Total sizes:") + print(" DRAM .data size: %7d bytes" % used_data) + print(" DRAM .bss size: %7d bytes" % used_bss) + print("Used static DRAM: %7d bytes (%7d available, %.1f%% used)" % + (used_dram, total_dram - used_dram, 100.0 * used_dram_ratio)) + print("Used static IRAM: %7d bytes (%7d available, %.1f%% used)" % + (used_iram, total_iram - used_iram, 100.0 * used_iram_ratio)) + print(" Flash code: %7d bytes" % flash_code) + print(" Flash rodata: %7d bytes" % flash_rodata) + print("Total image size:~%7d bytes (.bin may be padded larger)" % (total_size)) -def print_detailed_sizes(sections, key, header): +def print_detailed_sizes(sections, key, header, as_json=False): sizes = sizes_by_key(sections, key) - headings = (header, - "DRAM .data", - "& .bss", - "IRAM", - "Flash code", - "& rodata", - "Total") - print("%24s %10s %6s %6s %10s %8s %7s" % headings) result = {} for k in sizes: v = sizes[k] - result[k] = {} + result[k] = collections.OrderedDict() result[k]["data"] = v.get(".dram0.data", 0) result[k]["bss"] = v.get(".dram0.bss", 0) result[k]["iram"] = sum(t for (s,t) in v.items() if s.startswith(".iram0")) @@ -250,20 +276,37 @@ def print_detailed_sizes(sections, key, header): def return_header(elem): return elem[0] s = sorted(list(result.items()), key=return_header) + # do a secondary sort in order to have consistent order (for diff-ing the output) - for k,v in sorted(s, key=return_total_size, reverse=True): - if ":" in k: # print subheadings for key of format archive:file - sh,k = k.split(":") - print("%24s %10d %6d %6d %10d %8d %7d" % (k[:24], - v["data"], - v["bss"], - v["iram"], - v["flash_text"], - v["flash_rodata"], - v["total"])) + s = sorted(s, key=return_total_size, reverse=True) + + if as_json: + _json_dump(collections.OrderedDict(s)) + else: + print("Per-%s contributions to ELF file:" % key) + headings = (header, + "DRAM .data", + "& .bss", + "IRAM", + "Flash code", + "& rodata", + "Total") + header_format = "%24s %10d %6d %6d %10d %8d %7d" + print(header_format.replace("d", "s") % headings) + + for k,v in s: + if ":" in k: # print subheadings for key of format archive:file + sh,k = k.split(":") + print(header_format % (k[:24], + v["data"], + v["bss"], + v["iram"], + v["flash_text"], + v["flash_rodata"], + v["total"])) -def print_archive_symbols(sections, archive): +def print_archive_symbols(sections, archive, as_json=False): interested_sections = [".dram0.data", ".dram0.bss", ".iram0.text", ".iram0.vectors", ".flash.text", ".flash.rodata"] result = {} for t in interested_sections: @@ -277,15 +320,26 @@ def print_archive_symbols(sections, archive): continue s["sym_name"] = re.sub("(.text.|.literal.|.data.|.bss.|.rodata.)", "", s["sym_name"]) result[section_name][s["sym_name"]] = result[section_name].get(s["sym_name"], 0) + s["size"] + + # build a new ordered dict of each section, where each entry is an ordereddict of symbols to sizes + section_symbols = collections.OrderedDict() for t in interested_sections: - print("\nSymbols from section:", t) - section_total = 0 s = sorted(list(result[t].items()), key=lambda k_v: k_v[0]) # do a secondary sort in order to have consistent order (for diff-ing the output) - for key,val in sorted(s, key=lambda k_v: k_v[1], reverse=True): - print(("%s(%d)" % (key.replace(t + ".", ""), val)), end=' ') - section_total += val - print("\nSection total:",section_total) + s = sorted(s, key=lambda k_v: k_v[1], reverse=True) + section_symbols[t] = collections.OrderedDict(s) + + if as_json: + _json_dump(section_symbols) + else: + print("Symbols within the archive: %s (Not all symbols may be reported)" % (archive)) + for t,s in section_symbols.items(): + section_total = 0 + print("\nSymbols from section:", t) + for key, val in s.items(): + print(("%s(%d)" % (key.replace(t + ".", ""), val)), end=' ') + section_total += val + print("\nSection total:",section_total) if __name__ == "__main__": diff --git a/tools/test_idf_size/expected_output b/tools/test_idf_size/expected_output index 34bb8d8f04..2845ec61c7 100644 --- a/tools/test_idf_size/expected_output +++ b/tools/test_idf_size/expected_output @@ -1,3 +1,6 @@ + +*** +Running idf_size.py... Total sizes: DRAM .data size: 9324 bytes DRAM .bss size: 8296 bytes @@ -6,6 +9,9 @@ Used static IRAM: 38932 bytes ( 92140 available, 29.7% used) Flash code: 146944 bytes Flash rodata: 39580 bytes Total image size:~ 234780 bytes (.bin may be padded larger) + +*** +Running idf_size.py --archives... Total sizes: DRAM .data size: 9324 bytes DRAM .bss size: 8296 bytes @@ -54,6 +60,9 @@ libxtensa-debug-module.a 0 0 8 0 0 8 libwpa2.a 0 0 0 0 0 0 libwpa_supplicant.a 0 0 0 0 0 0 libwps.a 0 0 0 0 0 0 + +*** +Running idf_size.py --files... Total sizes: DRAM .data size: 9324 bytes DRAM .bss size: 8296 bytes @@ -345,6 +354,9 @@ ieee80211_action_vendor. 0 0 0 0 0 0 wpa2_internal.o 0 0 0 0 0 0 os_xtensa.o 0 0 0 0 0 0 wps_internal.o 0 0 0 0 0 0 + +*** +Running idf_size.py --archive_details... Total sizes: DRAM .data size: 9324 bytes DRAM .bss size: 8296 bytes @@ -378,6 +390,2626 @@ Section total: 961 Symbols from section: .flash.rodata str1.4(249) get_clk_en_mask(128) get_rst_en_mask(128) __FUNCTION__$5441(24) TG(8) Section total: 537 + +***]nProducing JSON output... +{ + "dram_data": 9324, + "dram_bss": 8296, + "used_dram": 17620, + "available_dram": 163116, + "used_dram_ratio": 0.09749026203966006, + "used_iram": 38932, + "available_iram": 92140, + "used_iram_ratio": 0.297027587890625, + "flash_code": 146944, + "flash_rodata": 39580, + "total_size": 234780 +} + +{ + "liblwip.a": { + "data": 14, + "bss": 3751, + "iram": 0, + "flash_text": 66978, + "flash_rodata": 13936, + "total": 84679 + }, + "libc.a": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 55583, + "flash_rodata": 3889, + "total": 59472 + }, + "libesp32.a": { + "data": 2635, + "bss": 2375, + "iram": 7758, + "flash_text": 4814, + "flash_rodata": 8133, + "total": 25715 + }, + "libfreertos.a": { + "data": 4156, + "bss": 832, + "iram": 12853, + "flash_text": 0, + "flash_rodata": 1545, + "total": 19386 + }, + "libspi_flash.a": { + "data": 36, + "bss": 359, + "iram": 7004, + "flash_text": 886, + "flash_rodata": 1624, + "total": 9909 + }, + "libsoc.a": { + "data": 660, + "bss": 8, + "iram": 3887, + "flash_text": 0, + "flash_rodata": 3456, + "total": 8011 + }, + "libheap.a": { + "data": 1331, + "bss": 4, + "iram": 4376, + "flash_text": 1218, + "flash_rodata": 980, + "total": 7909 + }, + "libgcc.a": { + "data": 4, + "bss": 20, + "iram": 104, + "flash_text": 5488, + "flash_rodata": 888, + "total": 6504 + }, + "libvfs.a": { + "data": 232, + "bss": 103, + "iram": 0, + "flash_text": 3770, + "flash_rodata": 403, + "total": 4508 + }, + "libunity.a": { + "data": 0, + "bss": 121, + "iram": 0, + "flash_text": 2316, + "flash_rodata": 830, + "total": 3267 + }, + "libstdc++.a": { + "data": 8, + "bss": 16, + "iram": 0, + "flash_text": 1827, + "flash_rodata": 1062, + "total": 2913 + }, + "libnewlib.a": { + "data": 152, + "bss": 272, + "iram": 853, + "flash_text": 803, + "flash_rodata": 86, + "total": 2166 + }, + "libpthread.a": { + "data": 16, + "bss": 12, + "iram": 174, + "flash_text": 774, + "flash_rodata": 638, + "total": 1614 + }, + "libdriver.a": { + "data": 40, + "bss": 20, + "iram": 0, + "flash_text": 961, + "flash_rodata": 537, + "total": 1558 + }, + "liblog.a": { + "data": 8, + "bss": 268, + "iram": 456, + "flash_text": 396, + "flash_rodata": 166, + "total": 1294 + }, + "libapp_update.a": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 123, + "flash_rodata": 717, + "total": 840 + }, + "libtcpip_adapter.a": { + "data": 0, + "bss": 81, + "iram": 0, + "flash_text": 180, + "flash_rodata": 359, + "total": 620 + }, + "libhal.a": { + "data": 0, + "bss": 0, + "iram": 515, + "flash_text": 0, + "flash_rodata": 32, + "total": 547 + }, + "libm.a": { + "data": 0, + "bss": 0, + "iram": 92, + "flash_text": 0, + "flash_rodata": 0, + "total": 92 + }, + "libmain.a": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 53, + "flash_rodata": 10, + "total": 63 + }, + "libcxx.a": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 11, + "flash_rodata": 0, + "total": 11 + }, + "libxtensa-debug-module.a": { + "data": 0, + "bss": 0, + "iram": 8, + "flash_text": 0, + "flash_rodata": 0, + "total": 8 + }, + "libbootloader_support.a": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libcoexist.a": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libcore.a": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libethernet.a": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libmbedtls.a": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libmesh.a": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libnet80211.a": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libnvs_flash.a": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libphy.a": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libpp.a": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "librtc.a": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libsmartconfig_ack.a": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libwpa.a": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libwpa2.a": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libwpa_supplicant.a": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libwps.a": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + } +} + +{ + "libc.a:lib_a-vfprintf.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 14193, + "flash_rodata": 756, + "total": 14949 + }, + "libc.a:lib_a-svfprintf.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 13834, + "flash_rodata": 756, + "total": 14590 + }, + "libc.a:lib_a-svfiprintf.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 9642, + "flash_rodata": 1210, + "total": 10852 + }, + "libc.a:lib_a-vfiprintf.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 9933, + "flash_rodata": 738, + "total": 10671 + }, + "liblwip.a:nd6.o": { + "data": 8, + "bss": 1027, + "iram": 0, + "flash_text": 8427, + "flash_rodata": 136, + "total": 9598 + }, + "liblwip.a:tcp_in.o": { + "data": 0, + "bss": 54, + "iram": 0, + "flash_text": 8127, + "flash_rodata": 916, + "total": 9097 + }, + "libfreertos.a:tasks.o": { + "data": 20, + "bss": 700, + "iram": 5667, + "flash_text": 0, + "flash_rodata": 503, + "total": 6890 + }, + "liblwip.a:tcp_out.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 5060, + "flash_rodata": 1124, + "total": 6184 + }, + "liblwip.a:sockets.o": { + "data": 0, + "bss": 728, + "iram": 0, + "flash_text": 4627, + "flash_rodata": 824, + "total": 6179 + }, + "liblwip.a:tcp.o": { + "data": 4, + "bss": 23, + "iram": 0, + "flash_text": 4290, + "flash_rodata": 1384, + "total": 5701 + }, + "liblwip.a:api_msg.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 3763, + "flash_rodata": 1366, + "total": 5129 + }, + "liblwip.a:dhcp.o": { + "data": 0, + "bss": 8, + "iram": 0, + "flash_text": 3456, + "flash_rodata": 1401, + "total": 4865 + }, + "libesp32.a:panic.o": { + "data": 2579, + "bss": 5, + "iram": 2145, + "flash_text": 0, + "flash_rodata": 0, + "total": 4729 + }, + "libesp32.a:esp_err_to_name.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 50, + "flash_rodata": 4091, + "total": 4141 + }, + "libgcc.a:unwind-dw2-fde.o": { + "data": 4, + "bss": 20, + "iram": 0, + "flash_text": 3316, + "flash_rodata": 404, + "total": 3744 + }, + "liblwip.a:pbuf.o": { + "data": 0, + "bss": 1, + "iram": 0, + "flash_text": 2453, + "flash_rodata": 1161, + "total": 3615 + }, + "libfreertos.a:portasm.o": { + "data": 3084, + "bss": 0, + "iram": 480, + "flash_text": 0, + "flash_rodata": 0, + "total": 3564 + }, + "libc.a:lib_a-dtoa.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 3522, + "flash_rodata": 13, + "total": 3535 + }, + "liblwip.a:etharp.o": { + "data": 0, + "bss": 241, + "iram": 0, + "flash_text": 2618, + "flash_rodata": 658, + "total": 3517 + }, + "liblwip.a:ip6.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 3212, + "flash_rodata": 124, + "total": 3336 + }, + "liblwip.a:dns.o": { + "data": 0, + "bss": 1292, + "iram": 0, + "flash_text": 1809, + "flash_rodata": 206, + "total": 3307 + }, + "libspi_flash.a:spi_flash_rom_patch.o": { + "data": 0, + "bss": 0, + "iram": 2518, + "flash_text": 0, + "flash_rodata": 766, + "total": 3284 + }, + "liblwip.a:udp.o": { + "data": 2, + "bss": 4, + "iram": 0, + "flash_text": 3020, + "flash_rodata": 216, + "total": 3242 + }, + "libesp32.a:intr_alloc.o": { + "data": 8, + "bss": 22, + "iram": 726, + "flash_text": 1749, + "flash_rodata": 710, + "total": 3215 + }, + "libheap.a:multi_heap.o": { + "data": 857, + "bss": 0, + "iram": 2217, + "flash_text": 0, + "flash_rodata": 0, + "total": 3074 + }, + "libfreertos.a:queue.o": { + "data": 8, + "bss": 56, + "iram": 2569, + "flash_text": 0, + "flash_rodata": 369, + "total": 3002 + }, + "libspi_flash.a:flash_ops.o": { + "data": 32, + "bss": 41, + "iram": 2352, + "flash_text": 99, + "flash_rodata": 0, + "total": 2524 + }, + "libgcc.a:unwind-dw2-xtensa.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 2172, + "flash_rodata": 324, + "total": 2496 + }, + "libsoc.a:rtc_clk.o": { + "data": 660, + "bss": 8, + "iram": 1794, + "flash_text": 0, + "flash_rodata": 0, + "total": 2462 + }, + "libc.a:lib_a-mprec.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 2134, + "flash_rodata": 296, + "total": 2430 + }, + "libvfs.a:vfs.o": { + "data": 192, + "bss": 40, + "iram": 0, + "flash_text": 1995, + "flash_rodata": 132, + "total": 2359 + }, + "liblwip.a:ip6_frag.o": { + "data": 0, + "bss": 6, + "iram": 0, + "flash_text": 1905, + "flash_rodata": 442, + "total": 2353 + }, + "liblwip.a:api_lib.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 1425, + "flash_rodata": 919, + "total": 2344 + }, + "liblwip.a:igmp.o": { + "data": 0, + "bss": 12, + "iram": 0, + "flash_text": 1604, + "flash_rodata": 707, + "total": 2323 + }, + "libesp32.a:dbg_stubs.o": { + "data": 0, + "bss": 2072, + "iram": 32, + "flash_text": 100, + "flash_rodata": 0, + "total": 2204 + }, + "libvfs.a:vfs_uart.o": { + "data": 40, + "bss": 63, + "iram": 0, + "flash_text": 1775, + "flash_rodata": 271, + "total": 2149 + }, + "libunity.a:unity_platform.o": { + "data": 0, + "bss": 13, + "iram": 0, + "flash_text": 1511, + "flash_rodata": 600, + "total": 2124 + }, + "libesp32.a:esp_timer_esp32.o": { + "data": 8, + "bss": 26, + "iram": 1295, + "flash_text": 254, + "flash_rodata": 526, + "total": 2109 + }, + "libsoc.a:rtc_periph.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 2080, + "total": 2080 + }, + "libspi_flash.a:flash_mmap.o": { + "data": 0, + "bss": 296, + "iram": 1298, + "flash_text": 124, + "flash_rodata": 327, + "total": 2045 + }, + "libheap.a:heap_caps.o": { + "data": 4, + "bss": 0, + "iram": 1195, + "flash_text": 188, + "flash_rodata": 593, + "total": 1980 + }, + "libstdc++.a:eh_personality.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 1561, + "flash_rodata": 384, + "total": 1945 + }, + "liblwip.a:ip4.o": { + "data": 0, + "bss": 6, + "iram": 0, + "flash_text": 1664, + "flash_rodata": 139, + "total": 1809 + }, + "liblwip.a:netif.o": { + "data": 0, + "bss": 241, + "iram": 0, + "flash_text": 1239, + "flash_rodata": 287, + "total": 1767 + }, + "libfreertos.a:xtensa_vectors.o": { + "data": 8, + "bss": 0, + "iram": 1697, + "flash_text": 0, + "flash_rodata": 36, + "total": 1741 + }, + "libesp32.a:cpu_start.o": { + "data": 0, + "bss": 1, + "iram": 806, + "flash_text": 277, + "flash_rodata": 486, + "total": 1570 + }, + "libesp32.a:clk.o": { + "data": 0, + "bss": 0, + "iram": 67, + "flash_text": 581, + "flash_rodata": 893, + "total": 1541 + }, + "libfreertos.a:timers.o": { + "data": 8, + "bss": 56, + "iram": 1149, + "flash_text": 0, + "flash_rodata": 233, + "total": 1446 + }, + "liblwip.a:sys_arch.o": { + "data": 0, + "bss": 8, + "iram": 0, + "flash_text": 1216, + "flash_rodata": 222, + "total": 1446 + }, + "libheap.a:multi_heap_poisoning.o": { + "data": 470, + "bss": 0, + "iram": 964, + "flash_text": 0, + "flash_rodata": 0, + "total": 1434 + }, + "libheap.a:heap_caps_init.o": { + "data": 0, + "bss": 4, + "iram": 0, + "flash_text": 1030, + "flash_rodata": 387, + "total": 1421 + }, + "liblwip.a:mld6.o": { + "data": 0, + "bss": 4, + "iram": 0, + "flash_text": 1334, + "flash_rodata": 0, + "total": 1338 + }, + "libspi_flash.a:cache_utils.o": { + "data": 4, + "bss": 14, + "iram": 836, + "flash_text": 81, + "flash_rodata": 390, + "total": 1325 + }, + "liblwip.a:raw.o": { + "data": 0, + "bss": 4, + "iram": 0, + "flash_text": 1087, + "flash_rodata": 223, + "total": 1314 + }, + "libesp32.a:esp_timer.o": { + "data": 8, + "bss": 20, + "iram": 702, + "flash_text": 429, + "flash_rodata": 142, + "total": 1301 + }, + "liblog.a:log.o": { + "data": 8, + "bss": 268, + "iram": 456, + "flash_text": 396, + "flash_rodata": 166, + "total": 1294 + }, + "libesp32.a:system_api.o": { + "data": 0, + "bss": 8, + "iram": 589, + "flash_text": 0, + "flash_rodata": 662, + "total": 1259 + }, + "libsoc.a:soc_memory_layout.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 1239, + "total": 1239 + }, + "liblwip.a:icmp.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 769, + "flash_rodata": 371, + "total": 1140 + }, + "libfreertos.a:xtensa_intr_asm.o": { + "data": 1024, + "bss": 0, + "iram": 51, + "flash_text": 0, + "flash_rodata": 0, + "total": 1075 + }, + "libfreertos.a:port.o": { + "data": 0, + "bss": 16, + "iram": 617, + "flash_text": 0, + "flash_rodata": 369, + "total": 1002 + }, + "libpthread.a:pthread.o": { + "data": 8, + "bss": 8, + "iram": 174, + "flash_text": 298, + "flash_rodata": 512, + "total": 1000 + }, + "liblwip.a:icmp6.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 863, + "flash_rodata": 127, + "total": 990 + }, + "libsoc.a:rtc_init.o": { + "data": 0, + "bss": 0, + "iram": 980, + "flash_text": 0, + "flash_rodata": 0, + "total": 980 + }, + "libunity.a:unity.o": { + "data": 0, + "bss": 108, + "iram": 0, + "flash_text": 767, + "flash_rodata": 90, + "total": 965 + }, + "libsoc.a:rtc_time.o": { + "data": 0, + "bss": 0, + "iram": 803, + "flash_text": 0, + "flash_rodata": 137, + "total": 940 + }, + "libesp32.a:dport_access.o": { + "data": 8, + "bss": 40, + "iram": 539, + "flash_text": 189, + "flash_rodata": 129, + "total": 905 + }, + "libc.a:lib_a-fseeko.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 862, + "flash_rodata": 0, + "total": 862 + }, + "libnewlib.a:time.o": { + "data": 0, + "bss": 32, + "iram": 139, + "flash_text": 691, + "flash_rodata": 0, + "total": 862 + }, + "liblwip.a:tcpip.o": { + "data": 0, + "bss": 16, + "iram": 0, + "flash_text": 644, + "flash_rodata": 191, + "total": 851 + }, + "libapp_update.a:esp_ota_ops.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 123, + "flash_rodata": 717, + "total": 840 + }, + "libdriver.a:periph_ctrl.o": { + "data": 8, + "bss": 0, + "iram": 0, + "flash_text": 520, + "flash_rodata": 256, + "total": 784 + }, + "liblwip.a:timers.o": { + "data": 0, + "bss": 12, + "iram": 0, + "flash_text": 638, + "flash_rodata": 131, + "total": 781 + }, + "libspi_flash.a:partition.o": { + "data": 0, + "bss": 8, + "iram": 0, + "flash_text": 582, + "flash_rodata": 141, + "total": 731 + }, + "libnewlib.a:locks.o": { + "data": 8, + "bss": 0, + "iram": 552, + "flash_text": 0, + "flash_rodata": 84, + "total": 644 + }, + "libesp32.a:ipc.o": { + "data": 0, + "bss": 36, + "iram": 159, + "flash_text": 329, + "flash_rodata": 104, + "total": 628 + }, + "libtcpip_adapter.a:tcpip_adapter_lwip.o": { + "data": 0, + "bss": 81, + "iram": 0, + "flash_text": 180, + "flash_rodata": 359, + "total": 620 + }, + "libpthread.a:pthread_local_storage.o": { + "data": 8, + "bss": 4, + "iram": 0, + "flash_text": 476, + "flash_rodata": 126, + "total": 614 + }, + "liblwip.a:inet_chksum.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 580, + "flash_rodata": 0, + "total": 580 + }, + "libesp32.a:crosscore_int.o": { + "data": 8, + "bss": 8, + "iram": 204, + "flash_text": 126, + "flash_rodata": 148, + "total": 494 + }, + "liblwip.a:netbuf.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 154, + "flash_rodata": 326, + "total": 480 + }, + "liblwip.a:vfs_lwip.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 307, + "flash_rodata": 155, + "total": 462 + }, + "libnewlib.a:syscall_table.o": { + "data": 144, + "bss": 240, + "iram": 0, + "flash_text": 67, + "flash_rodata": 0, + "total": 451 + }, + "libdriver.a:timer.o": { + "data": 16, + "bss": 0, + "iram": 0, + "flash_text": 112, + "flash_rodata": 281, + "total": 409 + }, + "libesp32.a:int_wdt.o": { + "data": 0, + "bss": 1, + "iram": 87, + "flash_text": 301, + "flash_rodata": 0, + "total": 389 + }, + "libstdc++.a:eh_globals.o": { + "data": 0, + "bss": 16, + "iram": 0, + "flash_text": 149, + "flash_rodata": 193, + "total": 358 + }, + "libesp32.a:brownout.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 145, + "flash_rodata": 191, + "total": 336 + }, + "libesp32.a:freertos_hooks.o": { + "data": 8, + "bss": 128, + "iram": 43, + "flash_text": 137, + "flash_rodata": 0, + "total": 316 + }, + "libhal.a:windowspill_asm.o": { + "data": 0, + "bss": 0, + "iram": 311, + "flash_text": 0, + "flash_rodata": 0, + "total": 311 + }, + "libsoc.a:cpu_util.o": { + "data": 0, + "bss": 0, + "iram": 310, + "flash_text": 0, + "flash_rodata": 0, + "total": 310 + }, + "libdriver.a:rtc_module.o": { + "data": 8, + "bss": 8, + "iram": 0, + "flash_text": 291, + "flash_rodata": 0, + "total": 307 + }, + "libfreertos.a:xtensa_context.o": { + "data": 0, + "bss": 0, + "iram": 299, + "flash_text": 0, + "flash_rodata": 0, + "total": 299 + }, + "libstdc++.a:eh_terminate.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 117, + "flash_rodata": 141, + "total": 258 + }, + "liblwip.a:ethernet.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 244, + "flash_rodata": 12, + "total": 256 + }, + "libc.a:lib_a-puts.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 182, + "flash_rodata": 60, + "total": 242 + }, + "libesp32.a:dport_panic_highint_hdl.o": { + "data": 8, + "bss": 0, + "iram": 234, + "flash_text": 0, + "flash_rodata": 0, + "total": 242 + }, + "libc.a:lib_a-reent.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 232, + "flash_rodata": 0, + "total": 232 + }, + "libc.a:lib_a-fopen.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 228, + "flash_rodata": 0, + "total": 228 + }, + "liblwip.a:dhcpserver.o": { + "data": 0, + "bss": 4, + "iram": 0, + "flash_text": 203, + "flash_rodata": 0, + "total": 207 + }, + "libunity.a:test_utils.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 38, + "flash_rodata": 140, + "total": 178 + }, + "libc.a:lib_a-sprintf.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 167, + "flash_rodata": 0, + "total": 167 + }, + "libesp32.a:cache_err_int.o": { + "data": 0, + "bss": 0, + "iram": 56, + "flash_text": 98, + "flash_rodata": 0, + "total": 154 + }, + "libfreertos.a:list.o": { + "data": 0, + "bss": 0, + "iram": 142, + "flash_text": 0, + "flash_rodata": 0, + "total": 142 + }, + "libfreertos.a:xtensa_intr.o": { + "data": 0, + "bss": 0, + "iram": 104, + "flash_text": 0, + "flash_rodata": 35, + "total": 139 + }, + "libnewlib.a:syscalls.o": { + "data": 0, + "bss": 0, + "iram": 94, + "flash_text": 45, + "flash_rodata": 0, + "total": 139 + }, + "libstdc++.a:si_class_type_info.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 136, + "total": 136 + }, + "libc.a:lib_a-assert.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 68, + "flash_rodata": 60, + "total": 128 + }, + "libc.a:lib_a-flags.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 127, + "flash_rodata": 0, + "total": 127 + }, + "libc.a:lib_a-printf.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 116, + "flash_rodata": 0, + "total": 116 + }, + "liblwip.a:ip4_addr.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 72, + "flash_rodata": 40, + "total": 112 + }, + "libstdc++.a:class_type_info.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 112, + "total": 112 + }, + "libc.a:lib_a-s_frexp.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 110, + "flash_rodata": 0, + "total": 110 + }, + "liblwip.a:ip.o": { + "data": 0, + "bss": 60, + "iram": 0, + "flash_text": 50, + "flash_rodata": 0, + "total": 110 + }, + "liblwip.a:memp.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 108, + "total": 108 + }, + "libgcc.a:lib2funcs.o": { + "data": 0, + "bss": 0, + "iram": 104, + "flash_text": 0, + "flash_rodata": 0, + "total": 104 + }, + "libc.a:lib_a-vprintf.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 94, + "flash_rodata": 0, + "total": 94 + }, + "libm.a:lib_a-s_fpclassify.o": { + "data": 0, + "bss": 0, + "iram": 92, + "flash_text": 0, + "flash_rodata": 0, + "total": 92 + }, + "liblwip.a:def.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 91, + "flash_rodata": 0, + "total": 91 + }, + "libc.a:lib_a-fiprintf.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 84, + "flash_rodata": 0, + "total": 84 + }, + "libesp32.a:hw_random.o": { + "data": 0, + "bss": 4, + "iram": 74, + "flash_text": 0, + "flash_rodata": 0, + "total": 78 + }, + "libesp32.a:stack_check.o": { + "data": 0, + "bss": 4, + "iram": 0, + "flash_text": 32, + "flash_rodata": 42, + "total": 78 + }, + "libhal.a:clock.o": { + "data": 0, + "bss": 0, + "iram": 72, + "flash_text": 0, + "flash_rodata": 0, + "total": 72 + }, + "libnewlib.a:reent_init.o": { + "data": 0, + "bss": 0, + "iram": 68, + "flash_text": 0, + "flash_rodata": 2, + "total": 70 + }, + "libmain.a:app_main.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 53, + "flash_rodata": 10, + "total": 63 + }, + "libhal.a:state_asm--restore_extra_nw.o": { + "data": 0, + "bss": 0, + "iram": 62, + "flash_text": 0, + "flash_rodata": 0, + "total": 62 + }, + "libhal.a:state_asm--save_extra_nw.o": { + "data": 0, + "bss": 0, + "iram": 62, + "flash_text": 0, + "flash_rodata": 0, + "total": 62 + }, + "libdriver.a:uart.o": { + "data": 8, + "bss": 12, + "iram": 0, + "flash_text": 38, + "flash_rodata": 0, + "total": 58 + }, + "libstdc++.a:new_opv.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 56, + "total": 56 + }, + "libfreertos.a:xtensa_vector_defaults.o": { + "data": 0, + "bss": 0, + "iram": 46, + "flash_text": 0, + "flash_rodata": 0, + "total": 46 + }, + "libc.a:lib_a-fseek.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 45, + "flash_rodata": 0, + "total": 45 + }, + "libgcc.a:_divdi3.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 40, + "total": 40 + }, + "libgcc.a:_moddi3.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 40, + "total": 40 + }, + "libgcc.a:_udivdi3.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 40, + "total": 40 + }, + "libgcc.a:_umoddi3.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 40, + "total": 40 + }, + "libstdc++.a:new_op.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 40, + "total": 40 + }, + "libfreertos.a:xtensa_init.o": { + "data": 0, + "bss": 4, + "iram": 32, + "flash_text": 0, + "flash_rodata": 0, + "total": 36 + }, + "libhal.a:interrupts--intlevel.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 32, + "total": 32 + }, + "liblwip.a:init.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 27, + "flash_rodata": 0, + "total": 27 + }, + "libesp32.a:wifi_init.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 17, + "flash_rodata": 9, + "total": 26 + }, + "liblwip.a:ip6_addr.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 20, + "total": 20 + }, + "libc.a:lib_a-errno.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 10, + "flash_rodata": 0, + "total": 10 + }, + "libhal.a:int_asm--set_intclear.o": { + "data": 0, + "bss": 0, + "iram": 8, + "flash_text": 0, + "flash_rodata": 0, + "total": 8 + }, + "libxtensa-debug-module.a:eri.o": { + "data": 0, + "bss": 0, + "iram": 8, + "flash_text": 0, + "flash_rodata": 0, + "total": 8 + }, + "libcxx.a:cxx_exception_stubs.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 6, + "flash_rodata": 0, + "total": 6 + }, + "libcxx.a:cxx_guards.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 5, + "flash_rodata": 0, + "total": 5 + }, + "libfreertos.a:FreeRTOS-openocd.o": { + "data": 4, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 4 + }, + "libstdc++.a:eh_term_handler.o": { + "data": 4, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 4 + }, + "libstdc++.a:eh_unex_handler.o": { + "data": 4, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 4 + }, + "libbootloader_support.a:bootloader_flash.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libbootloader_support.a:bootloader_sha.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libbootloader_support.a:esp_image_format.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libc.a:lib_a-fputs.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libc.a:lib_a-snprintf.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libc.a:lib_a-strerror.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libc.a:lib_a-sysgettod.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libc.a:lib_a-u_strerr.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libc.a:lib_a-vsnprintf.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libc.a:lib_a-xpg_strerror_r.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libcoexist.a:coexist_api.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libcoexist.a:coexist_arbit.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libcoexist.a:coexist_core.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libcoexist.a:coexist_dbg.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libcoexist.a:coexist_hw.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libcoexist.a:coexist_param.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libcoexist.a:coexist_timer.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libcore.a:misc_nvs.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libdriver.a:gpio.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libesp32.a:ets_timer_legacy.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libesp32.a:event_default_handlers.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libesp32.a:event_loop.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libesp32.a:lib_printf.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libesp32.a:phy_init.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libesp32.a:sha.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libesp32.a:wifi_os_adapter.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libethernet.a:emac_dev.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libethernet.a:emac_main.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libfreertos.a:event_groups.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libfreertos.a:ringbuf.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libgcc.a:_addsubdf3.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libgcc.a:_cmpdf2.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libgcc.a:_divdf3.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libgcc.a:_divsf3.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libgcc.a:_extendsfdf2.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libgcc.a:_fixdfsi.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libgcc.a:_floatdidf.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libgcc.a:_floatdisf.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libgcc.a:_floatsidf.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libgcc.a:_muldf3.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libgcc.a:_popcountsi2.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "liblwip.a:ethernetif.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "liblwip.a:ethip6.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "liblwip.a:wlanif.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libmbedtls.a:esp_sha256.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libmesh.a:mesh.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libmesh.a:mesh_common.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libmesh.a:mesh_config.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libmesh.a:mesh_main.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libmesh.a:mesh_parent.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libmesh.a:mesh_route.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libmesh.a:mesh_schedule.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libmesh.a:mesh_timer.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libmesh.a:mesh_utilities.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libmesh.a:mesh_wifi.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libnet80211.a:ieee80211.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libnet80211.a:ieee80211_action.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libnet80211.a:ieee80211_action_vendor.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libnet80211.a:ieee80211_api.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libnet80211.a:ieee80211_crypto.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libnet80211.a:ieee80211_crypto_ccmp.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libnet80211.a:ieee80211_crypto_tkip.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libnet80211.a:ieee80211_crypto_wep.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libnet80211.a:ieee80211_debug.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libnet80211.a:ieee80211_ets.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libnet80211.a:ieee80211_hostap.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libnet80211.a:ieee80211_ht.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libnet80211.a:ieee80211_ie_vendor.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libnet80211.a:ieee80211_input.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libnet80211.a:ieee80211_ioctl.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libnet80211.a:ieee80211_mesh_quick.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libnet80211.a:ieee80211_misc.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libnet80211.a:ieee80211_nvs.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libnet80211.a:ieee80211_output.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libnet80211.a:ieee80211_phy.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libnet80211.a:ieee80211_power.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libnet80211.a:ieee80211_proto.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libnet80211.a:ieee80211_regdomain.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libnet80211.a:ieee80211_rfid.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libnet80211.a:ieee80211_scan.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libnet80211.a:ieee80211_sta.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libnet80211.a:ieee80211_timer.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libnet80211.a:wl_chm.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libnet80211.a:wl_cnx.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libnvs_flash.a:nvs_api.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libnvs_flash.a:nvs_item_hash_list.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libnvs_flash.a:nvs_page.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libnvs_flash.a:nvs_pagemanager.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libnvs_flash.a:nvs_storage.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libnvs_flash.a:nvs_types.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libphy.a:phy.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libphy.a:phy_chip_v7.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libphy.a:phy_chip_v7_ana.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libphy.a:phy_chip_v7_cal.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libpp.a:esf_buf.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libpp.a:if_hwctrl.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libpp.a:lmac.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libpp.a:pm.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libpp.a:pm_for_bcn_only_mode.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libpp.a:pp.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libpp.a:pp_debug.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libpp.a:pp_timer.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libpp.a:rate_control.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libpp.a:trc.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libpp.a:wdev.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "librtc.a:bt_bb.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "librtc.a:pm.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "librtc.a:rtc.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "librtc.a:rtc_analog.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libsmartconfig_ack.a:smartconfig_ack.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libsoc.a:gpio_periph.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libsoc.a:rtc_sleep.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libstdc++.a:bad_alloc.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libstdc++.a:del_op.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libstdc++.a:del_opv.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libstdc++.a:eh_exception.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libstdc++.a:new_handler.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libstdc++.a:pure.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libstdc++.a:tinfo.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libwpa.a:ap_config.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libwpa.a:common.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libwpa.a:wpa.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libwpa.a:wpa_auth.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libwpa.a:wpa_auth_ie.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libwpa.a:wpa_common.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libwpa.a:wpa_debug.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libwpa.a:wpa_ie.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libwpa.a:wpa_main.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libwpa.a:wpabuf.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libwpa.a:wpas_glue.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libwpa2.a:wpa2_internal.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libwpa_supplicant.a:os_xtensa.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + }, + "libwps.a:wps_internal.o": { + "data": 0, + "bss": 0, + "iram": 0, + "flash_text": 0, + "flash_rodata": 0, + "total": 0 + } +} + +{ + ".dram0.data": { + "timer_spinlock": 16, + "periph_spinlock": 8, + "s_rtc_isr_handler_list_lock": 8, + "uart_selectlock": 8 + }, + ".dram0.bss": { + "p_uart_obj": 12, + "s_rtc_isr_handle": 4, + "s_rtc_isr_handler_list": 4 + }, + ".iram0.text": {}, + ".iram0.vectors": {}, + ".flash.text": { + "get_clk_en_mask": 211, + "get_rst_en_mask": 157, + "timer_group_intr_enable": 112, + "rtc_isr": 86, + "periph_module_enable": 78, + "rtc_isr_ensure_installed": 75, + "rtc_gpio_force_hold_dis_all": 65, + "rtc_isr_register": 65, + "is_wifi_clk_peripheral": 28, + "uart_set_select_notif_callback": 26, + "get_rst_en_reg": 25, + "get_clk_en_reg": 21, + "uart_get_selectlock": 12 + }, + ".flash.rodata": { + "str1.4": 249, + "get_clk_en_mask": 128, + "get_rst_en_mask": 128, + "__FUNCTION__$5441": 24, + "TG": 8 + } +} + + +*** +Running idf_size_tests.py... Total sizes: DRAM .data size: 0 bytes DRAM .bss size: 0 bytes +Used static DRAM: 0 bytes ( 0 available, nan% used) +Used static IRAM: 0 bytes ( 0 available, nan% used) + Flash code: 0 bytes + Flash rodata: 0 bytes +Total image size:~ 0 bytes (.bin may be padded larger) diff --git a/tools/test_idf_size/test.sh b/tools/test_idf_size/test.sh index 5b2edd1cf9..0270fa3640 100755 --- a/tools/test_idf_size/test.sh +++ b/tools/test_idf_size/test.sh @@ -2,11 +2,23 @@ { coverage debug sys \ && coverage erase &> output \ + && echo -e "\n***\nRunning idf_size.py..." >> output \ && coverage run -a $IDF_PATH/tools/idf_size.py app.map &>> output \ + && echo -e "\n***\nRunning idf_size.py --archives..." >> output \ && coverage run -a $IDF_PATH/tools/idf_size.py --archives app.map &>> output \ + && echo -e "\n***\nRunning idf_size.py --files..." >> output \ && coverage run -a $IDF_PATH/tools/idf_size.py --files app.map &>> output \ + && echo -e "\n***\nRunning idf_size.py --archive_details..." >> output \ && coverage run -a $IDF_PATH/tools/idf_size.py --archive_details libdriver.a app.map &>> output \ + && echo -e "\n***]nProducing JSON output..." >> output \ + && coverage run -a $IDF_PATH/tools/idf_size.py --json app.map &>> output \ + && coverage run -a $IDF_PATH/tools/idf_size.py --json --archives app.map &>> output \ + && coverage run -a $IDF_PATH/tools/idf_size.py --json --files app.map &>> output \ + && coverage run -a $IDF_PATH/tools/idf_size.py --json --archive_details libdriver.a app.map &>> output \ + && echo -e "\n***\nRunning idf_size_tests.py..." >> output \ && coverage run -a $IDF_PATH/tools/test_idf_size/test_idf_size.py &>> output \ - && diff output expected_output \ + && diff -Z output expected_output \ && coverage report \ ; } || { echo 'The test for idf_size has failed. Please examine the artifacts.' ; exit 1; } + +# Note: "diff -Z is used because some versions of Python print trailing whitespace for JSON pretty-printing, and some don't diff --git a/tools/test_idf_size/test_idf_size.py b/tools/test_idf_size/test_idf_size.py index adafea9294..4f7a93c032 100644 --- a/tools/test_idf_size/test_idf_size.py +++ b/tools/test_idf_size/test_idf_size.py @@ -24,18 +24,19 @@ except ImportError: if __name__ == "__main__": + # Should deliver a RuntimeError as the 'test' header doesn't exist try: idf_size.scan_to_header([], 'test') - except RuntimeError: - pass + except RuntimeError as e: + assert "Didn't find line" in str(e) + # Should deliver a RuntimeError as there's no content under the heading try: idf_size.load_memory_config(["Memory Configuration"]) pass - except RuntimeError: - pass + except RuntimeError as e: + assert "End of file" in str(e) - try: - idf_size.print_summary({"iram0_0_seg": {"length":0}, "dram0_0_seg": {"length":0}}, {}) - except ZeroDivisionError: - pass + # This used to crash with a division by zero error but now it just prints nan% due to + # zero lengths + idf_size.print_summary({"iram0_0_seg": {"length":0}, "dram0_0_seg": {"length":0}}, {}) From 13908160fb73c7942cd57fab397ded08361ee8ae Mon Sep 17 00:00:00 2001 From: baohongde Date: Thu, 30 May 2019 20:19:43 +0800 Subject: [PATCH 006/486] components/bt: Fix make errors due to macro usage error --- .../profile/std/hf_client/bta_hf_client_co.c | 37 +++++++++---------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/components/bt/bluedroid/btc/profile/std/hf_client/bta_hf_client_co.c b/components/bt/bluedroid/btc/profile/std/hf_client/bta_hf_client_co.c index 6e756fc6ce..b8b2ea40c7 100644 --- a/components/bt/bluedroid/btc/profile/std/hf_client/bta_hf_client_co.c +++ b/components/bt/bluedroid/btc/profile/std/hf_client/bta_hf_client_co.c @@ -212,30 +212,29 @@ void bta_hf_client_sco_co_open(UINT16 handle, UINT8 air_mode, UINT8 inout_pkt_si #endif /// (HFP_DYNAMIC_MEMORY == TRUE) - bta_hf_dec_init(); - bta_hf_enc_init(); + bta_hf_dec_init(); + bta_hf_enc_init(); - return; - -error_exit:; -#if (HFP_DYNAMIC_MEMORY == TRUE) - if (bta_hf_client_co_cb_ptr) { - osi_free(bta_hf_client_co_cb_ptr); - bta_hf_client_co_cb_ptr = NULL; - } - -#if (PLC_INCLUDED == TRUE) - if (bta_hf_ct_plc_ptr) { - osi_free(bta_hf_ct_plc_ptr); - bta_hf_ct_plc_ptr = NULL; - } -#endif ///(PLC_INCLUDED == TRUE) - -#endif /// (HFP_DYNAMIC_MEMORY == TRUE) + return; } else { + return; // Nothing to do } +#if (HFP_DYNAMIC_MEMORY == TRUE) +error_exit:; + if (bta_hf_client_co_cb_ptr) { + osi_free(bta_hf_client_co_cb_ptr); + bta_hf_client_co_cb_ptr = NULL; + } + +#if (PLC_INCLUDED == TRUE) + if (bta_hf_ct_plc_ptr) { + osi_free(bta_hf_ct_plc_ptr); + bta_hf_ct_plc_ptr = NULL; + } +#endif ///(PLC_INCLUDED == TRUE) +#endif /// (HFP_DYNAMIC_MEMORY == TRUE) return; } From 89e2b48a1847f999acc930a539fa6164f8dbe5c8 Mon Sep 17 00:00:00 2001 From: baohongde Date: Mon, 3 Jun 2019 19:29:54 +0800 Subject: [PATCH 007/486] components/bt: Add API to config EIR data --- components/bt/CMakeLists.txt | 1 + components/bt/bluedroid/api/esp_gap_bt_api.c | 32 ++ .../bluedroid/api/include/api/esp_bt_defs.h | 3 +- .../api/include/api/esp_gap_bt_api.h | 62 ++- components/bt/bluedroid/bta/dm/bta_dm_act.c | 413 +++++++++++++----- components/bt/bluedroid/bta/dm/bta_dm_api.c | 42 ++ components/bt/bluedroid/bta/dm/bta_dm_cfg.c | 20 +- components/bt/bluedroid/bta/dm/bta_dm_main.c | 1 + .../bt/bluedroid/bta/dm/include/bta_dm_int.h | 21 +- .../bt/bluedroid/bta/include/bta/bta_api.h | 39 +- .../bluedroid/bta/include/bta/bta_gap_bt_co.h | 29 ++ components/bt/bluedroid/btc/core/btc_util.c | 34 ++ .../bt/bluedroid/btc/include/btc/btc_util.h | 1 + .../btc/profile/std/gap/bta_gap_bt_co.c | 40 ++ .../btc/profile/std/gap/btc_gap_bt.c | 73 +++- .../btc/profile/std/include/btc_gap_bt.h | 7 + .../bluedroid/common/include/common/bt_defs.h | 1 + .../common/include/common/bt_target.h | 12 +- components/bt/bluedroid/stack/btm/btm_inq.c | 5 +- .../bluedroid/stack/include/stack/bt_types.h | 1 + .../bluedroid/stack/include/stack/btm_api.h | 6 +- .../bluedroid/stack/include/stack/hcidefs.h | 1 + 22 files changed, 688 insertions(+), 156 deletions(-) create mode 100644 components/bt/bluedroid/bta/include/bta/bta_gap_bt_co.h create mode 100644 components/bt/bluedroid/btc/profile/std/gap/bta_gap_bt_co.c diff --git a/components/bt/CMakeLists.txt b/components/bt/CMakeLists.txt index 50ff9abf60..081e2a606e 100644 --- a/components/bt/CMakeLists.txt +++ b/components/bt/CMakeLists.txt @@ -126,6 +126,7 @@ if(CONFIG_BT_ENABLED) "bluedroid/btc/profile/std/avrc/bta_avrc_co.c" "bluedroid/btc/profile/std/gap/btc_gap_ble.c" "bluedroid/btc/profile/std/gap/btc_gap_bt.c" + "bluedroid/btc/profile/std/gap/bta_gap_bt_co.c" "bluedroid/btc/profile/std/gatt/btc_gatt_common.c" "bluedroid/btc/profile/std/gatt/btc_gatt_util.c" "bluedroid/btc/profile/std/gatt/btc_gattc.c" diff --git a/components/bt/bluedroid/api/esp_gap_bt_api.c b/components/bt/bluedroid/api/esp_gap_bt_api.c index ff3f945c48..f096b0fd01 100644 --- a/components/bt/bluedroid/api/esp_gap_bt_api.c +++ b/components/bt/bluedroid/api/esp_gap_bt_api.c @@ -144,6 +144,38 @@ uint8_t *esp_bt_gap_resolve_eir_data(uint8_t *eir, esp_bt_eir_type_t type, uint8 return BTM_CheckEirData(eir, type, length); } +esp_err_t esp_bt_gap_config_eir_data(esp_bt_eir_data_t *eir_data) +{ + btc_msg_t msg; + btc_gap_bt_args_t arg; + + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + if (eir_data == NULL) { + return ESP_ERR_INVALID_ARG; + } + + if (eir_data->manufacturer_len > ESP_BT_EIR_MAX_LEN + || eir_data->url_len > ESP_BT_EIR_MAX_LEN) { + return ESP_ERR_INVALID_ARG; + } + + if ((eir_data->manufacturer_len > 0 && eir_data->p_manufacturer_data == NULL) + || (eir_data->url_len > 0 && eir_data->p_url == NULL)) { + return ESP_ERR_INVALID_ARG; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GAP_BT; + msg.act = BTC_GAP_BT_ACT_CONFIG_EIR; + + memcpy(&arg.config_eir, eir_data, sizeof(esp_bt_eir_data_t)); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_gap_bt_args_t), btc_gap_bt_arg_deep_copy) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + esp_err_t esp_bt_gap_set_cod(esp_bt_cod_t cod, esp_bt_cod_mode_t mode) { btc_msg_t msg; diff --git a/components/bt/bluedroid/api/include/api/esp_bt_defs.h b/components/bt/bluedroid/api/include/api/esp_bt_defs.h index 7e1063c71c..51044d45c7 100644 --- a/components/bt/bluedroid/api/include/api/esp_bt_defs.h +++ b/components/bt/bluedroid/api/include/api/esp_bt_defs.h @@ -51,7 +51,8 @@ typedef enum { ESP_BT_STATUS_PEER_LE_DATA_LEN_UNSUPPORTED, /* relate to BTM_PEER_LE_DATA_LEN_UNSUPPORTED in stack/btm_api.h */ ESP_BT_STATUS_CONTROL_LE_DATA_LEN_UNSUPPORTED,/* relate to BTM_CONTROL_LE_DATA_LEN_UNSUPPORTED in stack/btm_api.h */ ESP_BT_STATUS_ERR_ILLEGAL_PARAMETER_FMT, /* relate to HCI_ERR_ILLEGAL_PARAMETER_FMT in stack/hcidefs.h */ - ESP_BT_STATUS_MEMORY_FULL, /* relate to BT_STATUS_MEMORY_FULL in bt_def.h */ + ESP_BT_STATUS_MEMORY_FULL = 20, /* relate to BT_STATUS_MEMORY_FULL in bt_def.h */ + ESP_BT_STATUS_EIR_TOO_LARGE, /* relate to BT_STATUS_EIR_TOO_LARGE in bt_def.h */ } esp_bt_status_t; diff --git a/components/bt/bluedroid/api/include/api/esp_gap_bt_api.h b/components/bt/bluedroid/api/include/api/esp_gap_bt_api.h index 084cabec23..076f59caf7 100644 --- a/components/bt/bluedroid/api/include/api/esp_gap_bt_api.h +++ b/components/bt/bluedroid/api/include/api/esp_gap_bt_api.h @@ -90,8 +90,30 @@ typedef enum { ESP_BT_EIR_TYPE_SHORT_LOCAL_NAME = 0x08, /*!< Shortened Local Name */ ESP_BT_EIR_TYPE_CMPL_LOCAL_NAME = 0x09, /*!< Complete Local Name */ ESP_BT_EIR_TYPE_TX_POWER_LEVEL = 0x0a, /*!< Tx power level, value is 1 octet ranging from -127 to 127, unit is dBm*/ + ESP_BT_EIR_TYPE_URL = 0x24, /*!< Uniform resource identifier */ ESP_BT_EIR_TYPE_MANU_SPECIFIC = 0xff, /*!< Manufacturer specific data */ } esp_bt_eir_type_t; +#define ESP_BT_EIR_TYPE_MAX_NUM 12 /*!< MAX number of EIR type */ + +/* ESP_BT_EIR_FLAG bit definition */ +#define ESP_BT_EIR_FLAG_LIMIT_DISC (0x01 << 0) +#define ESP_BT_EIR_FLAG_GEN_DISC (0x01 << 1) +#define ESP_BT_EIR_FLAG_BREDR_NOT_SPT (0x01 << 2) +#define ESP_BT_EIR_FLAG_DMT_CONTROLLER_SPT (0x01 << 3) +#define ESP_BT_EIR_FLAG_DMT_HOST_SPT (0x01 << 4) + +#define ESP_BT_EIR_MAX_LEN 240 +/// EIR data content, according to "Supplement to the Bluetooth Core Specification" +typedef struct { + bool fec_required; /*!< FEC is required or not, true by default */ + bool include_txpower; /*!< EIR data include TX power, false by default */ + bool include_uuid; /*!< EIR data include UUID, false by default */ + uint8_t flag; /*!< EIR flags, see ESP_BT_EIR_FLAG for details, EIR will not include flag if it is 0, 0 by default */ + uint16_t manufacturer_len; /*!< Manufacturer data length, 0 by default */ + uint8_t *p_manufacturer_data; /*!< Manufacturer data point */ + uint16_t url_len; /*!< URL length, 0 by default */ + uint8_t *p_url; /*!< URL point */ +} esp_bt_eir_data_t; /// Major service class field of Class of Device, mutiple bits can be set typedef enum { @@ -179,6 +201,7 @@ typedef enum { ESP_BT_GAP_KEY_NOTIF_EVT, /*!< Simple Pairing Passkey Notification */ ESP_BT_GAP_KEY_REQ_EVT, /*!< Simple Pairing Passkey request */ ESP_BT_GAP_READ_RSSI_DELTA_EVT, /*!< read rssi event */ + ESP_BT_GAP_CONFIG_EIR_DATA_EVT, /*!< config EIR data event */ ESP_BT_GAP_EVT_MAX, } esp_bt_gap_cb_event_t; @@ -237,6 +260,19 @@ typedef union { int8_t rssi_delta; /*!< rssi delta value range -128 ~127, The value zero indicates that the RSSI is inside the Golden Receive Power Range, the Golden Receive Power Range is from ESP_BT_GAP_RSSI_LOW_THRLD to ESP_BT_GAP_RSSI_HIGH_THRLD */ } read_rssi_delta; /*!< read rssi parameter struct */ + /** + * @brief ESP_BT_GAP_CONFIG_EIR_DATA_EVT * + */ + struct config_eir_data_param { + esp_bt_status_t stat; /*!< config EIR status: + ESP_BT_STATUS_SUCCESS: config success + ESP_BT_STATUS_EIR_TOO_LARGE: the EIR data is more than 240B. The EIR may not contain the whole data. + others: failed + */ + uint8_t eir_type_num; /*!< the number of EIR types in EIR type */ + esp_bt_eir_type_t eir_type[ESP_BT_EIR_TYPE_MAX_NUM]; /*!< EIR types in EIR type */ + } config_eir_data; /*!< config EIR data */ + /** * @brief ESP_BT_GAP_AUTH_CMPL_EVT */ @@ -368,8 +404,8 @@ esp_err_t esp_bt_gap_set_scan_mode(esp_bt_connection_mode_t c_mode, esp_bt_disco /** * @brief Start device discovery. This function should be called after esp_bluedroid_enable() completes successfully. - * esp_bt_gap_cb_t will is called with ESP_BT_GAP_DISC_STATE_CHANGED_EVT if discovery is started or halted. - * esp_bt_gap_cb_t will is called with ESP_BT_GAP_DISC_RES_EVT if discovery result is got. + * esp_bt_gap_cb_t will be called with ESP_BT_GAP_DISC_STATE_CHANGED_EVT if discovery is started or halted. + * esp_bt_gap_cb_t will be called with ESP_BT_GAP_DISC_RES_EVT if discovery result is got. * * @param[in] mode - inquiry mode * @param[in] inq_len - inquiry duration in 1.28 sec units, ranging from 0x01 to 0x30 @@ -385,7 +421,7 @@ esp_err_t esp_bt_gap_start_discovery(esp_bt_inq_mode_t mode, uint8_t inq_len, ui /** * @brief Cancel device discovery. This function should be called after esp_bluedroid_enable() completes successfully - * esp_bt_gap_cb_t will is called with ESP_BT_GAP_DISC_STATE_CHANGED_EVT if discovery is stopped. + * esp_bt_gap_cb_t will be called with ESP_BT_GAP_DISC_STATE_CHANGED_EVT if discovery is stopped. * * @return * - ESP_OK : Succeed @@ -396,7 +432,7 @@ esp_err_t esp_bt_gap_cancel_discovery(void); /** * @brief Start SDP to get remote services. This function should be called after esp_bluedroid_enable() completes successfully. - * esp_bt_gap_cb_t will is called with ESP_BT_GAP_RMT_SRVCS_EVT after service discovery ends + * esp_bt_gap_cb_t will be called with ESP_BT_GAP_RMT_SRVCS_EVT after service discovery ends * * @return * - ESP_OK : Succeed @@ -409,7 +445,7 @@ esp_err_t esp_bt_gap_get_remote_services(esp_bd_addr_t remote_bda); * @brief Start SDP to look up the service matching uuid on the remote device. This function should be called after * esp_bluedroid_enable() completes successfully * - * esp_bt_gap_cb_t will is called with ESP_BT_GAP_RMT_SRVC_REC_EVT after service discovery ends + * esp_bt_gap_cb_t will be called with ESP_BT_GAP_RMT_SRVC_REC_EVT after service discovery ends * @return * - ESP_OK : Succeed * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled @@ -429,9 +465,23 @@ esp_err_t esp_bt_gap_get_remote_service_record(esp_bd_addr_t remote_bda, esp_bt_ */ uint8_t *esp_bt_gap_resolve_eir_data(uint8_t *eir, esp_bt_eir_type_t type, uint8_t *length); +/** + * @brief This function is called to config EIR data. + * + * esp_bt_gap_cb_t will be called with ESP_BT_GAP_CONFIG_EIR_DATA_EVT after config EIR ends. + * + * @param[in] eir_data - pointer of EIR data content + * @return + * - ESP_OK : Succeed + * - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_ERR_INVALID_ARG: if param is invalid + * - ESP_FAIL: others + */ +esp_err_t esp_bt_gap_config_eir_data(esp_bt_eir_data_t *eir_data); + /** * @brief This function is called to set class of device. - * esp_bt_gap_cb_t will is called with ESP_BT_GAP_SET_COD_EVT after set COD ends + * esp_bt_gap_cb_t will be called with ESP_BT_GAP_SET_COD_EVT after set COD ends * Some profile have special restrictions on class of device, * changes may cause these profile do not work * diff --git a/components/bt/bluedroid/bta/dm/bta_dm_act.c b/components/bt/bluedroid/bta/dm/bta_dm_act.c index c02987c9fb..49cce8749a 100644 --- a/components/bt/bluedroid/bta/dm/bta_dm_act.c +++ b/components/bt/bluedroid/bta/dm/bta_dm_act.c @@ -131,7 +131,7 @@ static void bta_dm_observe_discard_cb (uint32_t num_dis); static void bta_dm_delay_role_switch_cback(TIMER_LIST_ENT *p_tle); extern void sdpu_uuid16_to_uuid128(UINT16 uuid16, UINT8 *p_uuid128); static void bta_dm_disable_timer_cback(TIMER_LIST_ENT *p_tle); - +extern int bredr_txpwr_get(int *min_power_level, int *max_power_level); const UINT16 bta_service_id_to_uuid_lkup_tbl [BTA_MAX_SERVICE_ID] = { UUID_SERVCLASS_PNP_INFORMATION, /* Reserved */ @@ -325,6 +325,58 @@ void bta_dm_deinit_cb(void) memset(&bta_dm_cb, 0, sizeof(bta_dm_cb)); } +/******************************************************************************* + * + * Function bta_dm_eir_cfg_init + * + * Description Initializes the p_bta_dm_eir_cfg + * + * + * Returns void + * + ******************************************************************************/ +static void bta_dm_eir_cfg_init(void) +{ + p_bta_dm_eir_cfg->bta_dm_eir_fec_required = BTM_EIR_DEFAULT_FEC_REQUIRED; + p_bta_dm_eir_cfg->bta_dm_eir_min_name_len = 50; + + p_bta_dm_eir_cfg->bta_dm_eir_included_uuid = TRUE; + p_bta_dm_eir_cfg->bta_dm_eir_included_tx_power = FALSE; + p_bta_dm_eir_cfg->bta_dm_eir_inq_tx_power = 3; + p_bta_dm_eir_cfg->bta_dm_eir_flags = 0; + + p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec_len = 0; + p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec = NULL; + + p_bta_dm_eir_cfg->bta_dm_eir_url_len = 0; + p_bta_dm_eir_cfg->bta_dm_eir_url = NULL; +} + +/******************************************************************************* + * + * Function bta_dm_eir_cfg_deinit + * + * Description De-initializes the p_bta_dm_eir_cfg + * + * + * Returns void + * + ******************************************************************************/ +static void bta_dm_eir_cfg_deinit(void) +{ + p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec_len = 0; + if (p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec) { + osi_free(p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec); + p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec = NULL; + } + + p_bta_dm_eir_cfg->bta_dm_eir_url_len = 0; + if (p_bta_dm_eir_cfg->bta_dm_eir_url) { + osi_free(p_bta_dm_eir_cfg->bta_dm_eir_url); + p_bta_dm_eir_cfg->bta_dm_eir_url = NULL; + } +} + /******************************************************************************* ** ** Function bta_dm_sys_hw_cback @@ -363,6 +415,9 @@ static void bta_dm_sys_hw_cback( tBTA_SYS_HW_EVT status ) /* reinitialize the control block */ bta_dm_deinit_cb(); + /* reinitialize the Extended Inquiry Response */ + bta_dm_eir_cfg_deinit(); + bta_sys_free_timer(&bta_dm_search_cb.search_timer); #if ((defined BLE_INCLUDED) && (BLE_INCLUDED == TRUE)) #if ((defined BTA_GATT_INCLUDED) && (BTA_GATT_INCLUDED == TRUE) && SDP_INCLUDED == TRUE) @@ -391,6 +446,9 @@ static void bta_dm_sys_hw_cback( tBTA_SYS_HW_EVT status ) /* make sure the control block is properly initialized */ bta_dm_init_cb(); + /* make sure the Extended Inquiry Response is properly initialized */ + bta_dm_eir_cfg_init(); + /* and retrieve the callback */ bta_dm_cb.p_sec_cback = temp_cback; bta_dm_cb.is_bta_dm_active = TRUE; @@ -588,9 +646,52 @@ static void bta_dm_disable_timer_cback (TIMER_LIST_ENT *p_tle) *******************************************************************************/ void bta_dm_set_dev_name (tBTA_DM_MSG *p_data) { - BTM_SetLocalDeviceName((char *)p_data->set_name.name); +#if CLASSIC_BT_INCLUDED bta_dm_set_eir ((char *)p_data->set_name.name); +#endif /// CLASSIC_BT_INCLUDED +} + +void bta_dm_config_eir (tBTA_DM_MSG *p_data) +{ + tBTA_DM_API_CONFIG_EIR *config_eir = &p_data->config_eir; + + p_bta_dm_eir_cfg->bta_dm_eir_fec_required = config_eir->eir_fec_required; + p_bta_dm_eir_cfg->bta_dm_eir_included_uuid = config_eir->eir_included_uuid; + p_bta_dm_eir_cfg->bta_dm_eir_included_tx_power = config_eir->eir_included_tx_power; + p_bta_dm_eir_cfg->bta_dm_eir_flags = config_eir->eir_flags; + p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec_len = config_eir->eir_manufac_spec_len; + p_bta_dm_eir_cfg->bta_dm_eir_url_len = config_eir->eir_url_len; + + if (p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec) { + osi_free(p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec); + p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec = NULL; + } + if (config_eir->eir_manufac_spec) { + p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec = osi_malloc(config_eir->eir_manufac_spec_len); + if (p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec) { + memcpy(p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec, config_eir->eir_manufac_spec, config_eir->eir_manufac_spec_len); + } else { + APPL_TRACE_ERROR("%s, malloc failed.", __func__); + p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec_len = 0; + } + } + + if (p_bta_dm_eir_cfg->bta_dm_eir_url) { + osi_free(p_bta_dm_eir_cfg->bta_dm_eir_url); + p_bta_dm_eir_cfg->bta_dm_eir_url = NULL; + } + if (config_eir->eir_url) { + p_bta_dm_eir_cfg->bta_dm_eir_url = osi_malloc(config_eir->eir_url_len); + if (p_bta_dm_eir_cfg->bta_dm_eir_url == NULL) { + memcpy(p_bta_dm_eir_cfg->bta_dm_eir_url, config_eir->eir_url, config_eir->eir_url_len); + } else { + APPL_TRACE_ERROR("%s, malloc failed.", __func__); + p_bta_dm_eir_cfg->bta_dm_eir_url_len = 0; + } + } + + bta_dm_set_eir(NULL); } void bta_dm_update_white_list(tBTA_DM_MSG *p_data) @@ -3719,23 +3820,37 @@ static void bta_dm_set_eir (char *local_name) UINT8 custom_uuid_idx; #endif // BTA_EIR_SERVER_NUM_CUSTOM_UUID #endif // BTA_EIR_CANNED_UUID_LIST -#if (BTM_EIR_DEFAULT_FEC_REQUIRED == FALSE) - UINT8 free_eir_length = HCI_EXT_INQ_RESPONSE_LEN; -#else // BTM_EIR_DEFAULT_FEC_REQUIRED - UINT8 free_eir_length = HCI_DM5_PACKET_SIZE; -#endif // BTM_EIR_DEFAULT_FEC_REQUIRED + + UINT8 free_eir_length; + if (p_bta_dm_eir_cfg->bta_dm_eir_fec_required) { + free_eir_length = HCI_DM5_PACKET_SIZE; + } else { + free_eir_length = HCI_EXT_INQ_RESPONSE_LEN; + } + UINT8 num_uuid; UINT8 data_type; UINT8 local_name_len; + UINT8 eir_type[BTM_EIR_TYPE_MAX_NUM]; + UINT8 eir_type_num = 0; + + tBTA_STATUS status = BTA_SUCCESS; + /* wait until complete to disable */ if (bta_dm_cb.disable_timer.in_use) { + if (p_bta_dm_eir_cfg->config_eir_callback) { + p_bta_dm_eir_cfg->config_eir_callback(BTA_WRONG_MODE, eir_type_num , eir_type); + } return; } #if ( BTA_EIR_CANNED_UUID_LIST != TRUE ) /* wait until App is ready */ if (bta_dm_cb.app_ready_timer.in_use) { + if (p_bta_dm_eir_cfg->config_eir_callback) { + p_bta_dm_eir_cfg->config_eir_callback(BTA_WRONG_MODE, eir_type_num , eir_type); + } return; } @@ -3750,6 +3865,9 @@ static void bta_dm_set_eir (char *local_name) /* Allocate a buffer to hold HCI command */ if ((p_buf = (BT_HDR *)osi_malloc(BTM_CMD_BUF_SIZE)) == NULL) { APPL_TRACE_ERROR("bta_dm_set_eir couldn't allocate buffer"); + if (p_bta_dm_eir_cfg->config_eir_callback) { + p_bta_dm_eir_cfg->config_eir_callback(BTA_NO_RESOURCES, eir_type_num , eir_type); + } return; } p = (UINT8 *)p_buf + BTM_HCI_EIR_OFFSET; @@ -3790,6 +3908,7 @@ static void bta_dm_set_eir (char *local_name) UINT8_TO_STREAM(p, local_name_len + 1); UINT8_TO_STREAM(p, data_type); + eir_type[eir_type_num++] = data_type; if (local_name != NULL) { memcpy(p, local_name, local_name_len); @@ -3797,164 +3916,218 @@ static void bta_dm_set_eir (char *local_name) } free_eir_length -= local_name_len + 2; + /* if UUIDs are provided in configuration */ + if (p_bta_dm_eir_cfg->bta_dm_eir_included_uuid) { #if (BTA_EIR_CANNED_UUID_LIST == TRUE) - /* if UUID list is provided as static data in configuration */ - if (( p_bta_dm_eir_cfg->bta_dm_eir_uuid16_len > 0 ) - && (p_bta_dm_eir_cfg->bta_dm_eir_uuid16)) { - if ( free_eir_length > LEN_UUID_16 + 2) { - free_eir_length -= 2; + /* if UUID list is provided as static data in configuration */ + if (( p_bta_dm_eir_cfg->bta_dm_eir_uuid16_len > 0 ) + && (p_bta_dm_eir_cfg->bta_dm_eir_uuid16)) { + if ( free_eir_length > LEN_UUID_16 + 2) { + free_eir_length -= 2; - if ( free_eir_length >= p_bta_dm_eir_cfg->bta_dm_eir_uuid16_len) { - num_uuid = p_bta_dm_eir_cfg->bta_dm_eir_uuid16_len / LEN_UUID_16; - data_type = BTM_EIR_COMPLETE_16BITS_UUID_TYPE; - } else { /* not enough room for all UUIDs */ - APPL_TRACE_WARNING("BTA EIR: UUID 16-bit list is truncated"); - num_uuid = free_eir_length / LEN_UUID_16; - data_type = BTM_EIR_MORE_16BITS_UUID_TYPE; + if ( free_eir_length >= p_bta_dm_eir_cfg->bta_dm_eir_uuid16_len) { + num_uuid = p_bta_dm_eir_cfg->bta_dm_eir_uuid16_len / LEN_UUID_16; + data_type = BTM_EIR_COMPLETE_16BITS_UUID_TYPE; + } else { /* not enough room for all UUIDs */ + APPL_TRACE_WARNING("BTA EIR: UUID 16-bit list is truncated"); + num_uuid = free_eir_length / LEN_UUID_16; + data_type = BTM_EIR_MORE_16BITS_UUID_TYPE; + } + UINT8_TO_STREAM(p, num_uuid * LEN_UUID_16 + 1); + UINT8_TO_STREAM(p, data_type); + eir_type[eir_type_num++] = data_type; + memcpy(p, p_bta_dm_eir_cfg->bta_dm_eir_uuid16, num_uuid * LEN_UUID_16 ); + p += num_uuid * LEN_UUID_16; + free_eir_length -= num_uuid * LEN_UUID_16; + } else { + status = BTA_EIR_TOO_LARGE; } - UINT8_TO_STREAM(p, num_uuid * LEN_UUID_16 + 1); - UINT8_TO_STREAM(p, data_type); - memcpy(p, p_bta_dm_eir_cfg->bta_dm_eir_uuid16, num_uuid * LEN_UUID_16 ); - p += num_uuid * LEN_UUID_16; - free_eir_length -= num_uuid * LEN_UUID_16; } - } #else /* (BTA_EIR_CANNED_UUID_LIST == TRUE) */ - /* if UUID list is dynamic */ - if ( free_eir_length >= 2) { - p_length = p++; - p_type = p++; - num_uuid = 0; + /* if UUID list is dynamic */ + if ( free_eir_length >= 2) { + p_length = p++; + p_type = p++; + num_uuid = 0; - max_num_uuid = (free_eir_length - 2) / LEN_UUID_16; - data_type = BTM_GetEirSupportedServices( bta_dm_cb.eir_uuid, &p, max_num_uuid, &num_uuid ); + max_num_uuid = (free_eir_length - 2) / LEN_UUID_16; + data_type = BTM_GetEirSupportedServices( bta_dm_cb.eir_uuid, &p, max_num_uuid, &num_uuid ); - if ( data_type == BTM_EIR_MORE_16BITS_UUID_TYPE ) { - APPL_TRACE_WARNING("BTA EIR: UUID 16-bit list is truncated"); - } + if ( data_type == BTM_EIR_MORE_16BITS_UUID_TYPE ) { + APPL_TRACE_WARNING("BTA EIR: UUID 16-bit list is truncated"); + } #if (BTA_EIR_SERVER_NUM_CUSTOM_UUID > 0) - else { + else { + for (custom_uuid_idx = 0; custom_uuid_idx < BTA_EIR_SERVER_NUM_CUSTOM_UUID; custom_uuid_idx++) { + if (bta_dm_cb.custom_uuid[custom_uuid_idx].len == LEN_UUID_16) { + if ( num_uuid < max_num_uuid ) { + UINT16_TO_STREAM(p, bta_dm_cb.custom_uuid[custom_uuid_idx].uu.uuid16); + num_uuid++; + } else { + data_type = BTM_EIR_MORE_16BITS_UUID_TYPE; + APPL_TRACE_WARNING("BTA EIR: UUID 16-bit list is truncated"); + break; + } + } + } + } +#endif /* (BTA_EIR_SERVER_NUM_CUSTOM_UUID > 0) */ + + UINT8_TO_STREAM(p_length, num_uuid * LEN_UUID_16 + 1); + UINT8_TO_STREAM(p_type, data_type); + eir_type[eir_type_num++] = data_type; + free_eir_length -= num_uuid * LEN_UUID_16 + 2; + } else { + status = BTA_EIR_TOO_LARGE; + } +#endif /* (BTA_EIR_CANNED_UUID_LIST == TRUE) */ + +#if ( BTA_EIR_CANNED_UUID_LIST != TRUE )&&(BTA_EIR_SERVER_NUM_CUSTOM_UUID > 0) + /* Adding 32-bit UUID list */ + if ( free_eir_length >= 2) { + p_length = p++; + p_type = p++; + num_uuid = 0; + data_type = BTM_EIR_COMPLETE_32BITS_UUID_TYPE; + + max_num_uuid = (free_eir_length - 2) / LEN_UUID_32; + for (custom_uuid_idx = 0; custom_uuid_idx < BTA_EIR_SERVER_NUM_CUSTOM_UUID; custom_uuid_idx++) { - if (bta_dm_cb.custom_uuid[custom_uuid_idx].len == LEN_UUID_16) { + if (bta_dm_cb.custom_uuid[custom_uuid_idx].len == LEN_UUID_32) { if ( num_uuid < max_num_uuid ) { - UINT16_TO_STREAM(p, bta_dm_cb.custom_uuid[custom_uuid_idx].uu.uuid16); + UINT32_TO_STREAM(p, bta_dm_cb.custom_uuid[custom_uuid_idx].uu.uuid32); num_uuid++; } else { - data_type = BTM_EIR_MORE_16BITS_UUID_TYPE; - APPL_TRACE_WARNING("BTA EIR: UUID 16-bit list is truncated"); + data_type = BTM_EIR_MORE_32BITS_UUID_TYPE; + APPL_TRACE_WARNING("BTA EIR: UUID 32-bit list is truncated"); break; } } } + + UINT8_TO_STREAM(p_length, num_uuid * LEN_UUID_32 + 1); + UINT8_TO_STREAM(p_type, data_type); + eir_type[eir_type_num++] = data_type; + free_eir_length -= num_uuid * LEN_UUID_32 + 2; + } else { + status = BTA_EIR_TOO_LARGE; } -#endif /* (BTA_EIR_SERVER_NUM_CUSTOM_UUID > 0) */ - UINT8_TO_STREAM(p_length, num_uuid * LEN_UUID_16 + 1); - UINT8_TO_STREAM(p_type, data_type); - free_eir_length -= num_uuid * LEN_UUID_16 + 2; - } -#endif /* (BTA_EIR_CANNED_UUID_LIST == TRUE) */ + /* Adding 128-bit UUID list */ + if ( free_eir_length >= 2) { + p_length = p++; + p_type = p++; + num_uuid = 0; + data_type = BTM_EIR_COMPLETE_128BITS_UUID_TYPE; -#if ( BTA_EIR_CANNED_UUID_LIST != TRUE )&&(BTA_EIR_SERVER_NUM_CUSTOM_UUID > 0) - /* Adding 32-bit UUID list */ - if ( free_eir_length >= 2) { - p_length = p++; - p_type = p++; - num_uuid = 0; - data_type = BTM_EIR_COMPLETE_32BITS_UUID_TYPE; + max_num_uuid = (free_eir_length - 2) / LEN_UUID_128; - max_num_uuid = (free_eir_length - 2) / LEN_UUID_32; - - for (custom_uuid_idx = 0; custom_uuid_idx < BTA_EIR_SERVER_NUM_CUSTOM_UUID; custom_uuid_idx++) { - if (bta_dm_cb.custom_uuid[custom_uuid_idx].len == LEN_UUID_32) { - if ( num_uuid < max_num_uuid ) { - UINT32_TO_STREAM(p, bta_dm_cb.custom_uuid[custom_uuid_idx].uu.uuid32); - num_uuid++; - } else { - data_type = BTM_EIR_MORE_32BITS_UUID_TYPE; - APPL_TRACE_WARNING("BTA EIR: UUID 32-bit list is truncated"); - break; + for (custom_uuid_idx = 0; custom_uuid_idx < BTA_EIR_SERVER_NUM_CUSTOM_UUID; custom_uuid_idx++) { + if (bta_dm_cb.custom_uuid[custom_uuid_idx].len == LEN_UUID_128) { + if ( num_uuid < max_num_uuid ) { + ARRAY16_TO_STREAM(p, bta_dm_cb.custom_uuid[custom_uuid_idx].uu.uuid128); + num_uuid++; + } else { + data_type = BTM_EIR_MORE_128BITS_UUID_TYPE; + APPL_TRACE_WARNING("BTA EIR: UUID 128-bit list is truncated"); + break; + } } } + + UINT8_TO_STREAM(p_length, num_uuid * LEN_UUID_128 + 1); + UINT8_TO_STREAM(p_type, data_type); + eir_type[eir_type_num++] = data_type; + free_eir_length -= num_uuid * LEN_UUID_128 + 2; } - - UINT8_TO_STREAM(p_length, num_uuid * LEN_UUID_32 + 1); - UINT8_TO_STREAM(p_type, data_type); - free_eir_length -= num_uuid * LEN_UUID_32 + 2; - } - - /* Adding 128-bit UUID list */ - if ( free_eir_length >= 2) { - p_length = p++; - p_type = p++; - num_uuid = 0; - data_type = BTM_EIR_COMPLETE_128BITS_UUID_TYPE; - - max_num_uuid = (free_eir_length - 2) / LEN_UUID_128; - - for (custom_uuid_idx = 0; custom_uuid_idx < BTA_EIR_SERVER_NUM_CUSTOM_UUID; custom_uuid_idx++) { - if (bta_dm_cb.custom_uuid[custom_uuid_idx].len == LEN_UUID_128) { - if ( num_uuid < max_num_uuid ) { - ARRAY16_TO_STREAM(p, bta_dm_cb.custom_uuid[custom_uuid_idx].uu.uuid128); - num_uuid++; - } else { - data_type = BTM_EIR_MORE_128BITS_UUID_TYPE; - APPL_TRACE_WARNING("BTA EIR: UUID 128-bit list is truncated"); - break; - } - } + else { + status = BTA_EIR_TOO_LARGE; } - - UINT8_TO_STREAM(p_length, num_uuid * LEN_UUID_128 + 1); - UINT8_TO_STREAM(p_type, data_type); - free_eir_length -= num_uuid * LEN_UUID_128 + 2; - } #endif /* ( BTA_EIR_CANNED_UUID_LIST != TRUE )&&(BTA_EIR_SERVER_NUM_CUSTOM_UUID > 0) */ + } /* if Flags are provided in configuration */ - if (( p_bta_dm_eir_cfg->bta_dm_eir_flag_len > 0 ) - && ( p_bta_dm_eir_cfg->bta_dm_eir_flags ) - && ( free_eir_length >= p_bta_dm_eir_cfg->bta_dm_eir_flag_len + 2 )) { - UINT8_TO_STREAM(p, p_bta_dm_eir_cfg->bta_dm_eir_flag_len + 1); - UINT8_TO_STREAM(p, BTM_EIR_FLAGS_TYPE); - memcpy(p, p_bta_dm_eir_cfg->bta_dm_eir_flags, - p_bta_dm_eir_cfg->bta_dm_eir_flag_len); - p += p_bta_dm_eir_cfg->bta_dm_eir_flag_len; - free_eir_length -= p_bta_dm_eir_cfg->bta_dm_eir_flag_len + 2; + if ( p_bta_dm_eir_cfg->bta_dm_eir_flags != 0 ) { + if ( free_eir_length >= 3 ) { + UINT8_TO_STREAM(p, 2); + UINT8_TO_STREAM(p, BTM_EIR_FLAGS_TYPE); + eir_type[eir_type_num++] = BTM_EIR_FLAGS_TYPE; + UINT8_TO_STREAM(p, p_bta_dm_eir_cfg->bta_dm_eir_flags); + free_eir_length -= 3; + } else { + status = BTA_EIR_TOO_LARGE; + } } /* if Manufacturer Specific are provided in configuration */ if (( p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec_len > 0 ) - && ( p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec ) - && ( free_eir_length >= p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec_len + 2 )) { - p_length = p; + && ( p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec )) { + if ( free_eir_length >= p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec_len + 2) { + p_length = p; - UINT8_TO_STREAM(p, p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec_len + 1); - UINT8_TO_STREAM(p, BTM_EIR_MANUFACTURER_SPECIFIC_TYPE); - memcpy(p, p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec, + UINT8_TO_STREAM(p, p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec_len + 1); + UINT8_TO_STREAM(p, BTM_EIR_MANUFACTURER_SPECIFIC_TYPE); + eir_type[eir_type_num++] = BTM_EIR_MANUFACTURER_SPECIFIC_TYPE; + memcpy(p, p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec, p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec_len); - p += p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec_len; - free_eir_length -= p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec_len + 2; - + p += p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec_len; + free_eir_length -= p_bta_dm_eir_cfg->bta_dm_eir_manufac_spec_len + 2; + } else { + status = BTA_EIR_TOO_LARGE; + } } else { p_length = NULL; } /* if Inquiry Tx Resp Power compiled */ - if ((p_bta_dm_eir_cfg->bta_dm_eir_inq_tx_power) && - (free_eir_length >= 3)) { - UINT8_TO_STREAM(p, 2); /* Length field */ - UINT8_TO_STREAM(p, BTM_EIR_TX_POWER_LEVEL_TYPE); - UINT8_TO_STREAM(p, *(p_bta_dm_eir_cfg->bta_dm_eir_inq_tx_power)); - free_eir_length -= 3; + if (p_bta_dm_eir_cfg->bta_dm_eir_included_tx_power) { + if (free_eir_length >= 3) { + int min_power_level, max_power_level; + if (bredr_txpwr_get(&min_power_level, &max_power_level) == 0) { + INT8 btm_tx_power[BTM_TX_POWER_LEVEL_MAX + 1] = BTM_TX_POWER; + p_bta_dm_eir_cfg->bta_dm_eir_inq_tx_power = btm_tx_power[max_power_level]; + UINT8_TO_STREAM(p, 2); /* Length field */ + UINT8_TO_STREAM(p, BTM_EIR_TX_POWER_LEVEL_TYPE); + eir_type[eir_type_num++] = BTM_EIR_TX_POWER_LEVEL_TYPE; + UINT8_TO_STREAM(p, p_bta_dm_eir_cfg->bta_dm_eir_inq_tx_power); + free_eir_length -= 3; + } + } else { + status = BTA_EIR_TOO_LARGE; + } + } + + /* if URL are provided in configuration */ + if (( p_bta_dm_eir_cfg->bta_dm_eir_url_len > 0 ) + && ( p_bta_dm_eir_cfg->bta_dm_eir_url )) { + if ( free_eir_length >= p_bta_dm_eir_cfg->bta_dm_eir_url_len + 2 ) { + UINT8_TO_STREAM(p, p_bta_dm_eir_cfg->bta_dm_eir_url_len + 1); + UINT8_TO_STREAM(p, BTM_EIR_URL_TYPE); + eir_type[eir_type_num++] = BTM_EIR_URL_TYPE; + memcpy(p, p_bta_dm_eir_cfg->bta_dm_eir_url, + p_bta_dm_eir_cfg->bta_dm_eir_url_len); + p += p_bta_dm_eir_cfg->bta_dm_eir_url_len; + free_eir_length -= p_bta_dm_eir_cfg->bta_dm_eir_url_len + 2; + } else { + status = BTA_EIR_TOO_LARGE; + } } if ( free_eir_length ) { UINT8_TO_STREAM(p, 0); /* terminator of significant part */ } - BTM_WriteEIR( p_buf ); + tBTM_STATUS btm_status = BTM_WriteEIR( p_buf, p_bta_dm_eir_cfg->bta_dm_eir_fec_required ); + if ( btm_status == BTM_MODE_UNSUPPORTED) { + status = BTA_WRONG_MODE; + } else if (btm_status != BTM_SUCCESS) { + status = BTA_FAILURE; + } + + if (p_bta_dm_eir_cfg->config_eir_callback) { + p_bta_dm_eir_cfg->config_eir_callback(status, eir_type_num, eir_type); + } } /******************************************************************************* diff --git a/components/bt/bluedroid/bta/dm/bta_dm_api.c b/components/bt/bluedroid/bta/dm/bta_dm_api.c index 078903d31a..927257733f 100644 --- a/components/bt/bluedroid/bta/dm/bta_dm_api.c +++ b/components/bt/bluedroid/bta/dm/bta_dm_api.c @@ -183,6 +183,48 @@ void BTA_DmSetDeviceName(const char *p_name) } +void BTA_DmConfigEir(tBTA_DM_EIR_CONF *eir_config) +{ + tBTA_DM_API_CONFIG_EIR *p_msg; + + UINT8 eir_manufac_spec_len = eir_config->bta_dm_eir_manufac_spec_len; + UINT8 eir_url_len = eir_config->bta_dm_eir_url_len; + + if (eir_manufac_spec_len > HCI_EXT_INQ_RESPONSE_LEN) { + APPL_TRACE_WARNING ("%s: Manufacturer data is too long(%d), cut it to %d\n", + __func__, eir_manufac_spec_len, HCI_EXT_INQ_RESPONSE_LEN); + eir_manufac_spec_len = HCI_EXT_INQ_RESPONSE_LEN; + } + if (eir_url_len > HCI_EXT_INQ_RESPONSE_LEN) { + APPL_TRACE_WARNING ("%s: URL is too long(%d), cut it to %d\n", + __func__, eir_url_len, HCI_EXT_INQ_RESPONSE_LEN); + eir_url_len = HCI_EXT_INQ_RESPONSE_LEN; + } + + if ((p_msg = (tBTA_DM_API_CONFIG_EIR *) osi_malloc(sizeof(tBTA_DM_API_CONFIG_EIR) + eir_manufac_spec_len + eir_url_len)) != NULL) { + p_msg->hdr.event = BTA_DM_API_CONFIG_EIR_EVT; + + p_msg->eir_fec_required = eir_config->bta_dm_eir_fec_required; + p_msg->eir_included_tx_power = eir_config->bta_dm_eir_included_tx_power; + p_msg->eir_included_uuid = eir_config->bta_dm_eir_included_uuid; + p_msg->eir_flags = eir_config->bta_dm_eir_flags; + p_msg->eir_manufac_spec_len = eir_manufac_spec_len; + p_msg->eir_manufac_spec = p_msg->data; + p_msg->eir_url_len = eir_url_len; + p_msg->eir_url = p_msg->data + eir_manufac_spec_len; + + if (eir_manufac_spec_len > 0) { + memcpy(p_msg->eir_manufac_spec, eir_config->bta_dm_eir_manufac_spec, eir_manufac_spec_len); + } + + if (eir_url_len > 0) { + memcpy(p_msg->eir_url, eir_config->bta_dm_eir_url, eir_url_len); + } + + bta_sys_sendmsg(p_msg); + } +} + void BTA_DmUpdateWhiteList(BOOLEAN add_remove, BD_ADDR remote_addr, tBLE_ADDR_TYPE addr_type, tBTA_ADD_WHITELIST_CBACK *add_wl_cb) { tBTA_DM_API_UPDATE_WHITE_LIST *p_msg; diff --git a/components/bt/bluedroid/bta/dm/bta_dm_cfg.c b/components/bt/bluedroid/bta/dm/bta_dm_cfg.c index e9ad1f38d2..14462e4409 100644 --- a/components/bt/bluedroid/bta/dm/bta_dm_cfg.c +++ b/components/bt/bluedroid/bta/dm/bta_dm_cfg.c @@ -29,6 +29,7 @@ #include "bta/bta_api.h" #include "bta_dm_int.h" #include "bta/bta_jv_api.h" +#include "bta/bta_gap_bt_co.h" #ifndef BTA_DM_LINK_POLICY_SETTINGS #define BTA_DM_LINK_POLICY_SETTINGS (HCI_ENABLE_MASTER_SLAVE_SWITCH | HCI_ENABLE_HOLD_MODE | HCI_ENABLE_SNIFF_MODE | HCI_ENABLE_PARK_MODE) @@ -411,10 +412,12 @@ const UINT8 bta_dm_eir_uuid16_list[] = { 0x08, 0x11, /* Headset */ #endif // BTA_EIR_CANNED_UUID_LIST /* Extended Inquiry Response */ -const tBTA_DM_EIR_CONF bta_dm_eir_cfg = { +tBTA_DM_EIR_CONF bta_dm_eir_cfg = { + BTM_EIR_DEFAULT_FEC_REQUIRED, /* FEC required */ 50, /* minimum length of local name when it is shortened */ /* if length of local name is longer than this and EIR has not enough */ /* room for all UUID list then local name is shortened to this length */ + TRUE, /* Included UUIDs */ #if (BTA_EIR_CANNED_UUID_LIST == TRUE) 8, (UINT8 *)bta_dm_eir_uuid16_list, @@ -425,12 +428,17 @@ const tBTA_DM_EIR_CONF bta_dm_eir_cfg = { /* BTM_EIR_UUID_LKUP_TBL can be overrided */ }, #endif // BTA_EIR_CANNED_UUID_LIST - NULL, /* Inquiry TX power */ - 0, /* length of flags in bytes */ - NULL, /* flags for EIR */ + FALSE, /* Not included TX power*/ + 3, /* Inquiry TX power */ + 0, /* flags for EIR */ 0, /* length of manufacturer specific in bytes */ NULL, /* manufacturer specific */ - 0, /* length of additional data in bytes */ - NULL /* additional data */ + 0, /* length of URL in bytes */ + NULL, /* URL */ +#if (BTC_GAP_BT_INCLUDED == TRUE) + (tBTA_DM_CONFIG_EIR_CBACK *)btc_gap_bt_config_eir_cmpl_callback /* callback */ +#else + NULL +#endif /* #if (BTC_GAP_BT_INCLUDED == TRUE) */ }; tBTA_DM_EIR_CONF *p_bta_dm_eir_cfg = (tBTA_DM_EIR_CONF *) &bta_dm_eir_cfg; diff --git a/components/bt/bluedroid/bta/dm/bta_dm_main.c b/components/bt/bluedroid/bta/dm/bta_dm_main.c index 0f45dfbe97..3b9a7cc4ce 100644 --- a/components/bt/bluedroid/bta/dm/bta_dm_main.c +++ b/components/bt/bluedroid/bta/dm/bta_dm_main.c @@ -56,6 +56,7 @@ const tBTA_DM_ACTION bta_dm_action[BTA_DM_MAX_EVT] = { bta_dm_enable, /* BTA_DM_API_ENABLE_EVT */ bta_dm_disable, /* BTA_DM_API_DISABLE_EVT */ bta_dm_set_dev_name, /* BTA_DM_API_SET_NAME_EVT */ + bta_dm_config_eir, /* BTA_DM_API_CONFIG_EIR_EVT */ bta_dm_set_visibility, /* BTA_DM_API_SET_VISIBILITY_EVT */ bta_dm_acl_change, /* BTA_DM_ACL_CHANGE_EVT */ bta_dm_add_device, /* BTA_DM_API_ADD_DEVICE_EVT */ diff --git a/components/bt/bluedroid/bta/dm/include/bta_dm_int.h b/components/bt/bluedroid/bta/dm/include/bta_dm_int.h index 9ff577481c..2c21b0c574 100644 --- a/components/bt/bluedroid/bta/dm/include/bta_dm_int.h +++ b/components/bt/bluedroid/bta/dm/include/bta_dm_int.h @@ -52,6 +52,7 @@ enum { BTA_DM_API_ENABLE_EVT = BTA_SYS_EVT_START(BTA_ID_DM), BTA_DM_API_DISABLE_EVT, BTA_DM_API_SET_NAME_EVT, + BTA_DM_API_CONFIG_EIR_EVT, BTA_DM_API_SET_VISIBILITY_EVT, BTA_DM_ACL_CHANGE_EVT, @@ -188,6 +189,20 @@ typedef struct { BD_NAME name; /* max 248 bytes name, plus must be Null terminated */ } tBTA_DM_API_SET_NAME; +/* data type for BTA_DM_API_CONFIG_EIR_EVT */ +typedef struct { + BT_HDR hdr; + BOOLEAN eir_fec_required; + BOOLEAN eir_included_tx_power; + BOOLEAN eir_included_uuid; + UINT8 eir_flags; + UINT8 eir_manufac_spec_len; + UINT8 *eir_manufac_spec; + UINT8 eir_url_len; + UINT8 *eir_url; + UINT8 data[]; +}tBTA_DM_API_CONFIG_EIR; + typedef struct { BT_HDR hdr; BOOLEAN add_remove; @@ -478,7 +493,7 @@ typedef struct { typedef struct { BT_HDR hdr; - BOOLEAN add; + BOOLEAN add; UINT32 static_passkey; } tBTA_DM_API_SET_DEFAULT_PASSKEY; @@ -784,6 +799,7 @@ typedef union { tBTA_DM_API_ENABLE enable; tBTA_DM_API_SET_NAME set_name; + tBTA_DM_API_CONFIG_EIR config_eir; tBTA_DM_API_UPDATE_WHITE_LIST white_list; tBTA_DM_API_READ_ADV_TX_POWER read_tx_power; @@ -1185,7 +1201,7 @@ extern tBTA_DM_SSR_SPEC *p_bta_dm_ssr_spec; #endif /* #if (BTA_DM_PM_INCLUDED == TRUE) */ /* update dynamic BRCM Aware EIR data */ -extern const tBTA_DM_EIR_CONF bta_dm_eir_cfg; +extern tBTA_DM_EIR_CONF bta_dm_eir_cfg; extern tBTA_DM_EIR_CONF *p_bta_dm_eir_cfg; /* DM control block */ @@ -1222,6 +1238,7 @@ extern void bta_dm_search_sm_disable( void ); extern void bta_dm_enable (tBTA_DM_MSG *p_data); extern void bta_dm_disable (tBTA_DM_MSG *p_data); extern void bta_dm_set_dev_name (tBTA_DM_MSG *p_data); +extern void bta_dm_config_eir (tBTA_DM_MSG *p_data); extern void bta_dm_update_white_list(tBTA_DM_MSG *p_data); extern void bta_dm_ble_read_adv_tx_power(tBTA_DM_MSG *p_data); extern void bta_dm_ble_read_rssi(tBTA_DM_MSG *p_data); diff --git a/components/bt/bluedroid/bta/include/bta/bta_api.h b/components/bt/bluedroid/bta/include/bta/bta_api.h index c7bad21fed..a7dc0185f3 100644 --- a/components/bt/bluedroid/bta/include/bta/bta_api.h +++ b/components/bt/bluedroid/bta/include/bta/bta_api.h @@ -46,6 +46,7 @@ #define BTA_BUSY 3 #define BTA_NO_RESOURCES 4 #define BTA_WRONG_MODE 5 +#define BTA_EIR_TOO_LARGE 6 typedef UINT8 tBTA_STATUS; @@ -295,21 +296,31 @@ typedef struct { #endif } tBTA_DM_INQ; +/* Config EIR callback */ +typedef void (tBTA_DM_CONFIG_EIR_CBACK) (tBTA_STATUS status, UINT8 eir_type_num, UINT8 *eir_type); + typedef struct { + BOOLEAN bta_dm_eir_fec_required; /* FEC required */ UINT8 bta_dm_eir_min_name_len; /* minimum length of local name when it is shortened */ + + BOOLEAN bta_dm_eir_included_uuid; /* Included UUIDs or not */ #if (BTA_EIR_CANNED_UUID_LIST == TRUE) UINT8 bta_dm_eir_uuid16_len; /* length of 16-bit UUIDs */ UINT8 *bta_dm_eir_uuid16; /* 16-bit UUIDs */ #else - UINT32 uuid_mask[BTM_EIR_SERVICE_ARRAY_SIZE]; /* mask of UUID list in EIR */ + UINT32 uuid_mask[BTM_EIR_SERVICE_ARRAY_SIZE]; /* mask of UUID list in EIR */ #endif - INT8 *bta_dm_eir_inq_tx_power; /* Inquiry TX power */ - UINT8 bta_dm_eir_flag_len; /* length of flags in bytes */ - UINT8 *bta_dm_eir_flags; /* flags for EIR */ + + BOOLEAN bta_dm_eir_included_tx_power; /* Included inquiry TX power or not */ + INT8 bta_dm_eir_inq_tx_power; /* Inquiry TX power */ + + UINT8 bta_dm_eir_flags; /* flags for EIR */ UINT8 bta_dm_eir_manufac_spec_len; /* length of manufacturer specific in bytes */ UINT8 *bta_dm_eir_manufac_spec; /* manufacturer specific */ - UINT8 bta_dm_eir_additional_len; /* length of additional data in bytes */ - UINT8 *bta_dm_eir_additional; /* additional data */ + UINT8 bta_dm_eir_url_len; /* length of URL in bytes */ + UINT8 *bta_dm_eir_url; /* URL data */ + + tBTA_DM_CONFIG_EIR_CBACK *config_eir_callback; /* callback */ } tBTA_DM_EIR_CONF; #if BLE_INCLUDED == TRUE @@ -1450,6 +1461,18 @@ extern void BTA_DisableTestMode(void); *******************************************************************************/ extern void BTA_DmSetDeviceName(const char *p_name); +/******************************************************************************* +** +** Function BTA_DmConfigEir +** +** Description This function config EIR data of the local device. +** +** +** Returns void +** +*******************************************************************************/ +extern void BTA_DmConfigEir(tBTA_DM_EIR_CONF *eir_config); + extern void BTA_DmUpdateWhiteList(BOOLEAN add_remove, BD_ADDR remote_addr, tBLE_ADDR_TYPE addr_type, tBTA_ADD_WHITELIST_CBACK *add_wl_cb); extern void BTA_DmBleReadAdvTxPower(tBTA_CMPL_CB *cmpl_cb); @@ -2292,8 +2315,8 @@ extern void BTA_DmBleSetScanRspRaw (UINT8 *p_raw_scan_rsp, UINT32 raw_scan_rsp_l ** Returns None ** *******************************************************************************/ -extern void BTA_DmUpdateDuplicateExceptionalList(UINT8 subcode, UINT32 type, - BD_ADDR device_info, +extern void BTA_DmUpdateDuplicateExceptionalList(UINT8 subcode, UINT32 type, + BD_ADDR device_info, tBTA_UPDATE_DUPLICATE_EXCEPTIONAL_LIST_CMPL_CBACK p_update_duplicate_exceptional_list_cback); /******************************************************************************* diff --git a/components/bt/bluedroid/bta/include/bta/bta_gap_bt_co.h b/components/bt/bluedroid/bta/include/bta/bta_gap_bt_co.h new file mode 100644 index 0000000000..9e030ec354 --- /dev/null +++ b/components/bt/bluedroid/bta/include/bta/bta_gap_bt_co.h @@ -0,0 +1,29 @@ +// Copyright 2018 Espressif Systems (Shanghai) PTE LTD +// +// 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. + + +/****************************************************************************** + * + * This is the interface file for BT GAP call-out functions. + * + ******************************************************************************/ +#ifndef BTA_GAP_BT_CO_H +#define BTA_GAP_BT_CO_H + +#if (BTC_GAP_BT_INCLUDED == TRUE) + +extern void btc_gap_bt_config_eir_cmpl_callback (uint8_t status, uint8_t eir_type_num, uint8_t *eir_type); + +#endif /// (BTC_GAP_BT_INCLUDED == TRUE) +#endif /// BTA_GAP_BT_CO_H \ No newline at end of file diff --git a/components/bt/bluedroid/btc/core/btc_util.c b/components/bt/bluedroid/btc/core/btc_util.c index 633b03c60d..4174a1152f 100644 --- a/components/bt/bluedroid/btc/core/btc_util.c +++ b/components/bt/bluedroid/btc/core/btc_util.c @@ -32,6 +32,7 @@ #endif ///BTA_AV_INCLUDED == TRUE #include "common/bt_defs.h" #include "stack/btm_api.h" +#include "bta/bta_api.h" /************************************************************************************ ** Constants & Macros @@ -246,3 +247,36 @@ esp_bt_status_t btc_btm_status_to_esp_status (uint8_t btm_status) return esp_status; } + +esp_bt_status_t btc_bta_status_to_esp_status (uint8_t bta_status) +{ + esp_bt_status_t esp_status = ESP_BT_STATUS_FAIL; + switch(bta_status){ + case BTA_SUCCESS: + esp_status = ESP_BT_STATUS_SUCCESS; + break; + case BTA_FAILURE: + esp_status = ESP_BT_STATUS_FAIL; + break; + case BTA_PENDING: + esp_status = ESP_BT_STATUS_PENDING; + break; + case BTA_BUSY: + esp_status = ESP_BT_STATUS_BUSY; + break; + case BTA_NO_RESOURCES: + esp_status = ESP_BT_STATUS_NOMEM; + break; + case BTA_WRONG_MODE: + esp_status = ESP_BT_STATUS_NOT_READY; + break; + case BTA_EIR_TOO_LARGE: + esp_status = ESP_BT_STATUS_EIR_TOO_LARGE; + break; + default: + esp_status = ESP_BT_STATUS_FAIL; + break; + } + + return esp_status; +} \ No newline at end of file diff --git a/components/bt/bluedroid/btc/include/btc/btc_util.h b/components/bt/bluedroid/btc/include/btc/btc_util.h index df44297c3b..bc0570dd37 100644 --- a/components/bt/bluedroid/btc/include/btc/btc_util.h +++ b/components/bt/bluedroid/btc/include/btc/btc_util.h @@ -46,5 +46,6 @@ void uuid_to_string_legacy(bt_uuid_t *p_uuid, char *str); esp_bt_status_t btc_hci_to_esp_status(uint8_t hci_status); esp_bt_status_t btc_btm_status_to_esp_status (uint8_t btm_status); +esp_bt_status_t btc_bta_status_to_esp_status (uint8_t bta_status); #endif /* __BTC_UTIL_H__ */ diff --git a/components/bt/bluedroid/btc/profile/std/gap/bta_gap_bt_co.c b/components/bt/bluedroid/btc/profile/std/gap/bta_gap_bt_co.c new file mode 100644 index 0000000000..9710dcfa7a --- /dev/null +++ b/components/bt/bluedroid/btc/profile/std/gap/bta_gap_bt_co.c @@ -0,0 +1,40 @@ +// Copyright 2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 "btc_gap_bt.h" +#include "btc/btc_util.h" + +#if (BTC_GAP_BT_INCLUDED == TRUE) +void btc_gap_bt_config_eir_cmpl_callback (uint8_t status, uint8_t eir_type_num, uint8_t *eir_type) +{ + esp_bt_gap_cb_param_t param; + bt_status_t ret; + btc_msg_t msg; + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_GAP_BT; + msg.act = BTC_GAP_BT_CONFIG_EIR_DATA_EVT; + + param.config_eir_data.stat = btc_bta_status_to_esp_status(status); + param.config_eir_data.eir_type_num = eir_type_num; + memcpy(param.config_eir_data.eir_type, eir_type, eir_type_num); + + ret = btc_transfer_context(&msg, ¶m, + sizeof(esp_bt_gap_cb_param_t), NULL); + + if (ret != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s btc_transfer_context failed\n", __func__); + } +} +#endif /// (BTC_GAP_BT_INCLUDED == TRUE) \ No newline at end of file diff --git a/components/bt/bluedroid/btc/profile/std/gap/btc_gap_bt.c b/components/bt/bluedroid/btc/profile/std/gap/btc_gap_bt.c index 8de802f2ea..af2cfd2068 100644 --- a/components/bt/bluedroid/btc/profile/std/gap/btc_gap_bt.c +++ b/components/bt/bluedroid/btc/profile/std/gap/btc_gap_bt.c @@ -687,11 +687,27 @@ static void btc_gap_bt_ssp_passkey_reply(btc_gap_bt_args_t *arg) static void btc_gap_bt_ssp_confirm(btc_gap_bt_args_t *arg) { BTA_DmConfirm(arg->confirm_reply.bda.address, arg->confirm_reply.accept); - } #endif ///BT_SSP_INCLUDED == TRUE +static void btc_gap_bt_config_eir(btc_gap_bt_args_t *arg) +{ + tBTA_DM_EIR_CONF eir_config; + esp_bt_eir_data_t *eir_data = &arg->config_eir.eir_data; + + eir_config.bta_dm_eir_fec_required = eir_data->fec_required; + eir_config.bta_dm_eir_included_tx_power = eir_data->include_txpower; + eir_config.bta_dm_eir_included_uuid = eir_data->include_uuid; + eir_config.bta_dm_eir_flags = eir_data->flag; + eir_config.bta_dm_eir_manufac_spec_len = eir_data->manufacturer_len; + eir_config.bta_dm_eir_manufac_spec = eir_data->p_manufacturer_data; + eir_config.bta_dm_eir_url_len = eir_data->url_len; + eir_config.bta_dm_eir_url = eir_data->p_url; + + BTA_DmConfigEir(&eir_config); +} + void btc_gap_bt_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) { switch (msg->act) { @@ -712,13 +728,11 @@ void btc_gap_bt_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) break; case BTC_GAP_BT_ACT_SET_SECURITY_PARAM:{ btc_gap_bt_args_t *src = (btc_gap_bt_args_t *)p_src; - btc_gap_bt_args_t *dst = (btc_gap_bt_args_t *) p_dest; - uint8_t length = 0; + btc_gap_bt_args_t *dst = (btc_gap_bt_args_t *)p_dest; if (src->set_security_param.value) { - length = dst->set_security_param.len; - dst->set_security_param.value = osi_malloc(length); + dst->set_security_param.value = osi_malloc(src->set_security_param.len); if (dst->set_security_param.value != NULL) { - memcpy(dst->set_security_param.value, src->set_security_param.value, length); + memcpy(dst->set_security_param.value, src->set_security_param.value, src->set_security_param.len); } else { BTC_TRACE_ERROR("%s %d no mem\n",__func__, msg->act); } @@ -726,6 +740,30 @@ void btc_gap_bt_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) break; } #endif ///BT_SSP_INCLUDED == TRUE + + case BTC_GAP_BT_ACT_CONFIG_EIR:{ + btc_gap_bt_args_t *src = (btc_gap_bt_args_t *)p_src; + btc_gap_bt_args_t *dst = (btc_gap_bt_args_t *)p_dest; + if (src->config_eir.eir_data.p_manufacturer_data) { + dst->config_eir.eir_data.p_manufacturer_data = osi_malloc(src->config_eir.eir_data.manufacturer_len); + if (dst->config_eir.eir_data.p_manufacturer_data != NULL) { + memcpy(dst->config_eir.eir_data.p_manufacturer_data, src->config_eir.eir_data.p_manufacturer_data, src->config_eir.eir_data.manufacturer_len); + } else { + dst->config_eir.eir_data.manufacturer_len = 0; + BTC_TRACE_ERROR("%s %d no mem\n",__func__, msg->act); + } + } + if (src->config_eir.eir_data.p_url) { + dst->config_eir.eir_data.p_url = osi_malloc(src->config_eir.eir_data.url_len); + if (dst->config_eir.eir_data.p_url != NULL) { + memcpy(dst->config_eir.eir_data.p_url, src->config_eir.eir_data.p_url, src->config_eir.eir_data.url_len); + } else { + dst->config_eir.eir_data.url_len = 0; + BTC_TRACE_ERROR("%s %d no mem\n",__func__, msg->act); + } + } + break; + } default: BTC_TRACE_ERROR("Unhandled deep copy %d\n", msg->act); break; @@ -752,9 +790,20 @@ void btc_gap_bt_arg_deep_free(btc_msg_t *msg) case BTC_GAP_BT_ACT_CONFIRM_REPLY: break; case BTC_GAP_BT_ACT_SET_SECURITY_PARAM: - osi_free(arg->set_security_param.value); + if (arg->set_security_param.value) { + osi_free(arg->set_security_param.value); + } break; #endif ///BT_SSP_INCLUDED == TRUE + + case BTC_GAP_BT_ACT_CONFIG_EIR: + if (arg->config_eir.eir_data.p_manufacturer_data) { + osi_free(arg->config_eir.eir_data.p_manufacturer_data); + } + if (arg->config_eir.eir_data.p_url) { + osi_free(arg->config_eir.eir_data.p_url); + } + break; default: BTC_TRACE_ERROR("Unhandled deep copy %d, arg: %p\n", msg->act, arg); break; @@ -820,7 +869,10 @@ void btc_gap_bt_call_handler(btc_msg_t *msg) break; } #endif ///BT_SSP_INCLUDED == TRUE - + case BTC_GAP_BT_ACT_CONFIG_EIR: { + btc_gap_bt_config_eir(arg); + break; + } default: break; } @@ -853,6 +905,7 @@ void btc_gap_bt_cb_deep_free(btc_msg_t *msg) osi_free(((tBTA_DM_SEARCH_PARAM *) (msg->arg)) ->p_data); break; case BTC_GAP_BT_READ_RSSI_DELTA_EVT: + case BTC_GAP_BT_CONFIG_EIR_DATA_EVT: case BTC_GAP_BT_AUTH_CMPL_EVT: case BTC_GAP_BT_PIN_REQ_EVT: #if (BT_SSP_INCLUDED == TRUE) @@ -886,6 +939,10 @@ void btc_gap_bt_cb_handler(btc_msg_t *msg) btc_gap_bt_cb_to_app(ESP_BT_GAP_READ_RSSI_DELTA_EVT, (esp_bt_gap_cb_param_t *)msg->arg); break; } + case BTC_GAP_BT_CONFIG_EIR_DATA_EVT: { + btc_gap_bt_cb_to_app(ESP_BT_GAP_CONFIG_EIR_DATA_EVT, (esp_bt_gap_cb_param_t *)msg->arg); + break; + } case BTC_GAP_BT_AUTH_CMPL_EVT:{ btc_gap_bt_cb_to_app(ESP_BT_GAP_AUTH_CMPL_EVT, (esp_bt_gap_cb_param_t *)msg->arg); break; diff --git a/components/bt/bluedroid/btc/profile/std/include/btc_gap_bt.h b/components/bt/bluedroid/btc/profile/std/include/btc_gap_bt.h index 51e685965b..cae2a09bc1 100644 --- a/components/bt/bluedroid/btc/profile/std/include/btc_gap_bt.h +++ b/components/bt/bluedroid/btc/profile/std/include/btc_gap_bt.h @@ -32,6 +32,7 @@ typedef enum { BTC_GAP_BT_KEY_NOTIF_EVT, BTC_GAP_BT_KEY_REQ_EVT, BTC_GAP_BT_READ_RSSI_DELTA_EVT, + BTC_GAP_BT_CONFIG_EIR_DATA_EVT, }btc_gap_bt_evt_t; typedef enum { @@ -48,6 +49,7 @@ typedef enum { BTC_GAP_BT_ACT_SET_SECURITY_PARAM, BTC_GAP_BT_ACT_PASSKEY_REPLY, BTC_GAP_BT_ACT_CONFIRM_REPLY, + BTC_GAP_BT_ACT_CONFIG_EIR, } btc_gap_bt_act_t; /* btc_bt_gap_args_t */ @@ -124,6 +126,11 @@ typedef union { bt_bdaddr_t bda; bool accept; } confirm_reply; + + // BTC_GAP_BT_ACT_CONFIG_EIR + struct config_eir_args { + esp_bt_eir_data_t eir_data; + } config_eir; } btc_gap_bt_args_t; void btc_gap_bt_call_handler(btc_msg_t *msg); diff --git a/components/bt/bluedroid/common/include/common/bt_defs.h b/components/bt/bluedroid/common/include/common/bt_defs.h index 77719bc847..7cfa3d5b8e 100644 --- a/components/bt/bluedroid/common/include/common/bt_defs.h +++ b/components/bt/bluedroid/common/include/common/bt_defs.h @@ -88,6 +88,7 @@ typedef enum { BT_STATUS_PARAM_OUT_OF_RANGE, BT_STATUS_TIMEOUT, BT_STATUS_MEMORY_FULL, + BT_STATUS_EIR_TOO_LARGE, } bt_status_t; #ifndef CPU_LITTLE_ENDIAN diff --git a/components/bt/bluedroid/common/include/common/bt_target.h b/components/bt/bluedroid/common/include/common/bt_target.h index 98dbe7a16c..1745ab0dfa 100644 --- a/components/bt/bluedroid/common/include/common/bt_target.h +++ b/components/bt/bluedroid/common/include/common/bt_target.h @@ -151,13 +151,13 @@ #ifndef CONFIG_BTDM_BLE_ADV_REPORT_FLOW_CTRL_NUM #define BLE_ADV_REPORT_FLOW_CONTROL_NUM 100 -#else +#else #define BLE_ADV_REPORT_FLOW_CONTROL_NUM CONFIG_BTDM_BLE_ADV_REPORT_FLOW_CTRL_NUM #endif /* CONFIG_BTDM_BLE_ADV_REPORT_FLOW_CTRL_NUM */ #ifndef CONFIG_BTDM_BLE_ADV_REPORT_DISCARD_THRSHOLD #define BLE_ADV_REPORT_DISCARD_THRSHOLD 20 -#else +#else #define BLE_ADV_REPORT_DISCARD_THRSHOLD CONFIG_BTDM_BLE_ADV_REPORT_DISCARD_THRSHOLD #endif /* CONFIG_BTDM_BLE_ADV_REPORT_DISCARD_THRSHOLD */ @@ -1056,6 +1056,14 @@ #define BTM_BLE_ADV_TX_POWER {-12, -9, -6, -3, 0, 3, 6, 9} #endif +#ifndef BTM_TX_POWER +#define BTM_TX_POWER {-12, -9, -6, -3, 0, 3, 6, 9} +#endif + +#ifndef BTM_TX_POWER_LEVEL_MAX +#define BTM_TX_POWER_LEVEL_MAX 7 +#endif + #ifndef BLE_BATCH_SCAN_INCLUDED #define BLE_BATCH_SCAN_INCLUDED TRUE diff --git a/components/bt/bluedroid/stack/btm/btm_inq.c b/components/bt/bluedroid/stack/btm/btm_inq.c index 7feda0e6ab..4845ac5ab0 100644 --- a/components/bt/bluedroid/stack/btm/btm_inq.c +++ b/components/bt/bluedroid/stack/btm/btm_inq.c @@ -2387,16 +2387,17 @@ void btm_read_linq_tx_power_complete(UINT8 *p) ** ** Parameters p_buff - allocated HCI command buffer including extended ** inquriry response +** fec_required - FEC is required or not ** ** Returns BTM_SUCCESS - if successful ** BTM_MODE_UNSUPPORTED - if local device cannot support it ** *******************************************************************************/ -tBTM_STATUS BTM_WriteEIR( BT_HDR *p_buff ) +tBTM_STATUS BTM_WriteEIR( BT_HDR *p_buff, BOOLEAN fec_required) { if (controller_get_interface()->supports_extended_inquiry_response()) { BTM_TRACE_API("Write Extended Inquiry Response to controller\n"); - btsnd_hcic_write_ext_inquiry_response (p_buff, BTM_EIR_DEFAULT_FEC_REQUIRED); + btsnd_hcic_write_ext_inquiry_response (p_buff, fec_required); return BTM_SUCCESS; } else { osi_free(p_buff); diff --git a/components/bt/bluedroid/stack/include/stack/bt_types.h b/components/bt/bluedroid/stack/include/stack/bt_types.h index 52385cb513..f8b15919d2 100644 --- a/components/bt/bluedroid/stack/include/stack/bt_types.h +++ b/components/bt/bluedroid/stack/include/stack/bt_types.h @@ -446,6 +446,7 @@ typedef struct { #define BT_EIR_OOB_COD_TYPE 0x0D #define BT_EIR_OOB_SSP_HASH_C_TYPE 0x0E #define BT_EIR_OOB_SSP_RAND_R_TYPE 0x0F +#define BT_EIR_URL_TYPE 0x24 #define BT_EIR_MANUFACTURER_SPECIFIC_TYPE 0xFF #define BT_OOB_COD_SIZE 3 diff --git a/components/bt/bluedroid/stack/include/stack/btm_api.h b/components/bt/bluedroid/stack/include/stack/btm_api.h index 6aa833c036..9667b4c76d 100644 --- a/components/bt/bluedroid/stack/include/stack/btm_api.h +++ b/components/bt/bluedroid/stack/include/stack/btm_api.h @@ -540,8 +540,11 @@ typedef UINT8 tBTM_EIR_SEARCH_RESULT; #define BTM_EIR_SHORTENED_LOCAL_NAME_TYPE HCI_EIR_SHORTENED_LOCAL_NAME_TYPE /* 0x08 */ #define BTM_EIR_COMPLETE_LOCAL_NAME_TYPE HCI_EIR_COMPLETE_LOCAL_NAME_TYPE /* 0x09 */ #define BTM_EIR_TX_POWER_LEVEL_TYPE HCI_EIR_TX_POWER_LEVEL_TYPE /* 0x0A */ +#define BTM_EIR_URL_TYPE HCI_EIR_URL_TYPE /* 0x24 */ #define BTM_EIR_MANUFACTURER_SPECIFIC_TYPE HCI_EIR_MANUFACTURER_SPECIFIC_TYPE /* 0xFF */ +#define BTM_EIR_TYPE_MAX_NUM 12 /* Max EIR types */ + /* the following EIR tags are defined to OOB, not regular EIR data */ #define BTM_EIR_OOB_BD_ADDR_TYPE HCI_EIR_OOB_BD_ADDR_TYPE /* 6 bytes */ #define BTM_EIR_OOB_COD_TYPE HCI_EIR_OOB_COD_TYPE /* 3 bytes */ @@ -3867,13 +3870,14 @@ tBTM_STATUS BTM_DeleteStoredLinkKey(BD_ADDR bd_addr, tBTM_CMPL_CB *p_cb); ** ** Parameters p_buff - allocated HCI command buffer including extended ** inquriry response +** fec_required - FEC is required or not ** ** Returns BTM_SUCCESS - if successful ** BTM_MODE_UNSUPPORTED - if local device cannot support it ** *******************************************************************************/ //extern -tBTM_STATUS BTM_WriteEIR( BT_HDR *p_buff ); +tBTM_STATUS BTM_WriteEIR( BT_HDR *p_buff, BOOLEAN fec_required); /******************************************************************************* ** diff --git a/components/bt/bluedroid/stack/include/stack/hcidefs.h b/components/bt/bluedroid/stack/include/stack/hcidefs.h index 6249f2c395..b1b73efa78 100644 --- a/components/bt/bluedroid/stack/include/stack/hcidefs.h +++ b/components/bt/bluedroid/stack/include/stack/hcidefs.h @@ -1139,6 +1139,7 @@ #define HCI_EIR_SHORTENED_LOCAL_NAME_TYPE BT_EIR_SHORTENED_LOCAL_NAME_TYPE #define HCI_EIR_COMPLETE_LOCAL_NAME_TYPE BT_EIR_COMPLETE_LOCAL_NAME_TYPE #define HCI_EIR_TX_POWER_LEVEL_TYPE BT_EIR_TX_POWER_LEVEL_TYPE +#define HCI_EIR_URL_TYPE BT_EIR_URL_TYPE #define HCI_EIR_MANUFACTURER_SPECIFIC_TYPE BT_EIR_MANUFACTURER_SPECIFIC_TYPE #define HCI_EIR_OOB_BD_ADDR_TYPE BT_EIR_OOB_BD_ADDR_TYPE #define HCI_EIR_OOB_COD_TYPE BT_EIR_OOB_COD_TYPE From c8d967417c50728a7ed9e77f903d062457cc0702 Mon Sep 17 00:00:00 2001 From: "Michael (XIAO Xufeng)" Date: Wed, 5 Jun 2019 14:43:23 +0800 Subject: [PATCH 008/486] bootloader: remove duplicate region overlap logic (MINOR CHANGE) --- components/bootloader_support/include/bootloader_util.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/components/bootloader_support/include/bootloader_util.h b/components/bootloader_support/include/bootloader_util.h index 30f8bd8d2c..500498d397 100644 --- a/components/bootloader_support/include/bootloader_util.h +++ b/components/bootloader_support/include/bootloader_util.h @@ -29,6 +29,7 @@ static inline bool bootloader_util_regions_overlap( const intptr_t start1, const intptr_t end1, const intptr_t start2, const intptr_t end2) { - return (end1 > start2 && end2 > start1) || - !(end1 <= start2 || end2 <= start1); + assert(end1>start1); + assert(end2>start2); + return (end1 > start2 && end2 > start1); } From 7a6ff35a2ae055781438e2b72c8e1e9a000035c5 Mon Sep 17 00:00:00 2001 From: Sergei Silnov Date: Mon, 3 Jun 2019 12:37:52 +0200 Subject: [PATCH 009/486] idf.py: Add check for new cmake cache values --- tools/idf.py | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/tools/idf.py b/tools/idf.py index c9a3e464ee..8184fdd708 100755 --- a/tools/idf.py +++ b/tools/idf.py @@ -150,6 +150,32 @@ def detect_cmake_generator(): raise FatalError("To use %s, either the 'ninja' or 'GNU make' build tool must be available in the PATH" % PROG) +def _strip_quotes(value, regexp=re.compile(r"^\"(.*)\"$|^'(.*)'$|^(.*)$")): + """ + Strip quotes like CMake does during parsing cache entries + """ + + return [x for x in regexp.match(value).groups() if x is not None][0].rstrip() + + +def _new_cmakecache_entries(cache_path, new_cache_entries): + if not os.path.exists(cache_path): + return True + + current_cache = parse_cmakecache(cache_path) + + if new_cache_entries: + current_cache = parse_cmakecache(cache_path) + + for entry in new_cache_entries: + key, value = entry.split("=", 1) + current_value = current_cache.get(key, None) + if current_value is None or _strip_quotes(value) != current_value: + return True + + return False + + def _ensure_build_directory(args, always_run_cmake=False): """Check the build directory exists and that cmake has been run there. @@ -175,7 +201,8 @@ def _ensure_build_directory(args, always_run_cmake=False): if not os.path.isdir(build_dir): os.makedirs(build_dir) cache_path = os.path.join(build_dir, "CMakeCache.txt") - if not os.path.exists(cache_path) or always_run_cmake: + + if always_run_cmake or _new_cmakecache_entries(cache_path, args.define_cache_entry): if args.generator is None: args.generator = detect_cmake_generator() try: @@ -230,7 +257,7 @@ def parse_cmakecache(path): for line in f: # cmake cache lines look like: CMAKE_CXX_FLAGS_DEBUG:STRING=-g # groups are name, type, value - m = re.match(r"^([^#/:=]+):([^:=]+)=(.+)\n$", line) + m = re.match(r"^([^#/:=]+):([^:=]+)=(.*)\n$", line) if m: result[m.group(1)] = m.group(3) return result From 2503af2464a41fe21a98ee21381cf28d915e2c1b Mon Sep 17 00:00:00 2001 From: xiehang Date: Wed, 29 May 2019 11:52:51 +0800 Subject: [PATCH 010/486] wps: add overlap event modify some header files to be consistent with vnc --- components/esp_event/event_send_compat.inc | 5 +++++ components/esp_event/include/esp_event_legacy.h | 1 + components/esp_wifi/include/esp_wifi_types.h | 1 + components/esp_wifi/lib_esp32 | 2 +- .../wpa_supplicant/include/wpa2/eap_peer/eap_i.h | 10 ++++++++-- components/wpa_supplicant/include/wps/wps.h | 6 +++--- 6 files changed, 19 insertions(+), 6 deletions(-) diff --git a/components/esp_event/event_send_compat.inc b/components/esp_event/event_send_compat.inc index 42b4daf473..eff799975b 100644 --- a/components/esp_event/event_send_compat.inc +++ b/components/esp_event/event_send_compat.inc @@ -80,6 +80,7 @@ esp_err_t esp_event_send_to_default_loop(system_event_t *event) HANDLE_SYS_EVENT(WIFI, STA_WPS_ER_TIMEOUT); HANDLE_SYS_EVENT_ARG(WIFI, STA_WPS_ER_FAILED, sta_er_fail_reason); HANDLE_SYS_EVENT_ARG(WIFI, STA_WPS_ER_PIN, sta_er_pin); + HANDLE_SYS_EVENT(WIFI, STA_WPS_ER_PBC_OVERLAP); /* AP events */ HANDLE_SYS_EVENT(WIFI, AP_START); @@ -233,6 +234,10 @@ static void esp_system_event_debug(const system_event_t* event) ESP_LOGD(TAG, "SYSTEM_EVENT_STA_WPS_ER_PIN"); break; } + case SYSTEM_EVENT_STA_WPS_ER_PBC_OVERLAP: { + ESP_LOGD(TAG, "SYSTEM_EVENT_STA_WPS_ER_PBC_OVERLAP"); + break; + } case SYSTEM_EVENT_AP_START: { ESP_LOGD(TAG, "SYSTEM_EVENT_AP_START"); break; diff --git a/components/esp_event/include/esp_event_legacy.h b/components/esp_event/include/esp_event_legacy.h index 7748f69a0d..44e95546a1 100644 --- a/components/esp_event/include/esp_event_legacy.h +++ b/components/esp_event/include/esp_event_legacy.h @@ -40,6 +40,7 @@ typedef enum { SYSTEM_EVENT_STA_WPS_ER_FAILED, /*!< ESP32 station wps fails in enrollee mode */ SYSTEM_EVENT_STA_WPS_ER_TIMEOUT, /*!< ESP32 station wps timeout in enrollee mode */ SYSTEM_EVENT_STA_WPS_ER_PIN, /*!< ESP32 station wps pin code in enrollee mode */ + SYSTEM_EVENT_STA_WPS_ER_PBC_OVERLAP, /*!< ESP32 station wps overlap in enrollee mode */ SYSTEM_EVENT_AP_START, /*!< ESP32 soft-AP start */ SYSTEM_EVENT_AP_STOP, /*!< ESP32 soft-AP stop */ SYSTEM_EVENT_AP_STACONNECTED, /*!< a station connected to ESP32 soft-AP */ diff --git a/components/esp_wifi/include/esp_wifi_types.h b/components/esp_wifi/include/esp_wifi_types.h index 8e930c555d..426ba7f58a 100644 --- a/components/esp_wifi/include/esp_wifi_types.h +++ b/components/esp_wifi/include/esp_wifi_types.h @@ -508,6 +508,7 @@ typedef enum { WIFI_EVENT_STA_WPS_ER_FAILED, /**< ESP32 station wps fails in enrollee mode */ WIFI_EVENT_STA_WPS_ER_TIMEOUT, /**< ESP32 station wps timeout in enrollee mode */ WIFI_EVENT_STA_WPS_ER_PIN, /**< ESP32 station wps pin code in enrollee mode */ + WIFI_EVENT_STA_WPS_ER_PBC_OVERLAP, /**< ESP32 station wps overlap in enrollee mode */ WIFI_EVENT_AP_START, /**< ESP32 soft-AP start */ WIFI_EVENT_AP_STOP, /**< ESP32 soft-AP stop */ diff --git a/components/esp_wifi/lib_esp32 b/components/esp_wifi/lib_esp32 index b8b96f985a..fc518f822e 160000 --- a/components/esp_wifi/lib_esp32 +++ b/components/esp_wifi/lib_esp32 @@ -1 +1 @@ -Subproject commit b8b96f985aee155682a2907c6c0f3b693bb43785 +Subproject commit fc518f822e23b6f4fd8870d61cb1dba107727df2 diff --git a/components/wpa_supplicant/include/wpa2/eap_peer/eap_i.h b/components/wpa_supplicant/include/wpa2/eap_peer/eap_i.h index 6204f46538..401d3687a9 100644 --- a/components/wpa_supplicant/include/wpa2/eap_peer/eap_i.h +++ b/components/wpa_supplicant/include/wpa2/eap_peer/eap_i.h @@ -98,6 +98,13 @@ struct eap_method { #define BLOB_NAME_LEN 3 #define BLOB_NUM 3 +enum SIG_WPA2 { + SIG_WPA2_START = 0, + SIG_WPA2_RX, + SIG_WPA2_TASK_DEL, + SIG_WPA2_MAX, +}; + /** * struct eap_sm - EAP state machine data */ @@ -114,8 +121,7 @@ struct eap_sm { u8 current_identifier; u8 ownaddr[ETH_ALEN]; #ifdef USE_WPA2_TASK -#define SIG_WPA2_NUM 2 - u8 wpa2_sig_cnt[SIG_WPA2_NUM]; + u8 wpa2_sig_cnt[SIG_WPA2_MAX]; #endif u8 finish_state; diff --git a/components/wpa_supplicant/include/wps/wps.h b/components/wpa_supplicant/include/wps/wps.h index 4254495741..7249d6eee8 100644 --- a/components/wpa_supplicant/include/wps/wps.h +++ b/components/wpa_supplicant/include/wps/wps.h @@ -1009,9 +1009,9 @@ enum wps_cb_status { typedef void (*wps_st_cb_t)(int status); #ifdef USE_WPS_TASK -#define SIG_WPS_START 0 -#define SIG_WPS_RX 1 -#define SIG_WPS_NUM 2 +#define SIG_WPS_START 2 +#define SIG_WPS_RX 3 +#define SIG_WPS_NUM 9 #endif #define WPS_EAP_EXT_VENDOR_TYPE "WFA-SimpleConfig-Enrollee-1-0" From 5f5428233e969f87e99d70f01a066d9878f48b9e Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 5 Jun 2019 12:58:27 +1000 Subject: [PATCH 011/486] partition table: Update documentation about "test" partition type Reported from forum: https://esp32.com/viewtopic.php?f=13&t=10777&p=44164#p44164 --- docs/en/api-guides/bootloader.rst | 6 ++++-- docs/en/api-guides/partition-tables.rst | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/en/api-guides/bootloader.rst b/docs/en/api-guides/bootloader.rst index 6e5efbcc04..aea98d6c91 100644 --- a/docs/en/api-guides/bootloader.rst +++ b/docs/en/api-guides/bootloader.rst @@ -42,13 +42,15 @@ Partition table.:: ota_0, 0, ota_0, , 512K ota_1, 0, ota_1, , 512K +.. _bootloader_boot_from_test_firmware: + Boot from TEST firmware ------------------------ The user can write a special firmware for testing in production, and run it as needed. The partition table also needs a dedicated partition for this testing firmware (See `partition table`). To trigger a test app you need to set :ref:`CONFIG_BOOTLOADER_APP_TEST`. -:ref:`CONFIG_BOOTLOADER_NUM_PIN_APP_TEST` - number of the GPIO input to boot TEST partition. The selected GPIO will be configured as an input with internal pull-up enabled. To trigger a test app, this GPIO must be pulled low on reset. -After the GPIO input is deactivated and the device reboots, the old application will boot (factory or any OTA slot). +:ref:`CONFIG_BOOTLOADER_NUM_PIN_APP_TEST` - GPIO number to boot TEST partition. The selected GPIO will be configured as an input with internal pull-up enabled. To trigger a test app, this GPIO must be pulled low on reset. +After the GPIO input is deactivated and the device reboots, the normally configured application will boot (factory or any OTA slot). :ref:`CONFIG_BOOTLOADER_HOLD_TIME_GPIO` - this is hold time of GPIO for reset/test mode (by default 5 seconds). The GPIO must be held low continuously for this period of time after reset before a factory reset or test partition boot (as applicable) is performed. diff --git a/docs/en/api-guides/partition-tables.rst b/docs/en/api-guides/partition-tables.rst index 502c7118c9..f538a507b9 100644 --- a/docs/en/api-guides/partition-tables.rst +++ b/docs/en/api-guides/partition-tables.rst @@ -92,7 +92,7 @@ The 8-bit subtype field is specific to a given partition type. esp-idf currently - OTA never updates the factory partition. - If you want to conserve flash usage in an OTA project, you can remove the factory partition and use ota_0 instead. - ota_0 (0x10) ... ota_15 (0x1F) are the OTA app slots. Refer to the :doc:`OTA documentation <../api-reference/system/ota>` for more details, which then use the OTA data partition to configure which app slot the bootloader should boot. If using OTA, an application should have at least two OTA application slots (ota_0 & ota_1). Refer to the :doc:`OTA documentation <../api-reference/system/ota>` for more details. - - test (0x2) is a reserved subtype for factory test procedures. It is not currently supported by the esp-idf bootloader. + - test (0x20) is a reserved subtype for factory test procedures. It will be used as the fallback boot partition if no other valid app partition is found. It is also possible to configure the bootloader to read a GPIO input during each boot, and boot this partition if the GPIO is held low, see :ref:`bootloader_boot_from_test_firmware`. * When type is "data", the subtype field can be specified as ota (0), phy (1), nvs (2), or nvs_keys (4). From 912c75372ccd1fab272d0edf932d343e8d4589a2 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 7 Jun 2019 19:57:47 +0800 Subject: [PATCH 012/486] confgen.py: don't output compatibility definitions for options which are not defined MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For example, if a renamed option CONFIG_NEW is a bool with value “n”, kconfiglib will not generate a define for it in the Kconfig file. The define (#define CONFIG_NEW 1) will only be generated if the option is “y” or “m”. However the compatibility definition was always generated: #define CONFIG_OLD CONFIG_NEW. This broke the #ifdef checks which depended on the old option names. This commit wraps each compatibility definition: #ifdef CONFIG_NEW #define CONFIG_OLD CONFIG_NEW #endif so that the CONFIG_OLD definition is only generated if CONFIG_NEW is defined. --- tools/kconfig_new/confgen.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/kconfig_new/confgen.py b/tools/kconfig_new/confgen.py index 4ea0331772..bbd4e28042 100755 --- a/tools/kconfig_new/confgen.py +++ b/tools/kconfig_new/confgen.py @@ -167,7 +167,8 @@ class DeprecatedOptions(object): f_o.write('\n/* List of deprecated options */\n') for dep_opt in sorted(self.r_dic): new_opt = self.r_dic[dep_opt] - f_o.write('#define {}{} {}{}\n'.format(self.config_prefix, dep_opt, self.config_prefix, new_opt)) + f_o.write('#ifdef {}{}\n#define {}{} {}{}\n#endif\n\n'.format(self.config_prefix, new_opt, + self.config_prefix, dep_opt, self.config_prefix, new_opt)) def main(): From f3aab7d5362b0c78b093299b1fac62cc28ef688a Mon Sep 17 00:00:00 2001 From: baohongde Date: Mon, 10 Jun 2019 15:14:23 +0800 Subject: [PATCH 013/486] components/bt: Fix assert due to alloc LMP TX buffer failed --- components/bt/lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/bt/lib b/components/bt/lib index 6834a6bfcf..45df297ff2 160000 --- a/components/bt/lib +++ b/components/bt/lib @@ -1 +1 @@ -Subproject commit 6834a6bfcfd395acc37249fae1c3121fc2e705cb +Subproject commit 45df297ff295c106171d8c3621216e9f48f7d8b4 From af3a2f3aba049341518c597f624705753255cb21 Mon Sep 17 00:00:00 2001 From: Renz Christian Bagaporo Date: Mon, 10 Jun 2019 18:19:15 +0800 Subject: [PATCH 014/486] esp_event: fix placement issue when building with make The CMake build uses the linker fragment file to place functions in IRAM on certain configurations. This commit does that for Make as well. --- components/esp_event/component.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/components/esp_event/component.mk b/components/esp_event/component.mk index c59eccc41a..828cb6abac 100644 --- a/components/esp_event/component.mk +++ b/components/esp_event/component.mk @@ -4,6 +4,7 @@ COMPONENT_ADD_INCLUDEDIRS := include COMPONENT_PRIV_INCLUDEDIRS := private_include COMPONENT_SRCDIRS := . +COMPONENT_ADD_LDFRAGMENTS := linker.lf ifdef CONFIG_ESP_EVENT_LOOP_PROFILING PROFILING_ENABLED := 1 From 243e0b229fc6aab97101cd6bac6471283e6b6b56 Mon Sep 17 00:00:00 2001 From: David Cermak Date: Fri, 31 May 2019 15:37:51 +0200 Subject: [PATCH 015/486] tcp_transport: fix minor memory leak found by static analyzer Closes https://github.com/espressif/esp-idf/issues/3602 --- components/tcp_transport/transport_ws.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/components/tcp_transport/transport_ws.c b/components/tcp_transport/transport_ws.c index e31149e178..43f0bdd360 100644 --- a/components/tcp_transport/transport_ws.c +++ b/components/tcp_transport/transport_ws.c @@ -296,7 +296,10 @@ esp_transport_handle_t esp_transport_ws_init(esp_transport_handle_t parent_handl ws->parent = parent_handle; ws->path = strdup("/"); - ESP_TRANSPORT_MEM_CHECK(TAG, ws->path, return NULL); + ESP_TRANSPORT_MEM_CHECK(TAG, ws->path, { + free(ws); + return NULL; + }); ws->buffer = malloc(DEFAULT_WS_BUFFER); ESP_TRANSPORT_MEM_CHECK(TAG, ws->buffer, { free(ws->path); From bd537083d92fc8881d62a48eeacb178bdfb4bdc7 Mon Sep 17 00:00:00 2001 From: David Cermak Date: Fri, 31 May 2019 11:23:02 +0200 Subject: [PATCH 016/486] esp_event: fix possible malloc free issues found by static analyzer --- components/esp_event/esp_event.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/components/esp_event/esp_event.c b/components/esp_event/esp_event.c index b51a74cb50..99a3cf70bb 100644 --- a/components/esp_event/esp_event.c +++ b/components/esp_event/esp_event.c @@ -212,7 +212,7 @@ static esp_err_t base_node_add_handler(esp_event_base_node_t* base_node, int32_t id_node = (esp_event_id_node_t*) calloc(1, sizeof(*id_node)); if (!id_node) { - ESP_LOGI(TAG, "alloc for new id node failed"); + ESP_LOGE(TAG, "alloc for new id node failed"); return ESP_ERR_NO_MEM; } @@ -229,6 +229,8 @@ static esp_err_t base_node_add_handler(esp_event_base_node_t* base_node, int32_t else { SLIST_INSERT_AFTER(last_id_node, id_node, next); } + } else { + free(id_node); } return err; @@ -280,6 +282,8 @@ static esp_err_t loop_node_add_handler(esp_event_loop_node_t* loop_node, esp_eve else { SLIST_INSERT_AFTER(last_base_node, base_node, next); } + } else { + free(base_node); } return err; @@ -414,7 +418,7 @@ esp_err_t esp_event_loop_create(const esp_event_loop_args_t* event_loop_args, es loop = calloc(1, sizeof(*loop)); if (loop == NULL) { ESP_LOGE(TAG, "alloc for event loop failed"); - goto on_err; + return err; } loop->queue = xQueueCreate(event_loop_args->queue_size , sizeof(esp_event_post_instance_t)); @@ -692,6 +696,8 @@ esp_err_t esp_event_handler_register_with(esp_event_loop_handle_t event_loop, es else { SLIST_INSERT_AFTER(last_loop_node, loop_node, next); } + } else { + free(loop_node); } } else { From c34de4cba658e8331f8a3ab2f466190c7640595b Mon Sep 17 00:00:00 2001 From: David Cermak Date: Wed, 29 May 2019 17:07:39 +0200 Subject: [PATCH 017/486] mdns: fix static analysis warnings --- components/mdns/mdns.c | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/components/mdns/mdns.c b/components/mdns/mdns.c index e88672f82b..ad3dfb49e5 100644 --- a/components/mdns/mdns.c +++ b/components/mdns/mdns.c @@ -1312,6 +1312,7 @@ static mdns_tx_packet_t * _mdns_create_probe_packet(tcpip_adapter_if_t tcpip_if, q->domain = MDNS_DEFAULT_DOMAIN; if (!q->host || _mdns_question_exists(q, packet->questions)) { free(q); + continue; } else { queueToEnd(mdns_out_question_t, packet->questions, q); } @@ -2801,7 +2802,7 @@ void mdns_parse_packet(mdns_rx_packet_t * packet) col = 1; } else if (!clas) { col = -1; - } else { + } else if (service) { // only detect srv collision if service existed col = _mdns_check_srv_collision(service->service, priority, weight, port, name->host, name->domain); } if (col && (parsed_packet->probe || parsed_packet->authoritative)) { @@ -3831,23 +3832,25 @@ static void _mdns_execute_action(mdns_action_t * action) break; case ACTION_SERVICE_DEL: a = _mdns_server->services; - if (_mdns_server->services == action->data.srv_del.service) { - _mdns_server->services = a->next; - _mdns_send_bye(&a, 1, false); - _mdns_remove_scheduled_service_packets(a->service); - _mdns_free_service(a->service); - free(a); - } else { - while (a->next && a->next != action->data.srv_del.service) { - a = a->next; - } - if (a->next == action->data.srv_del.service) { - mdns_srv_item_t * b = a->next; - a->next = a->next->next; - _mdns_send_bye(&b, 1, false); - _mdns_remove_scheduled_service_packets(b->service); - _mdns_free_service(b->service); - free(b); + if (action->data.srv_del.service) { + if (_mdns_server->services == action->data.srv_del.service) { + _mdns_server->services = a->next; + _mdns_send_bye(&a, 1, false); + _mdns_remove_scheduled_service_packets(a->service); + _mdns_free_service(a->service); + free(a); + } else { + while (a->next && a->next != action->data.srv_del.service) { + a = a->next; + } + if (a->next == action->data.srv_del.service) { + mdns_srv_item_t * b = a->next; + a->next = a->next->next; + _mdns_send_bye(&b, 1, false); + _mdns_remove_scheduled_service_packets(b->service); + _mdns_free_service(b->service); + free(b); + } } } From e08b787d790bf43fe733e94d5e2cace54ea91ed1 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 11 Jun 2019 10:15:42 +1000 Subject: [PATCH 018/486] ci: Adjust the test_confserver timeout to 2 seconds On a VM, it seems like 500ms is sometimes a very short time... --- tools/kconfig_new/test/test_confserver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/kconfig_new/test/test_confserver.py b/tools/kconfig_new/test/test_confserver.py index d5da31913c..20fce7e031 100755 --- a/tools/kconfig_new/test/test_confserver.py +++ b/tools/kconfig_new/test/test_confserver.py @@ -51,7 +51,7 @@ def main(): cmdline = "../confserver.py --kconfig Kconfig --config %s" % temp_sdkconfig_path print("Running: %s" % cmdline) - p = pexpect.spawn(cmdline, timeout=0.5) + p = pexpect.spawn(cmdline, timeout=2) p.logfile = args.logfile p.setecho(False) From 63bd57c1d72b1c1c4f0742eeccf36d383df2da0f Mon Sep 17 00:00:00 2001 From: Renz Christian Bagaporo Date: Mon, 27 May 2019 11:07:54 +0800 Subject: [PATCH 019/486] partition_table: implement Python API for parttool Closes https://github.com/espressif/esp-idf/issues/1494 --- components/partition_table/Makefile.projbuild | 20 +- .../partition_table/gen_empty_partition.py | 64 ++++ components/partition_table/parttool.py | 359 +++++++++--------- .../partition_table/project_include.cmake | 2 +- .../gen_esp32part_tests.py | 28 +- components/spiffs/Makefile.projbuild | 8 +- docs/en/api-guides/partition-tables.rst | 96 +++++ tools/ci/executable-list.txt | 1 + 8 files changed, 373 insertions(+), 205 deletions(-) create mode 100644 components/partition_table/gen_empty_partition.py diff --git a/components/partition_table/Makefile.projbuild b/components/partition_table/Makefile.projbuild index 1785ad811e..353a7655d6 100644 --- a/components/partition_table/Makefile.projbuild +++ b/components/partition_table/Makefile.projbuild @@ -63,16 +63,16 @@ $(PARTITION_TABLE_BIN_UNSIGNED): $(PARTITION_TABLE_CSV_PATH) $(SDKCONFIG_MAKEFIL all_binaries: $(PARTITION_TABLE_BIN) partition_table_get_info check_table_contents partition_table_get_info: $(PARTITION_TABLE_BIN) - $(eval PHY_DATA_OFFSET:=$(shell $(GET_PART_INFO) --partition-type data --partition-subtype phy \ - --partition-table-file $(PARTITION_TABLE_BIN) get_partition_info --info offset)) - $(eval APP_OFFSET:=$(shell $(GET_PART_INFO) --partition-boot-default \ - --partition-table-file $(PARTITION_TABLE_BIN) get_partition_info --info offset)) - $(eval OTA_DATA_OFFSET:=$(shell $(GET_PART_INFO) --partition-type data --partition-subtype ota \ - --partition-table-file $(PARTITION_TABLE_BIN) get_partition_info --info offset)) - $(eval OTA_DATA_SIZE:=$(shell $(GET_PART_INFO) --partition-type data --partition-subtype ota \ - --partition-table-file $(PARTITION_TABLE_BIN) get_partition_info --info size)) - $(eval FACTORY_OFFSET:=$(shell $(GET_PART_INFO) --partition-type app --partition-subtype factory \ - --partition-table-file $(PARTITION_TABLE_BIN) get_partition_info --info offset)) + $(eval PHY_DATA_OFFSET:=$(shell $(GET_PART_INFO) --partition-table-file $(PARTITION_TABLE_BIN) \ + get_partition_info --partition-type data --partition-subtype phy --info offset)) + $(eval APP_OFFSET:=$(shell $(GET_PART_INFO) --partition-table-file $(PARTITION_TABLE_BIN) \ + get_partition_info --partition-boot-default --info offset)) + $(eval OTA_DATA_OFFSET:=$(shell $(GET_PART_INFO) --partition-table-file $(PARTITION_TABLE_BIN) \ + get_partition_info --partition-type data --partition-subtype ota --info offset)) + $(eval OTA_DATA_SIZE:=$(shell $(GET_PART_INFO) --partition-table-file $(PARTITION_TABLE_BIN) \ + get_partition_info --partition-type data --partition-subtype ota --info size)) + $(eval FACTORY_OFFSET:=$(shell $(GET_PART_INFO) --partition-table-file $(PARTITION_TABLE_BIN) \ + get_partition_info --partition-type app --partition-subtype factory --info offset)) export APP_OFFSET export PHY_DATA_OFFSET diff --git a/components/partition_table/gen_empty_partition.py b/components/partition_table/gen_empty_partition.py new file mode 100644 index 0000000000..f65f74d706 --- /dev/null +++ b/components/partition_table/gen_empty_partition.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python +# +# generates an empty binary file +# +# This tool generates an empty binary file of the required size. +# +# Copyright 2018 Espressif Systems (Shanghai) PTE LTD +# +# 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. +from __future__ import print_function, division +from __future__ import unicode_literals +import argparse +import sys + +__version__ = '1.0' + +quiet = False + + +def generate_blanked_file(size, output_path): + output = b"\xFF" * size + try: + stdout_binary = sys.stdout.buffer # Python 3 + except AttributeError: + stdout_binary = sys.stdout + with stdout_binary if output_path == '-' else open(output_path, 'wb') as f: + f.write(output) + + +def main(): + parser = argparse.ArgumentParser(description='Generates an empty binary file of the required size.') + parser.add_argument('size', help='Size of generated the file', type=str) + + parser.add_argument('output', help='Path for binary file.', nargs='?', default='-') + args = parser.parse_args() + + size = int(args.size, 0) + if size > 0: + generate_blanked_file(size, args.output) + return 0 + + +class InputError(RuntimeError): + def __init__(self, e): + super(InputError, self).__init__(e) + + +if __name__ == '__main__': + try: + r = main() + sys.exit(r) + except InputError as e: + print(e, file=sys.stderr) + sys.exit(2) diff --git a/components/partition_table/parttool.py b/components/partition_table/parttool.py index 14fcdf4fe7..f2c005a10c 100755 --- a/components/partition_table/parttool.py +++ b/components/partition_table/parttool.py @@ -24,193 +24,165 @@ import subprocess import tempfile import gen_esp32part as gen -__version__ = '1.0' -IDF_COMPONENTS_PATH = os.path.expandvars(os.path.join("$IDF_PATH", "components")) +__version__ = '2.0' + +COMPONENTS_PATH = os.path.expandvars(os.path.join("$IDF_PATH", "components")) +ESPTOOL_PY = os.path.join(COMPONENTS_PATH, "esptool_py", "esptool", "esptool.py") + +PARTITION_TABLE_OFFSET = 0x8000 -ESPTOOL_PY = os.path.join(IDF_COMPONENTS_PATH, "esptool_py", "esptool", "esptool.py") quiet = False def status(msg): - """ Print status message to stderr """ if not quiet: print(msg) -def _invoke_esptool(esptool_args, args): - m_esptool_args = [sys.executable, ESPTOOL_PY] +class _PartitionId(): - if args.port != "": - m_esptool_args.extend(["--port", args.port]) - - m_esptool_args.extend(esptool_args) - - if quiet: - with open(os.devnull, "w") as fnull: - subprocess.check_call(m_esptool_args, stdout=fnull, stderr=fnull) - else: - subprocess.check_call(m_esptool_args) + def __init__(self, name=None, type=None, subtype=None): + self.name = name + self.type = type + self.subtype = subtype -def _get_partition_table(args): - partition_table = None +class PartitionName(_PartitionId): - gen.offset_part_table = int(args.partition_table_offset, 0) - - if args.partition_table_file: - status("Reading partition table from partition table file...") - - try: - with open(args.partition_table_file, "rb") as partition_table_file: - partition_table = gen.PartitionTable.from_binary(partition_table_file.read()) - status("Partition table read from binary file {}".format(partition_table_file.name)) - except (gen.InputError, TypeError): - with open(args.partition_table_file, "r") as partition_table_file: - partition_table_file.seek(0) - partition_table = gen.PartitionTable.from_csv(partition_table_file.read()) - status("Partition table read from CSV file {}".format(partition_table_file.name)) - else: - port_info = (" on port " + args.port if args.port else "") - status("Reading partition table from device{}...".format(port_info)) - - f_name = None - with tempfile.NamedTemporaryFile(delete=False) as f: - f_name = f.name - - try: - invoke_args = ["read_flash", str(gen.offset_part_table), str(gen.MAX_PARTITION_LENGTH), f_name] - _invoke_esptool(invoke_args, args) - with open(f_name, "rb") as f: - partition_table = gen.PartitionTable.from_binary(f.read()) - status("Partition table read from device" + port_info) - finally: - os.unlink(f_name) - - return partition_table + def __init__(self, name): + _PartitionId.__init__(self, name=name) -def _get_partition(args): - partition_table = _get_partition_table(args) +class PartitionType(_PartitionId): - partition = None - - if args.partition_name: - partition = partition_table.find_by_name(args.partition_name) - elif args.partition_type and args.partition_subtype: - partition = partition_table.find_by_type(args.partition_type, args.partition_subtype) - elif args.partition_boot_default: - search = ["factory"] + ["ota_{}".format(d) for d in range(16)] - for subtype in search: - partition = partition_table.find_by_type("app", subtype) - if partition is not None: - break - else: - raise RuntimeError("Invalid partition selection arguments. Specify --partition-name OR \ - --partition-type and --partition-subtype OR --partition--boot-default.") - - if partition: - status("Found partition {}".format(str(partition))) - - return partition + def __init__(self, type, subtype): + _PartitionId.__init__(self, type=type, subtype=subtype) -def _get_and_check_partition(args): - partition = None - - partition = _get_partition(args) - - if not partition: - raise RuntimeError("Unable to find specified partition.") - - return partition +PARTITION_BOOT_DEFAULT = _PartitionId() -def write_partition(args): - erase_partition(args) +class ParttoolTarget(): - partition = _get_and_check_partition(args) + def __init__(self, port=None, partition_table_offset=PARTITION_TABLE_OFFSET, partition_table_file=None): + self.port = port - status("Checking input file size...") + gen.offset_part_table = partition_table_offset - with open(args.input, "rb") as input_file: - content_len = len(input_file.read()) - - if content_len != partition.size: - status("File size (0x{:x}) does not match partition size (0x{:x})".format(content_len, partition.size)) + if partition_table_file: + try: + with open(partition_table_file, "rb") as f: + partition_table = gen.PartitionTable.from_binary(f.read()) + except (gen.InputError, IOError, TypeError): + with open(partition_table_file, "r") as f: + f.seek(0) + partition_table = gen.PartitionTable.from_csv(f.read()) else: - status("File size matches partition size (0x{:x})".format(partition.size)) - - _invoke_esptool(["write_flash", str(partition.offset), args.input], args) - - status("Written contents of file '{}' to device at offset 0x{:x}".format(args.input, partition.offset)) - - -def read_partition(args): - partition = _get_and_check_partition(args) - _invoke_esptool(["read_flash", str(partition.offset), str(partition.size), args.output], args) - status("Read partition contents from device at offset 0x{:x} to file '{}'".format(partition.offset, args.output)) - - -def erase_partition(args): - partition = _get_and_check_partition(args) - _invoke_esptool(["erase_region", str(partition.offset), str(partition.size)], args) - status("Erased partition at offset 0x{:x} on device".format(partition.offset)) - - -def get_partition_info(args): - partition = None - - if args.table: - partition_table = _get_partition_table(args) - - if args.table.endswith(".csv"): - partition_table = partition_table.to_csv() - else: - partition_table = partition_table.to_binary() - - with open(args.table, "wb") as table_file: - table_file.write(partition_table) - status("Partition table written to " + table_file.name) - else: - partition = _get_partition(args) - - if partition: - info_dict = { - "offset": '0x{:x}'.format(partition.offset), - "size": '0x{:x}'.format(partition.size) - } - - infos = [] + temp_file = tempfile.NamedTemporaryFile(delete=False) + temp_file.close() try: - for info in args.info: - infos += [info_dict[info]] - except KeyError: - raise RuntimeError("Request for unknown partition info {}".format(info)) + self._call_esptool(["read_flash", str(partition_table_offset), str(gen.MAX_PARTITION_LENGTH), temp_file.name]) + with open(temp_file.name, "rb") as f: + partition_table = gen.PartitionTable.from_binary(f.read()) + finally: + os.unlink(temp_file.name) - status("Requested partition information [{}]:".format(", ".join(args.info))) - print(" ".join(infos)) - else: - status("Partition not found") + self.partition_table = partition_table + + def _call_esptool(self, args, out=None): + esptool_args = [sys.executable, ESPTOOL_PY] + + if self.port: + esptool_args += ["--port", self.port] + + esptool_args += args + + with open(os.devnull, "w") as null_file: + subprocess.check_call(esptool_args, stdout=null_file, stderr=null_file) + + def get_partition_info(self, partition_id): + partition = None + + if partition_id.name: + partition = self.partition_table.find_by_name(partition_id.name) + elif partition_id.type and partition_id.subtype: + partition = self.partition_table.find_by_type(partition_id.type, partition_id.subtype) + else: # default boot partition + search = ["factory"] + ["ota_{}".format(d) for d in range(16)] + for subtype in search: + partition = self.partition_table.find_by_type("app", subtype) + if partition: + break + + if not partition: + raise Exception("Partition does not exist") + + return partition + + def erase_partition(self, partition_id): + partition = self.get_partition_info(partition_id) + self._call_esptool(["erase_region", str(partition.offset), str(partition.size)]) + + def read_partition(self, partition_id, output): + partition = self.get_partition_info(partition_id) + self._call_esptool(["read_flash", str(partition.offset), str(partition.size), output]) + + def write_partition(self, partition_id, input): + self.erase_partition(partition_id) + + partition = self.get_partition_info(partition_id) + + with open(input, "rb") as input_file: + content_len = len(input_file.read()) + + if content_len > partition.size: + raise Exception("Input file size exceeds partition size") + + self._call_esptool(["write_flash", str(partition.offset), input]) -def generate_blank_partition_file(args): - output = None - stdout_binary = None +def _write_partition(target, partition_id, input): + target.write_partition(partition_id, input) + partition = target.get_partition_info(partition_id) + status("Written contents of file '{}' at offset 0x{:x}".format(input, partition.offset)) - partition = _get_and_check_partition(args) - output = b"\xFF" * partition.size + +def _read_partition(target, partition_id, output): + target.read_partition(partition_id, output) + partition = target.get_partition_info(partition_id) + status("Read partition '{}' contents from device at offset 0x{:x} to file '{}'" + .format(partition.name, partition.offset, output)) + + +def _erase_partition(target, partition_id): + target.erase_partition(partition_id) + partition = target.get_partition_info(partition_id) + status("Erased partition '{}' at offset 0x{:x}".format(partition.name, partition.offset)) + + +def _get_partition_info(target, partition_id, info): + try: + partition = target.get_partition_info(partition_id) + except Exception: + return + + info_dict = { + "offset": '0x{:x}'.format(partition.offset), + "size": '0x{:x}'.format(partition.size) + } + + infos = [] try: - stdout_binary = sys.stdout.buffer # Python 3 - except AttributeError: - stdout_binary = sys.stdout + for i in info: + infos += [info_dict[i]] + except KeyError: + raise RuntimeError("Request for unknown partition info {}".format(i)) - with stdout_binary if args.output == "" else open(args.output, 'wb') as f: - f.write(output) - status("Blank partition file '{}' generated".format(args.output)) + print(" ".join(infos)) def main(): @@ -220,48 +192,45 @@ def main(): parser.add_argument("--quiet", "-q", help="suppress stderr messages", action="store_true") - # There are two possible sources for the partition table: a device attached to the host - # or a partition table CSV/binary file. These sources are mutually exclusive. - partition_table_info_source_args = parser.add_mutually_exclusive_group() + # By default the device attached to the specified port is queried for the partition table. If a partition table file + # is specified, that is used instead. + parser.add_argument("--port", "-p", help="port where the target device of the command is connected to; the partition table is sourced from this device \ + when the partition table file is not defined") - partition_table_info_source_args.add_argument("--port", "-p", help="port where the device to read the partition table from is attached", default="") - partition_table_info_source_args.add_argument("--partition-table-file", "-f", help="file (CSV/binary) to read the partition table from") + parser.add_argument("--partition-table-offset", "-o", help="offset to read the partition table from", type=str) + parser.add_argument("--partition-table-file", "-f", help="file (CSV/binary) to read the partition table from; \ + overrides device attached to specified port as the partition table source when defined") - parser.add_argument("--partition-table-offset", "-o", help="offset to read the partition table from", default="0x8000") + partition_selection_parser = argparse.ArgumentParser(add_help=False) # Specify what partition to perform the operation on. This can either be specified using the # partition name or the first partition that matches the specified type/subtype - partition_selection_args = parser.add_mutually_exclusive_group() + partition_selection_args = partition_selection_parser.add_mutually_exclusive_group() partition_selection_args.add_argument("--partition-name", "-n", help="name of the partition") partition_selection_args.add_argument("--partition-type", "-t", help="type of the partition") partition_selection_args.add_argument('--partition-boot-default', "-d", help='select the default boot partition \ using the same fallback logic as the IDF bootloader', action="store_true") - parser.add_argument("--partition-subtype", "-s", help="subtype of the partition") + partition_selection_parser.add_argument("--partition-subtype", "-s", help="subtype of the partition") subparsers = parser.add_subparsers(dest="operation", help="run parttool -h for additional help") # Specify the supported operations - read_part_subparser = subparsers.add_parser("read_partition", help="read partition from device and dump contents into a file") + read_part_subparser = subparsers.add_parser("read_partition", help="read partition from device and dump contents into a file", + parents=[partition_selection_parser]) read_part_subparser.add_argument("--output", help="file to dump the read partition contents to") - write_part_subparser = subparsers.add_parser("write_partition", help="write contents of a binary file to partition on device") + write_part_subparser = subparsers.add_parser("write_partition", help="write contents of a binary file to partition on device", + parents=[partition_selection_parser]) write_part_subparser.add_argument("--input", help="file whose contents are to be written to the partition offset") - subparsers.add_parser("erase_partition", help="erase the contents of a partition on the device") + subparsers.add_parser("erase_partition", help="erase the contents of a partition on the device", parents=[partition_selection_parser]) - print_partition_info_subparser = subparsers.add_parser("get_partition_info", help="get partition information") - print_partition_info_subparser_info_type = print_partition_info_subparser.add_mutually_exclusive_group() - print_partition_info_subparser_info_type.add_argument("--info", help="type of partition information to get", nargs="+") - print_partition_info_subparser_info_type.add_argument("--table", help="dump the partition table to a file") - - generate_blank_subparser = subparsers.add_parser("generate_blank_partition_file", help="generate a blank (all 0xFF) partition file of \ - the specified partition that can be flashed to the device") - generate_blank_subparser.add_argument("--output", help="blank partition file filename") + print_partition_info_subparser = subparsers.add_parser("get_partition_info", help="get partition information", parents=[partition_selection_parser]) + print_partition_info_subparser.add_argument("--info", help="type of partition information to get", nargs="+") args = parser.parse_args() - quiet = args.quiet # No operation specified, display help and exit @@ -270,17 +239,55 @@ def main(): parser.print_help() sys.exit(1) - # Else execute the operation - operation_func = globals()[args.operation] + # Prepare the partition to perform operation on + if args.partition_name: + partition_id = PartitionName(args.partition_name) + elif args.partition_type: + if not args.partition_subtype: + raise RuntimeError("--partition-subtype should be defined when --partition-type is defined") + partition_id = PartitionType(args.partition_type, args.partition_subtype) + elif args.partition_boot_default: + partition_id = PARTITION_BOOT_DEFAULT + else: + raise RuntimeError("Partition to operate on should be defined using --partition-name OR \ + partition-type,--partition-subtype OR partition-boot-default") + + # Prepare the device to perform operation on + target_args = {} + + if args.port: + target_args["port"] = args.port + + if args.partition_table_file: + target_args["partition_table_file"] = args.partition_table_file + + if args.partition_table_offset: + target_args["partition_table_offset"] = int(args.partition_table_offset, 0) + + target = ParttoolTarget(**target_args) + + # Create the operation table and execute the operation + common_args = {'target':target, 'partition_id':partition_id} + parttool_ops = { + 'erase_partition':(_erase_partition, []), + 'read_partition':(_read_partition, ["output"]), + 'write_partition':(_write_partition, ["input"]), + 'get_partition_info':(_get_partition_info, ["info"]) + } + + (op, op_args) = parttool_ops[args.operation] + + for op_arg in op_args: + common_args.update({op_arg:vars(args)[op_arg]}) if quiet: # If exceptions occur, suppress and exit quietly try: - operation_func(args) + op(**common_args) except Exception: sys.exit(2) else: - operation_func(args) + op(**common_args) if __name__ == '__main__': diff --git a/components/partition_table/project_include.cmake b/components/partition_table/project_include.cmake index d8e31c7d1d..9f0dc93854 100644 --- a/components/partition_table/project_include.cmake +++ b/components/partition_table/project_include.cmake @@ -39,7 +39,7 @@ function(partition_table_get_partition_info result get_part_info_args part_info) ${idf_path}/components/partition_table/parttool.py -q --partition-table-offset ${PARTITION_TABLE_OFFSET} --partition-table-file ${PARTITION_CSV_PATH} - ${get_part_info_args} get_partition_info --info ${part_info} + get_partition_info ${get_part_info_args} --info ${part_info} OUTPUT_VARIABLE info RESULT_VARIABLE exit_code OUTPUT_STRIP_TRAILING_WHITESPACE) diff --git a/components/partition_table/test_gen_esp32part_host/gen_esp32part_tests.py b/components/partition_table/test_gen_esp32part_host/gen_esp32part_tests.py index 989d4c4758..17f16c5b46 100755 --- a/components/partition_table/test_gen_esp32part_host/gen_esp32part_tests.py +++ b/components/partition_table/test_gen_esp32part_host/gen_esp32part_tests.py @@ -403,13 +403,13 @@ app,app, factory, 32K, 1M class PartToolTests(Py23TestCase): - def _run_parttool(self, csvcontents, args, info): + def _run_parttool(self, csvcontents, args): csvpath = tempfile.mktemp() with open(csvpath, "w") as f: f.write(csvcontents) try: - output = subprocess.check_output([sys.executable, "../parttool.py"] + args.split(" ") - + ["--partition-table-file", csvpath, "get_partition_info", "--info", info], + output = subprocess.check_output([sys.executable, "../parttool.py", "-q", "--partition-table-file", + csvpath, "get_partition_info"] + args, stderr=subprocess.STDOUT) self.assertNotIn(b"WARNING", output) m = re.search(b"0x[0-9a-fA-F]+", output) @@ -425,17 +425,17 @@ phy_init, data, phy, 0xf000, 0x1000 factory, app, factory, 0x10000, 1M """ - def rpt(args, info): - return self._run_parttool(csv, args, info) + def rpt(args): + return self._run_parttool(csv, args) self.assertEqual( - rpt("--partition-type=data --partition-subtype=nvs -q", "offset"), b"0x9000") + rpt(["--partition-type", "data", "--partition-subtype", "nvs", "--info", "offset"]), b"0x9000") self.assertEqual( - rpt("--partition-type=data --partition-subtype=nvs -q", "size"), b"0x4000") + rpt(["--partition-type", "data", "--partition-subtype", "nvs", "--info", "size"]), b"0x4000") self.assertEqual( - rpt("--partition-name=otadata -q", "offset"), b"0xd000") + rpt(["--partition-name", "otadata", "--info", "offset"]), b"0xd000") self.assertEqual( - rpt("--partition-boot-default -q", "offset"), b"0x10000") + rpt(["--partition-boot-default", "--info", "offset"]), b"0x10000") def test_fallback(self): csv = """ @@ -446,16 +446,16 @@ ota_0, app, ota_0, 0x30000, 1M ota_1, app, ota_1, , 1M """ - def rpt(args, info): - return self._run_parttool(csv, args, info) + def rpt(args): + return self._run_parttool(csv, args) self.assertEqual( - rpt("--partition-type=app --partition-subtype=ota_1 -q", "offset"), b"0x130000") + rpt(["--partition-type", "app", "--partition-subtype", "ota_1", "--info", "offset"]), b"0x130000") self.assertEqual( - rpt("--partition-boot-default -q", "offset"), b"0x30000") # ota_0 + rpt(["--partition-boot-default", "--info", "offset"]), b"0x30000") # ota_0 csv_mod = csv.replace("ota_0", "ota_2") self.assertEqual( - self._run_parttool(csv_mod, "--partition-boot-default -q", "offset"), + self._run_parttool(csv_mod, ["--partition-boot-default", "--info", "offset"]), b"0x130000") # now default is ota_1 diff --git a/components/spiffs/Makefile.projbuild b/components/spiffs/Makefile.projbuild index 2fab90b172..13b2223a9a 100644 --- a/components/spiffs/Makefile.projbuild +++ b/components/spiffs/Makefile.projbuild @@ -21,9 +21,9 @@ define spiffs_create_partition_image $(1)_bin: $(PARTITION_TABLE_BIN) | check_python_dependencies - partition_size=`$(GET_PART_INFO) --partition-name $(1) \ + partition_size=`$(GET_PART_INFO) \ --partition-table-file $(PARTITION_TABLE_BIN) \ - get_partition_info --info size`; \ + get_partition_info --partition-name $(1) --info size`; \ $(PYTHON) $(SPIFFSGEN_PY) $$$$partition_size $(2) $(BUILD_DIR_BASE)/$(1).bin \ --page-size=$(CONFIG_SPIFFS_PAGE_SIZE) \ --obj-name-len=$(CONFIG_SPIFFS_OBJ_NAME_LEN) \ @@ -41,5 +41,5 @@ endif endef ESPTOOL_ALL_FLASH_ARGS += $(foreach partition,$(SPIFFSGEN_FLASH_IN_PROJECT), \ -$(shell $(GET_PART_INFO) --partition-name $(partition) \ ---partition-table-file $(PARTITION_TABLE_BIN) get_partition_info --info offset) $(BUILD_DIR_BASE)/$(partition).bin) \ No newline at end of file +$(shell $(GET_PART_INFO) --partition-table-file $(PARTITION_TABLE_BIN) \ +get_partition_info --partition-name $(partition) --info offset) $(BUILD_DIR_BASE)/$(partition).bin) \ No newline at end of file diff --git a/docs/en/api-guides/partition-tables.rst b/docs/en/api-guides/partition-tables.rst index 502c7118c9..000a9b3616 100644 --- a/docs/en/api-guides/partition-tables.rst +++ b/docs/en/api-guides/partition-tables.rst @@ -169,4 +169,100 @@ A manual flashing command is also printed as part of ``make partition_table``. Note that updating the partition table doesn't erase data that may have been stored according to the old partition table. You can use ``make erase_flash`` (or ``esptool.py erase_flash``) to erase the entire flash contents. +Partition Tool (parttool.py) +---------------------------- + +The component `partition_table` provides a tool :component_file:`parttool.py` for performing partition-related operations on a target device. The following operations can be performed using the tool: + + - reading a partition and saving the contents to a file (read_partition) + - writing the contents of a file to a partition (write_partition) + - erasing a partition (erase_partition) + - retrieving info such as offset and size of a given partition (get_partition_info) + +The tool can either be imported and used from another Python script or invoked from shell script for users wanting to perform operation programmatically. This is facilitated by the tool's Python API +and command-line interface, respectively. + +Python API +~~~~~~~~~~~ + +Before anything else, make sure that the `parttool` module is imported. + +.. code-block:: python + + import sys + import os + + idf_path = os.environ["IDF_PATH"] # get value of IDF_PATH from environment + parttool_dir = os.path.join(idf_path, "components", "partition_table") # parttool.py lives in $IDF_PATH/components/partition_table + + sys.path.append(parttool_dir) # this enables Python to find parttool module + from parttool import * # import all names inside parttool module + +The starting point for using the tool's Python API to do is create a `ParttoolTarget` object: + +.. code-block:: python + + # Create a partool.py target device connected on serial port /dev/ttyUSB1 + target = ParttoolTarget("/dev/ttyUSB1") + +The created object can now be used to perform operations on the target device: + +.. code-block:: python + + # Erase partition with name 'storage' + target.erase_partition(PartitionName("storage")) + + # Read partition with type 'data' and subtype 'spiffs' and save to file 'spiffs.bin' + target.read_partition(PartitionType("data", "spiffs"), "spiffs.bin") + + # Write to partition 'factory' the contents of a file named 'factory.bin' + target.write_partition(PartitionName("factory"), "factory.bin") + + # Print the size of default boot partition + storage = target.get_partition_info(PARTITION_BOOT_DEFAULT) + print(storage.size) + +The partition to operate on is specified using `PartitionName` or `PartitionType` or PARTITION_BOOT_DEFAULT. As the name implies, these can be used to refer +to partitions of a particular name, type-subtype combination, or the default boot partition. + +More information on the Python API is available in the docstrings for the tool. + +Command-line Interface +~~~~~~~~~~~~~~~~~~~~~~ + +The command-line interface of `parttool.py` has the following structure: + +.. code-block:: bash + + parttool.py [command-args] [subcommand] [subcommand-args] + + - command-args - These are arguments that are needed for executing the main command (parttool.py), mostly pertaining to the target device + - subcommand - This is the operation to be performed + - subcommand-args - These are arguments that are specific to the chosen operation + +.. code-block:: bash + + # Erase partition with name 'storage' + parttool.py --port "/dev/ttyUSB1" erase_partition --partition-name=storage + + # Read partition with type 'data' and subtype 'spiffs' and save to file 'spiffs.bin' + parttool.py --port "/dev/ttyUSB1" read_partition --partition-type=data --partition-subtype=spiffs "spiffs.bin" + + # Write to partition 'factory' the contents of a file named 'factory.bin' + parttool.py --port "/dev/ttyUSB1" write_partition --partition-name=factory "factory.bin" + + # Print the size of default boot partition + parttool.py --port "/dev/ttyUSB1" get_partition_info --partition-boot-default --info size + +More information can be obtained by specifying `--help` as argument: + +.. code-block:: bash + + # Display possible subcommands and show main command argument descriptions + parttool.py --help + + # Show descriptions for specific subcommand arguments + parttool.py [subcommand] --help + + .. _secure boot: security/secure-boot.rst diff --git a/tools/ci/executable-list.txt b/tools/ci/executable-list.txt index 5cbf7b3a2b..0dc1c7fd15 100644 --- a/tools/ci/executable-list.txt +++ b/tools/ci/executable-list.txt @@ -7,6 +7,7 @@ components/espcoredump/test/test_espcoredump.sh components/heap/test_multi_heap_host/test_all_configs.sh components/idf_test/unit_test/TestCaseScript/IDFUnitTest/__init__.py components/nvs_flash/nvs_partition_generator/nvs_partition_gen.py +components/partition_table/gen_empty_partition.py components/partition_table/gen_esp32part.py components/partition_table/parttool.py components/partition_table/test_gen_esp32part_host/gen_esp32part_tests.py From 1de627e68aee139255689c6ce16d8d26d9c9bd4b Mon Sep 17 00:00:00 2001 From: Renz Christian Bagaporo Date: Mon, 27 May 2019 11:08:28 +0800 Subject: [PATCH 020/486] app_update: implement Python API for otatool --- components/app_update/CMakeLists.txt | 6 +- components/app_update/Makefile.projbuild | 3 +- components/app_update/otatool.py | 327 ++++++++++++----------- docs/en/api-reference/system/ota.rst | 98 +++++++ 4 files changed, 277 insertions(+), 157 deletions(-) diff --git a/components/app_update/CMakeLists.txt b/components/app_update/CMakeLists.txt index 2c705d5091..e869c188fb 100644 --- a/components/app_update/CMakeLists.txt +++ b/components/app_update/CMakeLists.txt @@ -33,10 +33,8 @@ if(NOT BOOTLOADER_BUILD) idf_build_get_property(idf_path IDF_PATH) idf_build_get_property(python PYTHON) add_custom_command(OUTPUT ${blank_otadata_file} - COMMAND ${python} ${idf_path}/components/partition_table/parttool.py - --partition-type data --partition-subtype ota -q - --partition-table-file ${PARTITION_CSV_PATH} generate_blank_partition_file - --output ${blank_otadata_file}) + COMMAND ${python} ${idf_path}/components/partition_table/gen_empty_partition.py + ${otadata_size} ${blank_otadata_file}) add_custom_target(blank_ota_data ALL DEPENDS ${blank_otadata_file}) add_dependencies(app blank_ota_data) diff --git a/components/app_update/Makefile.projbuild b/components/app_update/Makefile.projbuild index 435d7535ac..a20856a6ef 100644 --- a/components/app_update/Makefile.projbuild +++ b/components/app_update/Makefile.projbuild @@ -17,8 +17,7 @@ endif $(BLANK_OTA_DATA_FILE): partition_table_get_info $(PARTITION_TABLE_CSV_PATH) | check_python_dependencies $(shell if [ "$(OTA_DATA_OFFSET)" != "" ] && [ "$(OTA_DATA_SIZE)" != "" ]; then \ - $(PARTTOOL_PY) --partition-type data --partition-subtype ota --partition-table-file $(PARTITION_TABLE_CSV_PATH) \ - -q generate_blank_partition_file --output $(BLANK_OTA_DATA_FILE); \ + $(PYTHON) $(IDF_PATH)/components/partition_table/gen_empty_partition.py $(OTA_DATA_SIZE) $(BLANK_OTA_DATA_FILE); \ fi; ) $(eval BLANK_OTA_DATA_FILE = $(shell if [ "$(OTA_DATA_OFFSET)" != "" ] && [ "$(OTA_DATA_SIZE)" != "" ]; then \ echo $(BLANK_OTA_DATA_FILE); else echo " "; fi) ) diff --git a/components/app_update/otatool.py b/components/app_update/otatool.py index aead479d92..2cfecde18b 100755 --- a/components/app_update/otatool.py +++ b/components/app_update/otatool.py @@ -21,16 +21,20 @@ import argparse import os import sys import binascii -import subprocess import tempfile import collections import struct -__version__ = '1.0' +try: + from parttool import PartitionName, PartitionType, ParttoolTarget, PARTITION_TABLE_OFFSET +except ImportError: + COMPONENTS_PATH = os.path.expandvars(os.path.join("$IDF_PATH", "components")) + PARTTOOL_DIR = os.path.join(COMPONENTS_PATH, "partition_table") -IDF_COMPONENTS_PATH = os.path.expandvars(os.path.join("$IDF_PATH", "components")) + sys.path.append(PARTTOOL_DIR) + from parttool import PartitionName, PartitionType, ParttoolTarget, PARTITION_TABLE_OFFSET -PARTTOOL_PY = os.path.join(IDF_COMPONENTS_PATH, "partition_table", "parttool.py") +__version__ = '2.0' SPI_FLASH_SEC_SIZE = 0x2000 @@ -42,121 +46,69 @@ def status(msg): print(msg) -def _invoke_parttool(parttool_args, args, output=False, partition=None): - invoke_args = [] +class OtatoolTarget(): - if partition: - invoke_args += [sys.executable, PARTTOOL_PY] + partition - else: - invoke_args += [sys.executable, PARTTOOL_PY, "--partition-type", "data", "--partition-subtype", "ota"] + OTADATA_PARTITION = PartitionType("data", "ota") - if quiet: - invoke_args += ["-q"] + def __init__(self, port=None, partition_table_offset=PARTITION_TABLE_OFFSET, partition_table_file=None, spi_flash_sec_size=SPI_FLASH_SEC_SIZE): + self.target = ParttoolTarget(port, partition_table_offset, partition_table_file) + self.spi_flash_sec_size = spi_flash_sec_size - if args.port != "": - invoke_args += ["--port", args.port] + temp_file = tempfile.NamedTemporaryFile(delete=False) + temp_file.close() + try: + self.target.read_partition(OtatoolTarget.OTADATA_PARTITION, temp_file.name) + with open(temp_file.name, "rb") as f: + self.otadata = f.read() + except Exception: + self.otadata = None + finally: + os.unlink(temp_file.name) - if args.partition_table_file: - invoke_args += ["--partition-table-file", args.partition_table_file] + def _check_otadata_partition(self): + if not self.otadata: + raise Exception("No otadata partition found") - if args.partition_table_offset: - invoke_args += ["--partition-table-offset", args.partition_table_offset] + def erase_otadata(self): + self._check_otadata_partition() + self.target.erase_partition(OtatoolTarget.OTADATA_PARTITION) - invoke_args += parttool_args + def _get_otadata_info(self): + info = [] - if output: - return subprocess.check_output(invoke_args) - else: - return subprocess.check_call(invoke_args) + otadata_info = collections.namedtuple("otadata_info", "seq crc") + for i in range(2): + start = i * (self.spi_flash_sec_size >> 1) -def _get_otadata_contents(args, check=True): - global quiet + seq = bytearray(self.otadata[start:start + 4]) + crc = bytearray(self.otadata[start + 28:start + 32]) - if check: - check_args = ["get_partition_info", "--info", "offset", "size"] + seq = struct.unpack('>I', seq) + crc = struct.unpack('>I', crc) - quiet = True - output = _invoke_parttool(check_args, args, True).split(b" ") - quiet = args.quiet + info.append(otadata_info(seq[0], crc[0])) - if not output: - raise RuntimeError("No ota_data partition found") + return info - with tempfile.NamedTemporaryFile(delete=False) as f: - f_name = f.name + def _get_partition_id_from_ota_id(self, ota_id): + if isinstance(ota_id, int): + return PartitionType("app", "ota_" + str(ota_id)) + else: + return PartitionName(ota_id) - try: - invoke_args = ["read_partition", "--output", f_name] - _invoke_parttool(invoke_args, args) - with open(f_name, "rb") as f: - contents = f.read() - finally: - os.unlink(f_name) + def switch_ota_partition(self, ota_id): + self._check_otadata_partition() - return contents + sys.path.append(PARTTOOL_DIR) + import gen_esp32part as gen - -def _get_otadata_status(otadata_contents): - status = [] - - otadata_status = collections.namedtuple("otadata_status", "seq crc") - - for i in range(2): - start = i * (SPI_FLASH_SEC_SIZE >> 1) - - seq = bytearray(otadata_contents[start:start + 4]) - crc = bytearray(otadata_contents[start + 28:start + 32]) - - seq = struct.unpack('>I', seq) - crc = struct.unpack('>I', crc) - - status.append(otadata_status(seq[0], crc[0])) - - return status - - -def read_otadata(args): - status("Reading ota_data partition contents...") - otadata_info = _get_otadata_contents(args) - otadata_info = _get_otadata_status(otadata_info) - - print(otadata_info) - - print("\t\t{:11}\t{:8s}|\t{:8s}\t{:8s}".format("OTA_SEQ", "CRC", "OTA_SEQ", "CRC")) - print("Firmware: 0x{:8x} \t 0x{:8x} |\t0x{:8x} \t 0x{:8x}".format(otadata_info[0].seq, otadata_info[0].crc, - otadata_info[1].seq, otadata_info[1].crc)) - - -def erase_otadata(args): - status("Erasing ota_data partition contents...") - _invoke_parttool(["erase_partition"], args) - status("Erased ota_data partition contents") - - -def switch_otadata(args): - sys.path.append(os.path.join(IDF_COMPONENTS_PATH, "partition_table")) - import gen_esp32part as gen - - with tempfile.NamedTemporaryFile(delete=False) as f: - f_name = f.name - - try: - def is_otadata_status_valid(status): + def is_otadata_info_valid(status): seq = status.seq % (1 << 32) crc = hex(binascii.crc32(struct.pack("I", seq), 0xFFFFFFFF) % (1 << 32)) return seq < (int('0xFFFFFFFF', 16) % (1 << 32)) and status.crc == crc - status("Looking for ota app partitions...") - - # In order to get the number of ota app partitions, we need the partition table - partition_table = None - invoke_args = ["get_partition_info", "--table", f_name] - - _invoke_parttool(invoke_args, args) - - partition_table = open(f_name, "rb").read() - partition_table = gen.PartitionTable.from_binary(partition_table) + partition_table = self.target.partition_table ota_partitions = list() @@ -171,39 +123,36 @@ def switch_otadata(args): ota_partitions = sorted(ota_partitions, key=lambda p: p.subtype) if not ota_partitions: - raise RuntimeError("No ota app partitions found") - - status("Verifying partition to switch to exists...") + raise Exception("No ota app partitions found") # Look for the app partition to switch to ota_partition_next = None try: - if args.name: - ota_partition_next = filter(lambda p: p.name == args.name, ota_partitions) + if isinstance(ota_id, int): + ota_partition_next = filter(lambda p: p.subtype - gen.MIN_PARTITION_SUBTYPE_APP_OTA == ota_id, ota_partitions) else: - ota_partition_next = filter(lambda p: p.subtype - gen.MIN_PARTITION_SUBTYPE_APP_OTA == args.slot, ota_partitions) + ota_partition_next = filter(lambda p: p.name == ota_id, ota_partitions) ota_partition_next = list(ota_partition_next)[0] except IndexError: - raise RuntimeError("Partition to switch to not found") + raise Exception("Partition to switch to not found") - otadata_contents = _get_otadata_contents(args) - otadata_status = _get_otadata_status(otadata_contents) + otadata_info = self._get_otadata_info() # Find the copy to base the computation for ota sequence number on otadata_compute_base = -1 # Both are valid, take the max as computation base - if is_otadata_status_valid(otadata_status[0]) and is_otadata_status_valid(otadata_status[1]): - if otadata_status[0].seq >= otadata_status[1].seq: + if is_otadata_info_valid(otadata_info[0]) and is_otadata_info_valid(otadata_info[1]): + if otadata_info[0].seq >= otadata_info[1].seq: otadata_compute_base = 0 else: otadata_compute_base = 1 # Only one copy is valid, use that - elif is_otadata_status_valid(otadata_status[0]): + elif is_otadata_info_valid(otadata_info[0]): otadata_compute_base = 0 - elif is_otadata_status_valid(otadata_status[1]): + elif is_otadata_info_valid(otadata_info[1]): otadata_compute_base = 1 # Both are invalid (could be initial state - all 0xFF's) else: @@ -216,7 +165,7 @@ def switch_otadata(args): # Find the next ota sequence number if otadata_compute_base == 0 or otadata_compute_base == 1: - base_seq = otadata_status[otadata_compute_base].seq % (1 << 32) + base_seq = otadata_info[otadata_compute_base].seq % (1 << 32) i = 0 while base_seq > target_seq % ota_partitions_num + i * ota_partitions_num: @@ -231,47 +180,68 @@ def switch_otadata(args): ota_seq_crc_next = binascii.crc32(ota_seq_next, 0xFFFFFFFF) % (1 << 32) ota_seq_crc_next = struct.pack("I", ota_seq_crc_next) - with open(f_name, "wb") as otadata_next_file: - start = (1 if otadata_compute_base == 0 else 0) * (SPI_FLASH_SEC_SIZE >> 1) + temp_file = tempfile.NamedTemporaryFile(delete=False) + temp_file.close() - otadata_next_file.write(otadata_contents) + try: + with open(temp_file.name, "wb") as otadata_next_file: + start = (1 if otadata_compute_base == 0 else 0) * (self.spi_flash_sec_size >> 1) - otadata_next_file.seek(start) - otadata_next_file.write(ota_seq_next) + otadata_next_file.write(self.otadata) - otadata_next_file.seek(start + 28) - otadata_next_file.write(ota_seq_crc_next) + otadata_next_file.seek(start) + otadata_next_file.write(ota_seq_next) - otadata_next_file.flush() + otadata_next_file.seek(start + 28) + otadata_next_file.write(ota_seq_crc_next) - _invoke_parttool(["write_partition", "--input", f_name], args) - status("Updated ota_data partition") - finally: - os.unlink(f_name) + otadata_next_file.flush() + + self.target.write_partition(OtatoolTarget.OTADATA_PARTITION, temp_file.name) + finally: + os.unlink(temp_file.name) + + def read_ota_partition(self, ota_id, output): + self.target.read_partition(self._get_partition_id_from_ota_id(ota_id), output) + + def write_ota_partition(self, ota_id, input): + self.target.write_partition(self._get_partition_id_from_ota_id(ota_id), input) + + def erase_ota_partition(self, ota_id): + self.target.erase_partition(self._get_partition_id_from_ota_id(ota_id)) -def _get_partition_specifier(args): - if args.name: - return ["--partition-name", args.name] - else: - return ["--partition-type", "app", "--partition-subtype", "ota_" + str(args.slot)] +def _read_otadata(target): + target._check_otadata_partition() + + otadata_info = target._get_otadata_info(target.otadata) + + print("\t\t{:11}\t{:8s}|\t{:8s}\t{:8s}".format("OTA_SEQ", "CRC", "OTA_SEQ", "CRC")) + print("Firmware: 0x{:8x} \t 0x{:8x} |\t0x{:8x} \t 0x{:8x}".format(otadata_info[0].seq, otadata_info[0].crc, + otadata_info[1].seq, otadata_info[1].crc)) -def read_ota_partition(args): - invoke_args = ["read_partition", "--output", args.output] - _invoke_parttool(invoke_args, args, partition=_get_partition_specifier(args)) - status("Read ota partition contents to file {}".format(args.output)) +def _erase_otadata(target): + target.erase_otadata() + status("Erased ota_data partition contents") -def write_ota_partition(args): - invoke_args = ["write_partition", "--input", args.input] - _invoke_parttool(invoke_args, args, partition=_get_partition_specifier(args)) - status("Written contents of file {} to ota partition".format(args.input)) +def _switch_ota_partition(target, ota_id): + target.switch_ota_partition(ota_id) -def erase_ota_partition(args): - invoke_args = ["erase_partition"] - _invoke_parttool(invoke_args, args, partition=_get_partition_specifier(args)) +def _read_ota_partition(target, ota_id, output): + target.read_ota_partition(ota_id, output) + status("Read ota partition contents to file {}".format(output)) + + +def _write_ota_partition(target, ota_id, input): + target.write_ota_partition(ota_id, input) + status("Written contents of file {} to ota partition".format(input)) + + +def _erase_ota_partition(target, ota_id): + target.erase_ota_partition(ota_id) status("Erased contents of ota partition") @@ -284,17 +254,20 @@ def main(): # There are two possible sources for the partition table: a device attached to the host # or a partition table CSV/binary file. These sources are mutually exclusive. - partition_table_info_source_args = parser.add_mutually_exclusive_group() + parser.add_argument("--port", "-p", help="port where the device to read the partition table from is attached") - partition_table_info_source_args.add_argument("--port", "-p", help="port where the device to read the partition table from is attached", default="") - partition_table_info_source_args.add_argument("--partition-table-file", "-f", help="file (CSV/binary) to read the partition table from", default="") + parser.add_argument("--partition-table-offset", "-o", help="offset to read the partition table from", type=str) - parser.add_argument("--partition-table-offset", "-o", help="offset to read the partition table from", default="0x8000") + parser.add_argument("--partition-table-file", "-f", help="file (CSV/binary) to read the partition table from; \ + overrides device attached to specified port as the partition table source when defined") subparsers = parser.add_subparsers(dest="operation", help="run otatool -h for additional help") + spi_flash_sec_size = argparse.ArgumentParser(add_help=False) + spi_flash_sec_size.add_argument("--spi-flash-sec-size", help="value of SPI_FLASH_SEC_SIZE macro", type=str) + # Specify the supported operations - subparsers.add_parser("read_otadata", help="read otadata partition") + subparsers.add_parser("read_otadata", help="read otadata partition", parents=[spi_flash_sec_size]) subparsers.add_parser("erase_otadata", help="erase otadata partition") slot_or_name_parser = argparse.ArgumentParser(add_help=False) @@ -302,7 +275,7 @@ def main(): slot_or_name_parser_args.add_argument("--slot", help="slot number of the ota partition", type=int) slot_or_name_parser_args.add_argument("--name", help="name of the ota partition") - subparsers.add_parser("switch_otadata", help="switch otadata partition", parents=[slot_or_name_parser]) + subparsers.add_parser("switch_ota_partition", help="switch otadata partition", parents=[slot_or_name_parser, spi_flash_sec_size]) read_ota_partition_subparser = subparsers.add_parser("read_ota_partition", help="read contents of an ota partition", parents=[slot_or_name_parser]) read_ota_partition_subparser.add_argument("--output", help="file to write the contents of the ota partition to") @@ -322,17 +295,69 @@ def main(): parser.print_help() sys.exit(1) - # Else execute the operation - operation_func = globals()[args.operation] + target_args = {} + + if args.port: + target_args["port"] = args.port + + if args.partition_table_file: + target_args["partition_table_file"] = args.partition_table_file + + if args.partition_table_offset: + target_args["partition_table_offset"] = int(args.partition_table_offset, 0) + + try: + if args.spi_flash_sec_size: + target_args["spi_flash_sec_size"] = int(args.spi_flash_sec_size, 0) + except AttributeError: + pass + + target = OtatoolTarget(**target_args) + + # Create the operation table and execute the operation + common_args = {'target':target} + + ota_id = [] + + try: + if args.name is not None: + ota_id = ["name"] + else: + if args.slot is not None: + ota_id = ["slot"] + except AttributeError: + pass + + otatool_ops = { + 'read_otadata':(_read_otadata, []), + 'erase_otadata':(_erase_otadata, []), + 'switch_ota_partition':(_switch_ota_partition, ota_id), + 'read_ota_partition':(_read_ota_partition, ["output"] + ota_id), + 'write_ota_partition':(_write_ota_partition, ["input"] + ota_id), + 'erase_ota_partition':(_erase_ota_partition, ota_id) + } + + (op, op_args) = otatool_ops[args.operation] + + for op_arg in op_args: + common_args.update({op_arg:vars(args)[op_arg]}) + + try: + common_args['ota_id'] = common_args.pop('name') + except KeyError: + try: + common_args['ota_id'] = common_args.pop('slot') + except KeyError: + pass if quiet: # If exceptions occur, suppress and exit quietly try: - operation_func(args) + op(**common_args) except Exception: sys.exit(2) else: - operation_func(args) + op(**common_args) if __name__ == '__main__': diff --git a/docs/en/api-reference/system/ota.rst b/docs/en/api-reference/system/ota.rst index 50938a3776..d83a38dab3 100644 --- a/docs/en/api-reference/system/ota.rst +++ b/docs/en/api-reference/system/ota.rst @@ -199,6 +199,104 @@ Secure OTA Updates Without Secure boot The verification of signed OTA updates can be performed even without enabling hardware secure boot. For doing so, refer :ref:`signed-app-verify` + +OTA Tool (otatool.py) +--------------------- + +The component `app_update` provides a tool :component_file:`otatool.py` for performing OTA partition-related operations on a target device. The following operations can be performed using the tool: + + - read contents of otadata partition (read_otadata) + - erase otadata partition, effectively resetting device to factory app (erase_otadata) + - switch OTA partitions (switch_ota_partition) + - erasing OTA partition (erase_ota_partition) + - write to OTA partition (write_ota_partition) + - read contents of OTA partition (read_ota_partition) + +The tool can either be imported and used from another Python script or invoked from shell script for users wanting to perform operation programmatically. This is facilitated by the tool's Python API +and command-line interface, respectively. + +Python API +^^^^^^^^^^ + +Before anything else, make sure that the `otatool` module is imported. + +.. code-block:: python + + import sys + import os + + idf_path = os.environ["IDF_PATH"] # get value of IDF_PATH from environment + otatool_dir = os.path.join(idf_path, "components", "app_update") # otatool.py lives in $IDF_PATH/components/app_update + + sys.path.append(otatool_dir) # this enables Python to find otatool module + from otatool import * # import all names inside otatool module + +The starting point for using the tool's Python API to do is create a `OtatoolTarget` object: + +.. code-block:: python + + # Create a partool.py target device connected on serial port /dev/ttyUSB1 + target = OtatoolTarget("/dev/ttyUSB1") + +The created object can now be used to perform operations on the target device: + +.. code-block:: python + + # Erase otadata, reseting the device to factory app + target.erase_otadata() + + # Erase contents of OTA app slot 0 + target.erase_ota_partition(0) + + # Switch boot partition to that of app slot 1 + target.switch_ota_partition(1) + + # Read OTA partition 'ota_3' and save contents to a file named 'ota_3.bin' + target.read_ota_partition("ota_3", "ota_3.bin") + +The OTA partition to operate on is specified using either the app slot number or the partition name. + +More information on the Python API is available in the docstrings for the tool. + +Command-line Interface +^^^^^^^^^^^^^^^^^^^^^^ + +The command-line interface of `otatool.py` has the following structure: + +.. code-block:: bash + + otatool.py [command-args] [subcommand] [subcommand-args] + + - command-args - these are arguments that are needed for executing the main command (parttool.py), mostly pertaining to the target device + - subcommand - this is the operation to be performed + - subcommand-args - these are arguments that are specific to the chosen operation + +.. code-block:: bash + + # Erase otadata, resetting the device to factory app + otatool.py --port "/dev/ttyUSB1" erase_otadata + + # Erase contents of OTA app slot 0 + otatool.py --port "/dev/ttyUSB1" erase_ota_partition --slot 0 + + # Switch boot partition to that of app slot 1 + otatool.py --port "/dev/ttyUSB1" switch_ota_partition --slot 1 + + # Read OTA partition 'ota_3' and save contents to a file named 'ota_3.bin' + otatool.py --port "/dev/ttyUSB1" read_ota_partition --name=ota_3 + + +More information can be obtained by specifying `--help` as argument: + +.. code-block:: bash + + # Display possible subcommands and show main command argument descriptions + otatool.py --help + + # Show descriptions for specific subcommand arguments + otatool.py [subcommand] --help + + See also -------- From 5d413224123710bda58ae73773edb54241142335 Mon Sep 17 00:00:00 2001 From: Renz Christian Bagaporo Date: Mon, 27 May 2019 11:09:35 +0800 Subject: [PATCH 021/486] examples: create example for both Python and CLI parttool interfaces --- examples/storage/parttool/README.md | 39 ++-- examples/storage/parttool/parttool_example.py | 191 +++++------------- examples/storage/parttool/parttool_example.sh | 73 +++++++ tools/ci/executable-list.txt | 1 + 4 files changed, 141 insertions(+), 163 deletions(-) create mode 100644 examples/storage/parttool/parttool_example.sh diff --git a/examples/storage/parttool/README.md b/examples/storage/parttool/README.md index b84203e03f..8b888fcfdd 100644 --- a/examples/storage/parttool/README.md +++ b/examples/storage/parttool/README.md @@ -4,10 +4,11 @@ This example demonstrates common operations the partitions tool [parttool.py](.. - reading, writing and erasing partitions, - retrieving info on a certain partition, -- dumping the entire partition table, and -- generating a blank partition file. +- dumping the entire partition table -Users taking a look at this example should focus on the contents of the python script [parttool_example.py](parttool_example.py). The script contains programmatic invocations of [parttool.py](../../../components/partition_table/parttool.py) in Python for the operations mentioned above; and can serve as a guide for users wanting to do the same in their applications. +Users taking a look at this example should focus on the contents of the Python script [parttool_example.py](parttool_example.py) or shell script [parttool_example.sh](parttool_example.sh). The scripts contain +programmatic invocation of the tool's functions via the Python API and command-line interface, respectively. Note +that on Windows, the shell script example requires a POSIX-compatible environment via MSYS2/Git Bash/WSL etc. The example performs the operations mentioned above in a straightforward manner: it performs writes to partitions and then verifies correct content by reading it back. For partitions, contents are compared to the originally written file. For the partition table, contents are verified against the partition table CSV @@ -17,50 +18,54 @@ file. An erased partition's contents is compared to a generated blank file. ### Build and Flash -Before running the example script [parttool_example.py](parttool_example.py), it is necessary to build and flash the firmware using the usual means: +Before running either of the example scripts, it is necessary to build and flash the firmware using the usual means: +Make: ```bash -# If using Make make build flash +``` -# If using CMake +CMake: +```bash idf.py build flash ``` ### Running [parttool_example.py](parttool_example.py) -The example can be executed by running the script [parttool_example.py](parttool_example.py). Either run it directly using - -```bash -./parttool_example.py -``` - -or run it using +The example can be executed by running the script [parttool_example.py](parttool_example.py) or [parttool_example.sh](parttool_example.sh). +Python script: ```bash python parttool_example.py ``` +Shell script: +``` +./parttool_example.sh +``` + The script searches for valid target devices connected to the host and performs the operations on the first one it finds. To perform the operations on a specific device, specify the port it is attached to during script invocation: +Python script: ```bash -# The target device is attached to /dev/ttyUSB2, for example python parttool_example.py --port /dev/ttyUSB2 ``` +Shell script: +``` +./parttool_example.sh /dev/ttyUSB2 +``` + ## Example output Running the script produces the following output: ``` Checking if device app binary matches built binary -Checking if device partition table matches partition table csv -Retrieving data partition offset and size Found data partition at offset 0x110000 with size 0x10000 Writing to data partition Reading data partition Erasing data partition -Generating blank data partition file Reading data partition Partition tool operations performed successfully! diff --git a/examples/storage/parttool/parttool_example.py b/examples/storage/parttool/parttool_example.py index 24b8fcd18f..bf2c6f7cdf 100755 --- a/examples/storage/parttool/parttool_example.py +++ b/examples/storage/parttool/parttool_example.py @@ -18,19 +18,12 @@ # limitations under the License. import os import sys -import subprocess import argparse -IDF_PATH = os.path.expandvars("$IDF_PATH") - -PARTTOOL_PY = os.path.join(IDF_PATH, "components", "partition_table", "parttool.py") - -PARTITION_TABLE_OFFSET = 0x8000 - -INVOKE_ARGS = [sys.executable, PARTTOOL_PY, "-q", "--partition-table-offset", str(PARTITION_TABLE_OFFSET)] +PARTITION_TABLE_DIR = os.path.join("components", "partition_table", "") -def sized_file_compare(file1, file2): +def assert_file_same(file1, file2, err): with open(file1, "rb") as f1: with open(file2, "rb") as f2: f1 = f1.read() @@ -41,121 +34,17 @@ def sized_file_compare(file1, file2): else: f1 = f1[:len(f2)] - return f1 == f2 - - -def check(condition, message): - if not condition: - print("Error: " + message) - sys.exit(1) - - -def write_data_partition(size): - print("Writing to data partition") - with open("write.bin", "wb") as f: - # Create a file to write to the data partition with randomly generated content - f.write(os.urandom(int(size, 16))) - - # Invokes the command - # - # parttool.py --partition-table-offset 0x8000 -q --partition-name storage write_partition --input write.bin - # - # to write the contents of a file to a partition in the device. - invoke_args = INVOKE_ARGS + ["--partition-name", "storage", "write_partition", "--input", f.name] - subprocess.check_call(invoke_args) - return f.name - - -def read_data_partition(): - print("Reading data partition") - # Invokes the command - # - # parttool.py --partition-table-offset 0x8000 -q --partition-name storage read_partition --output read.bin - # - # to read the contents of a partition in the device, which is then written to a file. - f = "read.bin" - invoke_args = INVOKE_ARGS + ["--partition-name", "storage", "read_partition", "--output", f] - subprocess.check_call(invoke_args) - return f - - -def get_data_partition_info(): - print("Retrieving data partition offset and size") - # Invokes the command - # - # parttool.py --partition-table-offset 0x8000 -q --partition-name storage get_partition_info --info offset size - # - # to get the offset and size of a partition named 'storage'. - invoke_args = INVOKE_ARGS + ["--partition-name", "storage", "get_partition_info", "--info", "offset", "size"] - - (offset, size) = subprocess.check_output(invoke_args).strip().split(b" ") - return (offset, size) - - -def check_app(args): - print("Checking if device app binary matches built binary") - # Invokes the command - # - # parttool.py --partition-table-offset 0x8000 --partition-type app --partition-subtype factory read_partition --output app.bin" - # - # to read the app binary and write it to a file. The read app binary is compared to the built binary in the build folder. - invoke_args = INVOKE_ARGS + ["--partition-type", "app", "--partition-subtype", "factory", "read_partition", "--output", "app.bin"] - subprocess.check_call(invoke_args) - - app_same = sized_file_compare("app.bin", args.binary) - check(app_same, "Device app binary does not match built binary") - - -def check_partition_table(): - sys.path.append(os.path.join(IDF_PATH, "components", "partition_table")) - import gen_esp32part as gen - - print("Checking if device partition table matches partition table csv") - # Invokes the command - # - # parttool.py --partition-table-offset 0x8000 get_partition_info --table table.bin - # - # to read the device partition table and write it to a file. The read partition table is compared to - # the partition table csv. - invoke_args = INVOKE_ARGS + ["get_partition_info", "--table", "table.bin"] - subprocess.check_call(invoke_args) - - with open("table.bin", "rb") as read: - partition_table_csv = os.path.join(IDF_PATH, "examples", "storage", "parttool", "partitions_example.csv") - with open(partition_table_csv, "r") as csv: - read = gen.PartitionTable.from_binary(read.read()) - csv = gen.PartitionTable.from_csv(csv.read()) - check(read == csv, "Device partition table does not match csv partition table") - - -def erase_data_partition(): - print("Erasing data partition") - # Invokes the command - # - # parttool.py --partition-table-offset 0x8000 --partition-name storage erase_partition - # - # to erase the 'storage' partition. - invoke_args = INVOKE_ARGS + ["--partition-name", "storage", "erase_partition"] - subprocess.check_call(invoke_args) - - -def generate_blank_data_file(): - print("Generating blank data partition file") - - # Invokes the command - # - # parttool.py --partition-table-offset 0x8000 --partition-name storage generate_blank_partition_file --output blank.bin - # - # to generate a blank partition file and write it to a file. The blank partition file has the same size as the - # 'storage' partition. - f = "blank.bin" - invoke_args = INVOKE_ARGS + ["--partition-name", "storage", "generate_blank_partition_file", "--output", f] - subprocess.check_call(invoke_args) - return f + if not f1 == f2: + raise Exception(err) def main(): - global INVOKE_ARGS + COMPONENTS_PATH = os.path.expandvars(os.path.join("$IDF_PATH", "components")) + PARTTOOL_DIR = os.path.join(COMPONENTS_PATH, "partition_table") + + sys.path.append(PARTTOOL_DIR) + from parttool import PartitionName, PartitionType, ParttoolTarget + from gen_empty_partition import generate_blanked_file parser = argparse.ArgumentParser("ESP-IDF Partitions Tool Example") @@ -164,43 +53,53 @@ def main(): args = parser.parse_args() - if args.port: - INVOKE_ARGS += ["--port", args.port] + target = ParttoolTarget(args.port) - # Before proceeding, do checks to verify whether the app and partition table in the device matches the built binary and - # the generated partition table during build - check_app(args) - check_partition_table() + # Read app partition and save the contents to a file. The app partition is identified + # using type-subtype combination + print("Checking if device app binary matches built binary") + factory = PartitionType("app", "factory") + target.read_partition(factory, "app.bin") + assert_file_same(args.binary, "app.bin", "Device app binary does not match built binary") - # Get the offset and size of the data partition - (offset, size) = get_data_partition_info() + # Retrieve info on data storage partition, this time identifying it by name. + storage = PartitionName("storage") + storage_info = target.get_partition_info(storage) + print("Found data partition at offset 0x{:x} with size 0x{:x}".format(storage_info.offset, storage_info.size)) - print("Found data partition at offset %s with size %s" % (offset, size)) + # Create a file whose contents will be written to the storage partition + with open("write.bin", "wb") as f: + # Create a file to write to the data partition with randomly generated content + f.write(os.urandom(storage_info.size)) - # Write a generated file of random bytes to the found data partition - written = write_data_partition(size) + # Write the contents of the created file to storage partition + print("Writing to data partition") + target.write_partition(storage, "write.bin") - # Read back the contents of the data partition - read = read_data_partition() + # Read back the contents of the storage partition + print("Reading data partition") + target.read_partition(storage, "read.bin") - # Compare the written and read back data - data_same = sized_file_compare(read, written) - check(data_same, "Read contents of the data partition does not match written data") + assert_file_same("write.bin", "read.bin", "Read contents of storage partition does not match source file contents") - # Erase the data partition - erase_data_partition() + # Erase contents of the storage partition + print("Erasing data partition") + target.erase_partition(storage) - # Read back the erase data partition, which should be all 0xFF's after erasure - read = read_data_partition() + # Read back the erased data partition + print("Reading data partition") + target.read_partition(storage, "read.bin") - # Generate blank partition file (all 0xFF's) - blank = generate_blank_data_file() + # Generate a file of all 0xFF + generate_blanked_file(storage_info.size, "blank.bin") - # Verify that the partition has been erased by comparing the contents to the generated blank file - data_same = sized_file_compare(read, blank) - check(data_same, "Erased data partition contents does not match blank partition file") + assert_file_same("blank.bin", "read.bin", "Contents of storage partition not fully erased") + # Example end and cleanup print("\nPartition tool operations performed successfully!") + clean_files = ["app.bin", "read.bin", "blank.bin", "write.bin"] + for clean_file in clean_files: + os.unlink(clean_file) if __name__ == '__main__': diff --git a/examples/storage/parttool/parttool_example.sh b/examples/storage/parttool/parttool_example.sh new file mode 100644 index 0000000000..888c914686 --- /dev/null +++ b/examples/storage/parttool/parttool_example.sh @@ -0,0 +1,73 @@ +#!/bin/bash +# +# Demonstrates command-line interface of Partition Tool, parttool.py +# +# +# $1 - serial port where target device to operate on is connnected to, by default the first found valid serial port +# $2 - path to this example's built binary file (parttool.bin), by default $PWD/build/parttool.bin +PORT=$1 +PARTTOOL_PY="python $IDF_PATH/components/partition_table/parttool.py -q" + +if [[ "$PORT" != "" ]]; then + PARTTOOL_PY="$PARTTOOL_PY --port $PORT" +fi + +GEN_EMPTY_PARTITION_PY="python $IDF_PATH/components/partition_table/gen_empty_partition.py" + +BINARY=$2 + +if [[ "$BINARY" == "" ]]; then + BINARY=build/parttool.bin +fi + +function assert_file_same() +{ + sz_a=$(stat -c %s $1) + sz_b=$(stat -c %s $2) + sz=$((sz_a < sz_b ? sz_a : sz_b)) + res=$(cmp -s -n $sz $1 $2) || + (echo "!!!!!!!!!!!!!!!!!!!" + echo "FAILURE: $3" + echo "!!!!!!!!!!!!!!!!!!!") +} + +# Read app partition and save the contents to a file. The app partition is identified +# using type-subtype combination +echo "Checking if device app binary matches built binary" +$PARTTOOL_PY read_partition --partition-type=app --partition-subtype=factory --output=app.bin +assert_file_same app.bin $BINARY "Device app binary does not match built binary" + +# Retrieve info on data storage partition, this time identifying it by name. +offset=$($PARTTOOL_PY get_partition_info --partition-name=storage --info offset) +size=$($PARTTOOL_PY get_partition_info --partition-name=storage --info size) +echo "Found data partition at offset $offset with size $size" + +# Create a file whose contents will be written to the storage partition +head -c $(($size)) /dev/urandom > write.bin + +# Write the contents of the created file to storage partition +echo "Writing to data partition" +$PARTTOOL_PY write_partition --partition-name=storage --input write.bin + +# Read back the contents of the storage partition +echo "Reading data partition" +$PARTTOOL_PY read_partition --partition-name=storage --output read.bin + +assert_file_same write.bin read.bin "Read contents of storage partition does not match source file contents" + +# Erase contents of the storage partition +echo "Erasing data partition" +$PARTTOOL_PY erase_partition --partition-name=storage + +# Read back the erased data partition +echo "Reading data partition" +$PARTTOOL_PY read_partition --partition-name=storage --output read.bin + +# Generate a file of all 0xFF +$GEN_EMPTY_PARTITION_PY $(($size)) blank.bin + +assert_file_same read.bin blank.bin "Contents of storage partition not fully erased" + +# Example end and cleanup +printf "\nPartition tool operations performed successfully\n" +rm -rf app.bin read.bin blank.bin write.bin \ No newline at end of file diff --git a/tools/ci/executable-list.txt b/tools/ci/executable-list.txt index 0dc1c7fd15..f6ac3d1e7a 100644 --- a/tools/ci/executable-list.txt +++ b/tools/ci/executable-list.txt @@ -22,6 +22,7 @@ examples/build_system/cmake/idf_as_lib/build.sh examples/build_system/cmake/idf_as_lib/run-esp32.sh examples/build_system/cmake/idf_as_lib/run.sh examples/storage/parttool/parttool_example.py +examples/storage/parttool/parttool_example.sh examples/system/ota/otatool/otatool_example.py tools/check_kconfigs.py tools/check_python_dependencies.py From 7f7a9272f0f1aae4721c688f31f47957e37cb527 Mon Sep 17 00:00:00 2001 From: Renz Christian Bagaporo Date: Mon, 27 May 2019 11:10:00 +0800 Subject: [PATCH 022/486] examples: create example for both Python and CLI otatool interfaces --- examples/system/ota/otatool/README.md | 57 ++--- .../ota/otatool/get_running_partition.py | 87 ++++++++ .../system/ota/otatool/otatool_example.py | 194 ++++++------------ .../system/ota/otatool/otatool_example.sh | 95 +++++++++ tools/ci/executable-list.txt | 2 + 5 files changed, 275 insertions(+), 160 deletions(-) create mode 100644 examples/system/ota/otatool/get_running_partition.py create mode 100644 examples/system/ota/otatool/otatool_example.sh diff --git a/examples/system/ota/otatool/README.md b/examples/system/ota/otatool/README.md index c14e1cfb0d..9b52e8952c 100644 --- a/examples/system/ota/otatool/README.md +++ b/examples/system/ota/otatool/README.md @@ -6,7 +6,9 @@ This example demonstrates common operations the OTA tool [otatool.py](../../../c - switching boot partitions, and - switching to factory partition. -Users taking a look at this example should focus on the contents of the python script [otatool_example.py](otatool_example.py). The script contains programmatic invocations of the tool [otatool.py](../../../components/app_update/otatool.py) in Python for the operations mentioned above; and can serve as a guide for users wanting to do the same in their applications. +Users taking a look at this example should focus on the contents of the Python script [otatool_example.py](otatool_example.py) or shell script [otatool_example.sh](otatool_example.sh). The scripts contain +programmatic invocation of the tool's functions via the Python API and command-line interface, respectively. Note +that on Windows, the shell script example requires a POSIX-compatible environment via MSYS2/Git Bash/WSL etc. The built application in this example outputs the currently running partition, whose output is used to verify if the tool switched OTA partitions succesfully. The built application binary is written to all OTA partitions at the start of the example to be able to determine the running @@ -16,38 +18,46 @@ partition for all switches performed. ### Build and Flash -Before running the example script [otatool_example.py](otatool_example.py), it is necessary to build and flash the firmware using the usual means: +Before running either of the example scripts, it is necessary to build and flash the firmware using the usual means: +Make: ```bash -# If using Make make build flash +``` -# If using CMake +CMake: +```bash idf.py build flash ``` ### Running [otatool_example.py](otatool_example.py) -The example can be executed by running the script [otatool_example.py](otatool_example.py). Either run it directly using - -```bash -./otatool_example.py -``` - -or run it using +The example can be executed by running the script [otatool_example.py](otatool_example.py) or [otatool_example.sh](otatool_example.sh). +Python script: ```bash python otatool_example.py ``` -The script searches for valid target devices connected to the host and performs the operations on the first one it finds. This could present problems if there -are multiple viable target devices attached to the host. To perform the operations on a specific device, specify the port it is attached to during script invocation: +Shell script: +``` +./otatool_example.sh +``` +The script searches for valid target devices connected to the host and performs the operations on the first one it finds. This could present problems if there +are multiple viable target devices attached to the host. To perform the operations on a specific device, specify the port it is attached to during script invocation ("/dev/ttyUSB2" for example): + +Python script: ```bash -# The target device is attached to /dev/ttyUSB2, for example python otatool_example.py --port /dev/ttyUSB2 ``` + +Shell script: +``` +./otatool_example.sh /dev/ttyUSB2 +``` + ## Example output Running the script produces the following output: @@ -55,16 +65,13 @@ Running the script produces the following output: ``` Writing factory firmware to ota_0 Writing factory firmware to ota_1 -Checking written firmware to ota_0 and ota_1 match factory firmware -Switching to ota partition name factory -Switching to ota partition name factory -Switching to ota partition slot 0 -Switching to ota partition name ota_1 -Switching to ota partition slot 1 -Switching to ota partition name ota_0 -Switching to ota partition slot 0 -Switching to ota partition name factory -Switching to ota partition slot 1 +Switching to factory app +Switching to OTA slot 0 +Switching to OTA slot 1 (twice in a row) +Switching to OTA slot 0 (twice in a row) +Switching to factory app +Switching to OTA slot 1 + +Partition tool operations performed successfully -OTA tool operations executed successfully! ``` diff --git a/examples/system/ota/otatool/get_running_partition.py b/examples/system/ota/otatool/get_running_partition.py new file mode 100644 index 0000000000..b91d71a6d4 --- /dev/null +++ b/examples/system/ota/otatool/get_running_partition.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python +# +# Demonstrates the use of otatool.py, a tool for performing ota partition level +# operations. +# +# Copyright 2018 Espressif Systems (Shanghai) PTE LTD +# +# 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. +import os +import sys +import serial +import subprocess +import re +import argparse + +from subprocess import CalledProcessError + + +def get_running_partition(port=None): + # Monitor the serial output of target device. The firmware outputs the currently + # running partition + + IDF_PATH = os.path.expandvars("$IDF_PATH") + sys.path.append(os.path.join(IDF_PATH, 'components', 'esptool_py', 'esptool')) + import esptool + + ESPTOOL_PY = os.path.join(IDF_PATH, "components", "esptool_py", "esptool", "esptool.py") + + baud = os.environ.get("ESPTOOL_BAUD", esptool.ESPLoader.ESP_ROM_BAUD) + + if not port: + error_message = "Unable to obtain default target device port.\nSerial log:\n\n" + try: + # Check what esptool.py finds on what port the device is connected to + output = subprocess.check_output([sys.executable, ESPTOOL_PY, "chip_id"]) # may raise CalledProcessError + pattern = r"Serial port ([\S]+)" + pattern = re.compile(pattern.encode()) + + port = re.search(pattern, output).group(1) # may raise AttributeError + except CalledProcessError as e: + raise Exception(error_message + e.output) + except AttributeError: + raise Exception(error_message + output) + + serial_instance = serial.serial_for_url(port.decode("utf-8"), baud, do_not_open=True) + + serial_instance.dtr = False + serial_instance.rts = False + + serial_instance.rts = True + serial_instance.open() + serial_instance.rts = False + + # Read until example end and find the currently running partition string + content = serial_instance.read_until(b"Example end") + pattern = re.compile(b"Running partition: ([a-z0-9_]+)") + running = re.search(pattern, content).group(1) + + return running.decode("utf-8") + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--port", default=None) + args = parser.parse_args() + + try: + res = get_running_partition(args.port) + except Exception as e: + print(e.message) + sys.exit(1) + + print(res) + + +if __name__ == "__main__": + main() diff --git a/examples/system/ota/otatool/otatool_example.py b/examples/system/ota/otatool/otatool_example.py index 17ed0cdb9e..b2a464eaec 100755 --- a/examples/system/ota/otatool/otatool_example.py +++ b/examples/system/ota/otatool/otatool_example.py @@ -18,20 +18,12 @@ # limitations under the License. import os import sys -import subprocess import argparse -import serial -import re -IDF_PATH = os.path.expandvars("$IDF_PATH") - -OTATOOL_PY = os.path.join(IDF_PATH, "components", "app_update", "otatool.py") -ESPTOOL_PY = os.path.join(IDF_PATH, "components", "esptool_py", "esptool", "esptool.py") - -INVOKE_ARGS = [sys.executable, OTATOOL_PY, "-q"] +from get_running_partition import get_running_partition -def sized_file_compare(file1, file2): +def assert_file_same(file1, file2, err): with open(file1, "rb") as f1: with open(file2, "rb") as f2: f1 = f1.read() @@ -42,122 +34,22 @@ def sized_file_compare(file1, file2): else: f1 = f1[:len(f2)] - return f1 == f2 + if not f1 == f2: + raise Exception(err) -def check(condition, message): - if not condition: - print("Error: " + message) - sys.exit(1) - - -def flash_example_firmware_to_ota_partitions(args): - # Invokes the command - # - # otatool.py -q write_ota_partition --slot or - # otatool.py -q write_ota_partition --name - # - # to write the contents of a file to the specified ota partition (either using name or the slot number) - print("Writing factory firmware to ota_0") - invoke_args = INVOKE_ARGS + ["write_ota_partition", "--slot", "0", "--input", args.binary] - subprocess.check_call(invoke_args) - - print("Writing factory firmware to ota_1") - invoke_args = INVOKE_ARGS + ["write_ota_partition", "--name", "ota_1", "--input", args.binary] - subprocess.check_call(invoke_args) - - # Verify that the contents of the two ota slots are the same as that of the factory partition - print("Checking written firmware to ota_0 and ota_1 match factory firmware") - - # Invokes the command - # - # otatool.py -q read_ota_partition --slot or - # otatool.py -q read_ota_partition --name - # - # to read the contents of a specified ota partition (either using name or the slot number) and write to a file - invoke_args = INVOKE_ARGS + ["read_ota_partition", "--slot", "0", "--output", "app_0.bin"] - subprocess.check_call(invoke_args) - - invoke_args = INVOKE_ARGS + ["read_ota_partition", "--name", "ota_1", "--output", "app_1.bin"] - subprocess.check_call(invoke_args) - - ota_same = sized_file_compare("app_0.bin", args.binary) - check(ota_same, "Slot 0 app does not match factory app") - - ota_same = sized_file_compare("app_1.bin", args.binary) - check(ota_same, "Slot 1 app does not match factory app") - - -def check_running_ota_partition(expected, port=None): - # Monitor the serial output of target device. The firmware outputs the currently - # running partition. It should match the partition the otatool switched to. - - if expected == 0 or expected == "ota_0": - expected = b"ota_0" - elif expected == 1 or expected == "ota_1": - expected = b"ota_1" - else: - expected = b"factory" - - sys.path.append(os.path.join(IDF_PATH, 'components', 'esptool_py', 'esptool')) - import esptool - - baud = os.environ.get("ESPTOOL_BAUD", esptool.ESPLoader.ESP_ROM_BAUD) - - if not port: - # Check what esptool.py finds on what port the device is connected to - output = subprocess.check_output([sys.executable, ESPTOOL_PY, "chip_id"]) - pattern = r"Serial port ([\S]+)" - pattern = re.compile(pattern.encode()) - port = re.search(pattern, output).group(1) - - serial_instance = serial.serial_for_url(port.decode("utf-8"), baud, do_not_open=True) - - serial_instance.dtr = False - serial_instance.rts = False - - serial_instance.rts = True - serial_instance.open() - serial_instance.rts = False - - # Read until example end and find the currently running partition string - content = serial_instance.read_until(b"Example end") - pattern = re.compile(b"Running partition: ([a-z0-9_]+)") - running = re.search(pattern, content).group(1) - - check(expected == running, "Running partition %s does not match expected %s" % (running, expected)) - - -def switch_partition(part, port): - if isinstance(part, int): - spec = "slot" - else: - spec = "name" - - print("Switching to ota partition %s %s" % (spec, str(part))) - - if str(part) == "factory": - # Invokes the command - # - # otatool.py -q erase_otadata - # - # to erase the otadata partition, effectively setting boot firmware to - # factory - subprocess.check_call(INVOKE_ARGS + ["erase_otadata"]) - else: - # Invokes the command - # - # otatool.py -q switch_otadata --slot or - # otatool.py -q switch_otadata --name - # - # to switch to the indicated ota partition (either using name or the slot number) - subprocess.check_call(INVOKE_ARGS + ["switch_otadata", "--" + spec, str(part)]) - - check_running_ota_partition(part, port) +def assert_running_partition(expected, port=None): + running = get_running_partition(port) + if running != expected: + raise Exception("Running partition %s does not match expected %s" % (running, expected)) def main(): - global INVOKE_ARGS + COMPONENTS_PATH = os.path.expandvars(os.path.join("$IDF_PATH", "components")) + OTATOOL_DIR = os.path.join(COMPONENTS_PATH, "app_update") + + sys.path.append(OTATOOL_DIR) + from otatool import OtatoolTarget parser = argparse.ArgumentParser("ESP-IDF OTA Tool Example") @@ -165,29 +57,61 @@ def main(): parser.add_argument("--binary", "-b", help="path to built example binary", default=os.path.join("build", "otatool.bin")) args = parser.parse_args() - if args.port: - INVOKE_ARGS += ["--port", args.port] + target = OtatoolTarget(args.port) - # Flash the factory firmware to all ota partitions - flash_example_firmware_to_ota_partitions(args) + print("Writing factory firmware to ota_0") + target.write_ota_partition(0, args.binary) - # Perform switching ota partitions - switch_partition("factory", args.port) - switch_partition("factory", args.port) # check switching to factory partition twice in a row + print("Writing factory firmware to ota_1") + target.write_ota_partition("ota_1", args.binary) - switch_partition(0, args.port) + # Verify that the contents of the two ota slots are the same as that of the factory partition + print("Checking written firmware to ota_0 and ota_1 match factory firmware") + target.read_ota_partition("ota_0", "app0.bin") + target.read_ota_partition(1, "app1.bin") - switch_partition("ota_1", args.port) - switch_partition(1, args.port) # check switching to ota_1 partition twice in a row + assert_file_same("app0.bin", args.binary, "Slot 0 app does not match factory app") + assert_file_same("app1.bin", args.binary, "Slot 1 app does not match factory app") - switch_partition("ota_0", args.port) - switch_partition(0, args.port) # check switching to ota_0 partition twice in a row + # Switch to factory app + print("Switching to factory app") + target.erase_otadata() + assert_running_partition("factory") - switch_partition("factory", args.port) + # Switch to slot 0 + print("Switching to OTA slot 0") + target.switch_ota_partition(0) + assert_running_partition("ota_0") - switch_partition(1, args.port) # check switching to ota_1 partition from factory + # Switch to slot 1 twice in a row + print("Switching to OTA slot 1 (twice in a row)") + target.switch_ota_partition(1) + assert_running_partition("ota_1") + target.switch_ota_partition("ota_1") + assert_running_partition("ota_1") + # Switch to slot 0 twice in a row + print("Switching to OTA slot 0 (twice in a row)") + target.switch_ota_partition(0) + assert_running_partition("ota_0") + target.switch_ota_partition("ota_0") + assert_running_partition("ota_0") + + # Switch to factory app + print("Switching to factory app") + target.erase_otadata() + assert_running_partition("factory") + + # Switch to slot 1 + print("Switching to OTA slot 1") + target.switch_ota_partition(1) + assert_running_partition("ota_1") + + # Example end and cleanup print("\nOTA tool operations executed successfully!") + clean_files = ["app0.bin", "app1.bin"] + for clean_file in clean_files: + os.unlink(clean_file) if __name__ == '__main__': diff --git a/examples/system/ota/otatool/otatool_example.sh b/examples/system/ota/otatool/otatool_example.sh new file mode 100644 index 0000000000..bcf8456dcc --- /dev/null +++ b/examples/system/ota/otatool/otatool_example.sh @@ -0,0 +1,95 @@ +#!/bin/bash +# +# Demonstrates command-line interface of OTA Partitions Tool, otatool.py +# +# +# $1 - serial port where target device to operate on is connnected to, by default the first found valid serial port +# $2 - path to this example's built binary file (parttool.bin), by default $PWD/build/otatool.bin + +PORT=$1 +OTATOOL_PY="python $IDF_PATH/components/app_update/otatool.py -q" + +if [[ "$PORT" != "" ]]; then + OTATOOL_PY="$OTATOOL_PY --port $PORT" +fi + +BINARY=$2 + +if [[ "$BINARY" == "" ]]; then + BINARY=build/otatool.bin +fi + +function assert_file_same() +{ + sz_a=$(stat -c %s $1) + sz_b=$(stat -c %s $2) + sz=$((sz_a < sz_b ? sz_a : sz_b)) + res=$(cmp -s -n $sz $1 $2) || + (echo "!!!!!!!!!!!!!!!!!!!" + echo "FAILURE: $3" + echo "!!!!!!!!!!!!!!!!!!!") +} + +function assert_running_partition() +{ + running=$(python get_running_partition.py) + if [[ "$running" != "$1" ]]; then + echo "!!!!!!!!!!!!!!!!!!!" + echo "FAILURE: Running partition '$running' does not match expected '$1'" + echo "!!!!!!!!!!!!!!!!!!!" + exit 1 + fi +} + +# Flash the example firmware to OTA partitions. The first write uses slot number to identify OTA +# partition, the second one uses the name. +echo "Writing factory firmware to ota_0" +$OTATOOL_PY write_ota_partition --slot 0 --input $BINARY + +echo "Writing factory firmware to ota_1" +$OTATOOL_PY write_ota_partition --name ota_1 --input $BINARY + +# Read back the written firmware +$OTATOOL_PY read_ota_partition --name ota_0 --output app0.bin +$OTATOOL_PY read_ota_partition --slot 1 --output app1.bin + +assert_file_same $BINARY app0.bin "Slot 0 app does not match factory app" +assert_file_same $BINARY app1.bin "Slot 1 app does not match factory app" + +# Switch to factory app +echo "Switching to factory app" +$OTATOOL_PY erase_otadata +assert_running_partition factory + +# Switch to slot 0 +echo "Switching to OTA slot 0" +$OTATOOL_PY switch_ota_partition --slot 0 +assert_running_partition ota_0 + +# Switch to slot 1 twice in a row +echo "Switching to OTA slot 1 (twice in a row)" +$OTATOOL_PY switch_ota_partition --slot 1 +assert_running_partition ota_1 +$OTATOOL_PY switch_ota_partition --name ota_1 +assert_running_partition ota_1 + +# Switch to slot 0 twice in a row +echo "Switching to OTA slot 0 (twice in a row)" +$OTATOOL_PY switch_ota_partition --slot 0 +assert_running_partition ota_0 +$OTATOOL_PY switch_ota_partition --name ota_0 +assert_running_partition ota_0 + +# Switch to factory app +echo "Switching to factory app" +$OTATOOL_PY erase_otadata +assert_running_partition factory + +# Switch to slot 1 +echo "Switching to OTA slot 1" +$OTATOOL_PY switch_ota_partition --slot 1 +assert_running_partition ota_1 + +# Example end and cleanup +printf "\nPartition tool operations performed successfully\n" +rm -rf app0.bin app1.bin \ No newline at end of file diff --git a/tools/ci/executable-list.txt b/tools/ci/executable-list.txt index f6ac3d1e7a..e0637d9bf8 100644 --- a/tools/ci/executable-list.txt +++ b/tools/ci/executable-list.txt @@ -23,7 +23,9 @@ examples/build_system/cmake/idf_as_lib/run-esp32.sh examples/build_system/cmake/idf_as_lib/run.sh examples/storage/parttool/parttool_example.py examples/storage/parttool/parttool_example.sh +examples/system/ota/otatool/get_running_partition.py examples/system/ota/otatool/otatool_example.py +examples/system/ota/otatool/otatool_example.sh tools/check_kconfigs.py tools/check_python_dependencies.py tools/ci/apply_bot_filter.py From 3836aa9ae679a3de7a220cd6142097a1bb4020bf Mon Sep 17 00:00:00 2001 From: Renz Christian Bagaporo Date: Tue, 4 Jun 2019 15:49:55 +0800 Subject: [PATCH 023/486] partition_table,app_update: use config partition table offset --- components/app_update/CMakeLists.txt | 10 ++++++++-- components/app_update/Makefile.projbuild | 8 ++++++-- components/partition_table/Makefile.projbuild | 5 +++++ 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/components/app_update/CMakeLists.txt b/components/app_update/CMakeLists.txt index e869c188fb..6e48af6942 100644 --- a/components/app_update/CMakeLists.txt +++ b/components/app_update/CMakeLists.txt @@ -42,10 +42,16 @@ if(NOT BOOTLOADER_BUILD) set(otatool_py ${python} ${COMPONENT_DIR}/otatool.py) add_custom_target(read_otadata DEPENDS "${PARTITION_CSV_PATH}" - COMMAND ${otatool_py} --partition-table-file ${PARTITION_CSV_PATH} read_otadata) + COMMAND ${otatool_py} + --partition-table-file ${PARTITION_CSV_PATH} + --partition-table-offset ${PARTITION_TABLE_OFFSET} + read_otadata) add_custom_target(erase_otadata DEPENDS "${PARTITION_CSV_PATH}" - COMMAND ${otatool_py} --partition-table-file ${PARTITION_CSV_PATH} erase_otadata) + COMMAND ${otatool_py} + --partition-table-file ${PARTITION_CSV_PATH} + --partition-table-offset ${PARTITION_TABLE_OFFSET} + erase_otadata) esptool_py_flash_project_args(otadata ${otadata_offset} "${blank_otadata_file}" FLASH_IN_PROJECT) endif() diff --git a/components/app_update/Makefile.projbuild b/components/app_update/Makefile.projbuild index a20856a6ef..a3f2f68423 100644 --- a/components/app_update/Makefile.projbuild +++ b/components/app_update/Makefile.projbuild @@ -29,10 +29,14 @@ blank_ota_data: $(BLANK_OTA_DATA_FILE) ESPTOOL_ALL_FLASH_ARGS += $(OTA_DATA_OFFSET) $(BLANK_OTA_DATA_FILE) erase_otadata: $(PARTITION_TABLE_CSV_PATH) partition_table_get_info | check_python_dependencies - $(OTATOOL_PY) --partition-table-file $(PARTITION_TABLE_CSV_PATH) erase_otadata + $(OTATOOL_PY) --partition-table-file $(PARTITION_TABLE_CSV_PATH) \ + --partition-table-offset $(PARTITION_TABLE_OFFSET) \ + erase_otadata read_otadata: $(PARTITION_TABLE_CSV_PATH) partition_table_get_info | check_python_dependencies - $(OTATOOL_PY) --partition-table-file $(PARTITION_TABLE_CSV_PATH) read_otadata + $(OTATOOL_PY) --partition-table-file $(PARTITION_TABLE_CSV_PATH) \ + --partition-table-offset $(partition_table_offset) \ + read_otadata erase_ota: erase_otadata @echo "WARNING: erase_ota is deprecated. Use erase_otadata instead." diff --git a/components/partition_table/Makefile.projbuild b/components/partition_table/Makefile.projbuild index 353a7655d6..6b28b093f5 100644 --- a/components/partition_table/Makefile.projbuild +++ b/components/partition_table/Makefile.projbuild @@ -64,14 +64,19 @@ all_binaries: $(PARTITION_TABLE_BIN) partition_table_get_info check_table_conten partition_table_get_info: $(PARTITION_TABLE_BIN) $(eval PHY_DATA_OFFSET:=$(shell $(GET_PART_INFO) --partition-table-file $(PARTITION_TABLE_BIN) \ + --partition-table-offset $(PARTITION_TABLE_OFFSET) \ get_partition_info --partition-type data --partition-subtype phy --info offset)) $(eval APP_OFFSET:=$(shell $(GET_PART_INFO) --partition-table-file $(PARTITION_TABLE_BIN) \ + --partition-table-offset $(PARTITION_TABLE_OFFSET) \ get_partition_info --partition-boot-default --info offset)) $(eval OTA_DATA_OFFSET:=$(shell $(GET_PART_INFO) --partition-table-file $(PARTITION_TABLE_BIN) \ + --partition-table-offset $(PARTITION_TABLE_OFFSET) \ get_partition_info --partition-type data --partition-subtype ota --info offset)) $(eval OTA_DATA_SIZE:=$(shell $(GET_PART_INFO) --partition-table-file $(PARTITION_TABLE_BIN) \ + --partition-table-offset $(PARTITION_TABLE_OFFSET) \ get_partition_info --partition-type data --partition-subtype ota --info size)) $(eval FACTORY_OFFSET:=$(shell $(GET_PART_INFO) --partition-table-file $(PARTITION_TABLE_BIN) \ + --partition-table-offset $(PARTITION_TABLE_OFFSET) \ get_partition_info --partition-type app --partition-subtype factory --info offset)) export APP_OFFSET From f59c3e7e3e20a0527916d1b7acbe9219eeadb162 Mon Sep 17 00:00:00 2001 From: Andrey Gramakov Date: Wed, 22 May 2019 12:11:25 +0300 Subject: [PATCH 024/486] Updated building-openocd-windows.rst instruction --- .../building-openocd-windows.rst | 86 +++++++++++++------ 1 file changed, 62 insertions(+), 24 deletions(-) diff --git a/docs/en/api-guides/jtag-debugging/building-openocd-windows.rst b/docs/en/api-guides/jtag-debugging/building-openocd-windows.rst index a04b89df62..2452235176 100644 --- a/docs/en/api-guides/jtag-debugging/building-openocd-windows.rst +++ b/docs/en/api-guides/jtag-debugging/building-openocd-windows.rst @@ -5,9 +5,27 @@ Building OpenOCD from Sources for Windows The following instructions are alternative to downloading binary OpenOCD from `Espressif GitHub `_. To quickly setup the binary OpenOCD, instead of compiling it yourself, backup and proceed to section :doc:`setup-openocd-windows`. - .. highlight:: bash +.. note:: + + Following instructions are assumed to be runned in MSYS2 environment with MINGW32 subsystem! + + +Install Dependencies +==================== + +Install packages that are required to compile OpenOCD:: + + pacman -S --noconfirm --needed autoconf automake git make \ + mingw-w64-i686-gcc \ + mingw-w64-i686-toolchain \ + mingw-w64-i686-libtool \ + mingw-w64-i686-pkg-config \ + mingw-w64-cross-winpthreads-git \ + p7zip + + Download Sources of OpenOCD =========================== @@ -16,30 +34,20 @@ The sources for the ESP32-enabled variant of OpenOCD are available from Espressi cd ~/esp git clone --recursive https://github.com/espressif/openocd-esp32.git + The clone of sources should be now saved in ``~/esp/openocd-esp32`` directory. -Install Dependencies -==================== +Downloading libusb +================== -Install packages that are required to compile OpenOCD: +Build and export variables for a following OpenOCD compilation:: -.. note:: + wget https://github.com/libusb/libusb/releases/download/v1.0.22/libusb-1.0.22.7z + 7z x -olibusb ./libusb-1.0.22.7z + export CPPFLAGS="$CPPFLAGS -I${PWD}/libusb/include/libusb-1.0" + export LDFLAGS="$LDFLAGS -L${PWD}/libusb/MinGW32/.libs/dll" - Install the following packages one by one, check if installation was successful and then proceed to the next package. Resolve reported problems before moving to the next step. - -:: - - pacman -S libtool - pacman -S autoconf - pacman -S automake - pacman -S texinfo - pacman -S mingw-w64-i686-libusb-compat-git - pacman -S pkg-config - -.. note:: - - Installation of ``pkg-config`` is breaking operation of esp-idf toolchain. After building of OpenOCD it should be uninstalled. It be covered at the end of this instruction. To build OpenOCD again, you will need to run ``pacman -S pkg-config`` once more. This issue does not concern other packages installed in this step (before ``pkg-config``). Build OpenOCD @@ -48,11 +56,15 @@ Build OpenOCD Proceed with configuring and building OpenOCD:: cd ~/esp/openocd-esp32 + export CPPFLAGS="$CPPFLAGS -D__USE_MINGW_ANSI_STDIO=1 -Wno-error"; export CFLAGS="$CFLAGS -Wno-error" ./bootstrap - ./configure + ./configure --disable-doxygen-pdf --enable-ftdi --enable-jlink --enable-ulink --build=i686-w64-mingw32 --host=i686-w64-mingw32 make + cp ../libusb/MinGW32/dll/libusb-1.0.dll ./src + cp /opt/i686-w64-mingw32/bin/libwinpthread-1.dll ./src + -Optionally you can add ``make install`` step at the end. Skip it, if you have an existing OpenOCD (from e.g. another development platform), as it may get overwritten. +Optionally you can add ``make install`` step at the end. Skip it, if you have an existing OpenOCD (from e.g. another development platform), as it may get overwritten. Also you could use ``export DESTDIR="/custom/install/dir"; make install``. .. note:: @@ -61,12 +73,38 @@ Optionally you can add ``make install`` step at the end. Skip it, if you have an * If the ``./configure`` is successfully run, information of enabled JTAG will be printed under ``OpenOCD configuration summary``. * If the information of your device is not shown in the log, use ``./configure`` to enable it as described in ``../openocd-esp32/doc/INSTALL.txt``. * For details concerning compiling OpenOCD, please refer to ``openocd-esp32/README.Windows``. + * Don't forget to copy `libusb-1.0.dll` and `libwinpthread-1.dll` into `OOCD_INSTALLDIR/bin` from ``~/esp/openocd-esp32/src``. -Once ``make`` process is successfully completed, the executable of OpenOCD will be saved in ``~/esp/openocd-esp32/src/openocd`` directory. +Once ``make`` process is successfully completed, the executable of OpenOCD will be saved in ``~/esp/openocd-esp32/src`` directory. -Remove ``pkg-config``, as discussed during installation of dependencies:: - pacman -Rs pkg-config +Full Listing +============ + +A complete described previously process is provided below for the faster execution, e.g. as a shell script:: + + pacman -S --noconfirm --needed autoconf automake git make mingw-w64-i686-gcc mingw-w64-i686-toolchain mingw-w64-i686-libtool mingw-w64-i686-pkg-config mingw-w64-cross-winpthreads-git p7zip + cd ~/esp + git clone --recursive https://github.com/espressif/openocd-esp32.git + + wget https://github.com/libusb/libusb/releases/download/v1.0.22/libusb-1.0.22.7z + 7z x -olibusb ./libusb-1.0.22.7z + export CPPFLAGS="$CPPFLAGS -I${PWD}/libusb/include/libusb-1.0"; export LDFLAGS="$LDFLAGS -L${PWD}/libusb/MinGW32/.libs/dll" + + export CPPFLAGS="$CPPFLAGS -D__USE_MINGW_ANSI_STDIO=1 -Wno-error"; export CFLAGS="$CFLAGS -Wno-error" + cd ~/esp/openocd-esp32 + ./bootstrap + ./configure --disable-doxygen-pdf --enable-ftdi --enable-jlink --enable-ulink --build=i686-w64-mingw32 --host=i686-w64-mingw32 + make + cp ../libusb/MinGW32/dll/libusb-1.0.dll ./src + cp /opt/i686-w64-mingw32/bin/libwinpthread-1.dll ./src + + # # optional + # export DESTDIR="$PWD" + # make install + # cp ./src/libusb-1.0.dll $DESTDIR/mingw32/bin + # cp ./src/libwinpthread-1.dll $DESTDIR/mingw32/bin + Next Steps From 33dd7011be064abf94e3c6e80887684796df4b40 Mon Sep 17 00:00:00 2001 From: Renz Christian Bagaporo Date: Fri, 24 May 2019 18:32:42 +0800 Subject: [PATCH 025/486] cmake: expand build components before generating config !4452 had config generation first before building the component list to be used in the build. This proved to be detrimental when a new target is added as config generation would consider configs from both targets. --- tools/cmake/build.cmake | 13 +++++++------ tools/cmake/kconfig.cmake | 17 ++++++++++------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/tools/cmake/build.cmake b/tools/cmake/build.cmake index 585d26e195..97c43e8a23 100644 --- a/tools/cmake/build.cmake +++ b/tools/cmake/build.cmake @@ -365,12 +365,6 @@ macro(idf_build_process target) # Check for required Python modules __build_check_python() - # Generate config values in different formats - idf_build_get_property(sdkconfig SDKCONFIG) - idf_build_get_property(sdkconfig_defaults SDKCONFIG_DEFAULTS) - __kconfig_generate_config("${sdkconfig}" "${sdkconfig_defaults}") - __build_import_configs() - # Write the partial build properties to a temporary file. # The path to this generated file is set to a short-lived build # property BUILD_PROPERTIES_FILE. @@ -416,6 +410,7 @@ macro(idf_build_process target) idf_build_unset_property(BUILD_PROPERTIES_FILE) file(REMOVE ${build_properties_file}) + # Finally, do component expansion. In this case it simply means getting a final list # of build component targets given the requirements set by each component. if(__COMPONENTS) @@ -442,6 +437,12 @@ macro(idf_build_process target) idf_build_set_property(___COMPONENT_REQUIRES_COMMON ${lib} APPEND) endforeach() + # Generate config values in different formats + idf_build_get_property(sdkconfig SDKCONFIG) + idf_build_get_property(sdkconfig_defaults SDKCONFIG_DEFAULTS) + __kconfig_generate_config("${sdkconfig}" "${sdkconfig_defaults}") + __build_import_configs() + # Temporary trick to support both gcc5 and gcc8 builds if(CMAKE_C_COMPILER_VERSION VERSION_EQUAL 5.2.0) set(GCC_NOT_5_2_0 0 CACHE STRING "GCC is 5.2.0 version") diff --git a/tools/cmake/kconfig.cmake b/tools/cmake/kconfig.cmake index 020e595509..28be99371e 100644 --- a/tools/cmake/kconfig.cmake +++ b/tools/cmake/kconfig.cmake @@ -88,14 +88,17 @@ endfunction() function(__kconfig_generate_config sdkconfig sdkconfig_defaults) # List all Kconfig and Kconfig.projbuild in known components idf_build_get_property(component_targets __COMPONENT_TARGETS) + idf_build_get_property(build_component_targets __BUILD_COMPONENT_TARGETS) foreach(component_target ${component_targets}) - __component_get_property(kconfig ${component_target} KCONFIG) - __component_get_property(kconfig_projbuild ${component_target} KCONFIG_PROJBUILD) - if(kconfig) - list(APPEND kconfigs ${kconfig}) - endif() - if(kconfig_projbuild) - list(APPEND kconfig_projbuilds ${kconfig_projbuild}) + if(component_target IN_LIST build_component_targets) + __component_get_property(kconfig ${component_target} KCONFIG) + __component_get_property(kconfig_projbuild ${component_target} KCONFIG_PROJBUILD) + if(kconfig) + list(APPEND kconfigs ${kconfig}) + endif() + if(kconfig_projbuild) + list(APPEND kconfig_projbuilds ${kconfig_projbuild}) + endif() endif() endforeach() From 297b2c5a3974b376bb17dc3785e8ffffb2701913 Mon Sep 17 00:00:00 2001 From: Renz Christian Bagaporo Date: Tue, 28 May 2019 21:43:03 +0800 Subject: [PATCH 026/486] cmake: evaluate component requirements in one go !4452 simplified early expansion by using an early expansion script that only does one thing: get the public and private requirements for each component, albeit one by one. This was also dependent on parsing the command output of the expansion script. This commit makes it so that a list of all components to be processed to passed to the expansion script, generating a cmake file that sets each component requirements in one go. This also makes sure that only components that registered themselves get included in the final build list. --- tools/cmake/build.cmake | 46 ++-------- tools/cmake/component.cmake | 58 ++++++------ .../scripts/component_get_requirements.cmake | 88 +++++++++++++++---- 3 files changed, 107 insertions(+), 85 deletions(-) diff --git a/tools/cmake/build.cmake b/tools/cmake/build.cmake index 97c43e8a23..6cf9a73b55 100644 --- a/tools/cmake/build.cmake +++ b/tools/cmake/build.cmake @@ -195,7 +195,8 @@ function(__build_expand_requirements component_target) # Since there are circular dependencies, make sure that we do not infinitely # expand requirements for each component. idf_build_get_property(component_targets_seen __COMPONENT_TARGETS_SEEN) - if(component_target IN_LIST component_targets_seen) + __component_get_property(component_registered ${component_target} __COMPONENT_REGISTERED) + if(component_target IN_LIST component_targets_seen OR NOT component_registered) return() endif() @@ -365,51 +366,13 @@ macro(idf_build_process target) # Check for required Python modules __build_check_python() - # Write the partial build properties to a temporary file. - # The path to this generated file is set to a short-lived build - # property BUILD_PROPERTIES_FILE. - idf_build_get_property(build_dir BUILD_DIR) - set(build_properties_file ${build_dir}/build_properties.temp.cmake) - idf_build_set_property(BUILD_PROPERTIES_FILE ${build_properties_file}) - __build_write_properties(${build_properties_file}) + idf_build_set_property(__COMPONENT_REQUIRES_COMMON ${target} APPEND) + __component_get_requirements() # Perform early expansion of component CMakeLists.txt in CMake scripting mode. # It is here we retrieve the public and private requirements of each component. # It is also here we add the common component requirements to each component's # own requirements. - idf_build_get_property(component_targets __COMPONENT_TARGETS) - idf_build_set_property(__COMPONENT_REQUIRES_COMMON ${target} APPEND) - idf_build_get_property(common_reqs __COMPONENT_REQUIRES_COMMON) - foreach(component_target ${component_targets}) - get_property(component_dir TARGET ${component_target} PROPERTY COMPONENT_DIR) - __component_get_requirements(error reqs priv_reqs ${component_dir}) - if(error) - message(FATAL_ERROR "${error}") - endif() - - list(APPEND reqs "${common_reqs}") - - # Remove duplicates and the component itself from its requirements - __component_get_property(alias ${component_target} COMPONENT_ALIAS) - __component_get_property(_name ${component_target} COMPONENT_NAME) - - # Prevent component from linking to itself. - if(reqs) - list(REMOVE_DUPLICATES reqs) - list(REMOVE_ITEM reqs ${alias} ${_name}) - endif() - - if(priv_reqs) - list(REMOVE_DUPLICATES priv_reqs) - list(REMOVE_ITEM priv_reqs ${alias} ${_name}) - endif() - - __component_set_property(${component_target} REQUIRES "${reqs}") - __component_set_property(${component_target} PRIV_REQUIRES "${priv_reqs}") - endforeach() - idf_build_unset_property(BUILD_PROPERTIES_FILE) - file(REMOVE ${build_properties_file}) - # Finally, do component expansion. In this case it simply means getting a final list # of build component targets given the requirements set by each component. @@ -431,6 +394,7 @@ macro(idf_build_process target) # Get a list of common component requirements in component targets form (previously # we just have a list of component names) + idf_build_get_property(common_reqs __COMPONENT_REQUIRES_COMMON) foreach(common_req ${common_reqs}) __component_get_target(component_target ${common_req}) __component_get_property(lib ${component_target} COMPONENT_LIB) diff --git a/tools/cmake/component.cmake b/tools/cmake/component.cmake index b3dd460c83..6caca93a74 100644 --- a/tools/cmake/component.cmake +++ b/tools/cmake/component.cmake @@ -125,6 +125,23 @@ function(__component_dir_quick_check var component_dir) set(${var} ${res} PARENT_SCOPE) endfunction() +# +# Write a CMake file containing all component and their properties. This is possible because each component +# keeps a list of all its properties. +# +function(__component_write_properties output_file) + idf_build_get_property(component_targets __COMPONENT_TARGETS) + foreach(component_target ${component_targets}) + __component_get_property(component_properties ${component_target} __COMPONENT_PROPERTIES) + foreach(property ${component_properties}) + __component_get_property(val ${component_target} ${property}) + set(component_properties_text + "${component_properties_text}\nset(__component_${component_target}_${property} ${val})") + endforeach() + file(WRITE ${output_file} "${component_properties_text}") + endforeach() +endfunction() + # # Add a component to process in the build. The components are keeped tracked of in property # __COMPONENT_TARGETS in component target form. @@ -184,44 +201,35 @@ endfunction() # Given a component directory, get the requirements by expanding it early. The expansion is performed # using a separate CMake script (the expansion is performed in a separate instance of CMake in scripting mode). # -function(__component_get_requirements error requires_var priv_requires_var component_dir) +function(__component_get_requirements) idf_build_get_property(idf_path IDF_PATH) - idf_build_get_property(build_properties_file BUILD_PROPERTIES_FILE) - idf_build_get_property(idf_target IDF_TARGET) - # This function assumes that the directory has been checked to contain a component, thus - # no check is performed here. + idf_build_get_property(build_dir BUILD_DIR) + set(build_properties_file ${build_dir}/build_properties.temp.cmake) + set(component_properties_file ${build_dir}/component_properties.temp.cmake) + set(component_requires_file ${build_dir}/component_requires.temp.cmake) + + __build_write_properties(${build_properties_file}) + __component_write_properties(${component_properties_file}) + execute_process(COMMAND "${CMAKE_COMMAND}" - -D "IDF_PATH=${idf_path}" - -D "IDF_TARGET=${idf_target}" - -D "COMPONENT_DIR=${component_dir}" -D "BUILD_PROPERTIES_FILE=${build_properties_file}" - -D "CMAKE_BUILD_EARLY_EXPANSION=1" + -D "COMPONENT_PROPERTIES_FILE=${component_properties_file}" + -D "COMPONENT_REQUIRES_FILE=${component_requires_file}" -P "${idf_path}/tools/cmake/scripts/component_get_requirements.cmake" RESULT_VARIABLE result ERROR_VARIABLE error ) if(NOT result EQUAL 0) - set(error "${error}" PARENT_SCOPE) - return() + message(FATAL_ERROR "${error}") endif() - string(REGEX REPLACE ";" "\\\\;" _output "${error}") - string(REGEX REPLACE "\n" ";" _output "${_output}") - list(REVERSE _output) + include(${component_requires_file}) - if(_output) - list(GET _output 1 _output) - - string(REGEX MATCH "\(.*\):::\(.*\)" _output "${_output}") - - string(REPLACE ":" ";" requires "${CMAKE_MATCH_1}") - string(REPLACE ":" ";" priv_requires "${CMAKE_MATCH_2}") - endif() - - set(${requires_var} ${requires} PARENT_SCOPE) - set(${priv_requires_var} ${priv_requires} PARENT_SCOPE) + file(REMOVE ${build_properties_file}) + file(REMOVE ${component_properties_file}) + file(REMOVE ${component_requires_file}) endfunction() # __component_add_sources, __component_check_target diff --git a/tools/cmake/scripts/component_get_requirements.cmake b/tools/cmake/scripts/component_get_requirements.cmake index 8223f709be..0b0d18727e 100644 --- a/tools/cmake/scripts/component_get_requirements.cmake +++ b/tools/cmake/scripts/component_get_requirements.cmake @@ -1,10 +1,5 @@ -include(${IDF_PATH}/tools/cmake/utilities.cmake) - include("${BUILD_PROPERTIES_FILE}") -include("${SDKCONFIG_CMAKE}") - -macro(require_idf_targets) -endmacro() +include("${COMPONENT_PROPERTIES_FILE}") function(idf_build_get_property var property) cmake_parse_arguments(_ "GENERATOR_EXPRESSION" "" "" ${ARGN}) @@ -12,17 +7,20 @@ function(idf_build_get_property var property) message(FATAL_ERROR "Getting build property generator expression not supported before idf_component_register().") endif() - set(${var} ${property} PARENT_SCOPE) + set(${var} ${${property}} PARENT_SCOPE) endfunction() -function(print_requires requires priv_requires) - spaces2list(requires) - spaces2list(priv_requires) - string(REPLACE ";" ":" requires "${requires}") - string(REPLACE ";" ":" priv_requires "${priv_requires}") - message("${requires}:::${priv_requires}") +idf_build_get_property(idf_path IDF_PATH) +include(${idf_path}/tools/cmake/utilities.cmake) + +function(__component_get_property var component_target property) + set(_property __component_${component_target}_${property}) + set(${var} ${${_property}} PARENT_SCOPE) endfunction() +macro(require_idf_targets) +endmacro() + macro(idf_component_register) set(options) set(single_value) @@ -30,14 +28,16 @@ macro(idf_component_register) INCLUDE_DIRS PRIV_INCLUDE_DIRS LDFRAGMENTS REQUIRES PRIV_REQUIRES REQUIRED_IDF_TARGETS EMBED_FILES EMBED_TXTFILES) cmake_parse_arguments(_ "${options}" "${single_value}" "${multi_value}" "${ARGN}") - print_requires("${__REQUIRES}" "${__PRIV_REQUIRES}") - set(__is_component 1) + set(__component_requires "${__REQUIRES}") + set(__component_priv_requires "${__PRIV_REQUIRES}") + set(__component_registered 1) return() endmacro() macro(register_component) - print_requires("${COMPONENT_REQUIRES}" "${COMPONENT_PRIV_REQUIRES}") - set(__is_component 1) + set(__component_requires "${COMPONENT_REQUIRES}") + set(__component_priv_requires "${COMPONENT_PRIV_REQUIRES}") + set(__component_registered 1) return() endmacro() @@ -45,5 +45,55 @@ macro(register_config_only_component) register_component() endmacro() -set(CMAKE_BUILD_EARLY_EXPANSION) -include(${COMPONENT_DIR}/CMakeLists.txt OPTIONAL) +idf_build_get_property(__common_reqs __COMPONENT_REQUIRES_COMMON) +idf_build_get_property(__component_targets __COMPONENT_TARGETS) + +function(__component_get_requirements) + # This is in a function (separate variable context) so that variables declared + # and set by the included CMakeLists.txt does not bleed into the next inclusion. + # We are only interested in the public and private requirements of components + __component_get_property(__component_dir ${__component_target} COMPONENT_DIR) + include(${__component_dir}/CMakeLists.txt OPTIONAL) + + spaces2list(__component_requires) + spaces2list(__component_priv_requires) + + set(__component_requires "${__component_requires}" PARENT_SCOPE) + set(__component_priv_requires "${__component_priv_requires}" PARENT_SCOPE) + set(__component_registered ${__component_registered} PARENT_SCOPE) +endfunction() + +set(CMAKE_BUILD_EARLY_EXPANSION 1) +foreach(__component_target ${__component_targets}) + set(__component_requires "") + set(__component_priv_requires "") + set(__component_registered 0) + + __component_get_requirements() + + list(APPEND __component_requires "${__common_reqs}") + + # Remove duplicates and the component itself from its requirements + __component_get_property(__component_alias ${__component_target} COMPONENT_ALIAS) + __component_get_property(__component_name ${__component_target} COMPONENT_NAME) + + # Prevent component from linking to itself. + if(__component_requires) + list(REMOVE_DUPLICATES __component_requires) + list(REMOVE_ITEM __component_requires ${__component_alias} ${__component_name}) + endif() + + if(__component_requires) + list(REMOVE_DUPLICATES __component_priv_requires) + list(REMOVE_ITEM __component_priv_requires ${__component_alias} ${__component_name}) + endif() + + set(__contents +"__component_set_property(${__component_target} REQUIRES \"${__component_requires}\") +__component_set_property(${__component_target} PRIV_REQUIRES \"${__component_priv_requires}\") +__component_set_property(${__component_target} __COMPONENT_REGISTERED ${__component_registered})" + ) + set(__component_requires_contents "${__component_requires_contents}\n${__contents}") +endforeach() + +file(WRITE ${COMPONENT_REQUIRES_FILE} "${__component_requires_contents}") \ No newline at end of file From 70dfcb35d44430256cb56c9c7ec21cfdcc8639c8 Mon Sep 17 00:00:00 2001 From: Renz Christian Bagaporo Date: Tue, 4 Jun 2019 20:19:25 +0800 Subject: [PATCH 027/486] mbedtls: component CMakeLists.txt corrections Since !4452 the common component requirements automatically get privately linked to libraries built under ESP-IDF build system (this includes targets from third-party libraries). This removes a variable that was used for that purpose before !4452. Since the internal target names were changed, the compile definition for warning on using deprecated functions is not being passed. Since using the internal name is unreliable, prefer passing this compile definition from the test itself. --- components/mbedtls/CMakeLists.txt | 9 +-------- components/mbedtls/test/CMakeLists.txt | 6 ++++++ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/components/mbedtls/CMakeLists.txt b/components/mbedtls/CMakeLists.txt index 3b1fd504e2..44af366f45 100644 --- a/components/mbedtls/CMakeLists.txt +++ b/components/mbedtls/CMakeLists.txt @@ -2,8 +2,6 @@ set(COMPONENT_ADD_INCLUDEDIRS "port/include" "mbedtls/include") set(COMPONENT_SRCS "mbedtls.c") set(COMPONENT_REQUIRES lwip) -set(MBEDTLS_PRIV_REQUIRES ${IDF_COMPONENT_REQUIRES_COMMON} soc) - register_component() # Only build mbedtls libraries @@ -96,9 +94,4 @@ foreach(target ${mbedtls_targets}) endforeach() # Link mbedtls libraries to component library -target_link_libraries(${COMPONENT_LIB} ${mbedtls_targets}) - -# Catch usage of deprecated mbedTLS functions when building tests -if(mbedtls_test IN_LIST BUILD_TEST_COMPONENTS) - add_definitions(-DMBEDTLS_DEPRECATED_WARNING) -endif() \ No newline at end of file +target_link_libraries(${COMPONENT_LIB} ${mbedtls_targets}) \ No newline at end of file diff --git a/components/mbedtls/test/CMakeLists.txt b/components/mbedtls/test/CMakeLists.txt index e1aad74fac..5a8cfd8d33 100644 --- a/components/mbedtls/test/CMakeLists.txt +++ b/components/mbedtls/test/CMakeLists.txt @@ -4,3 +4,9 @@ set(COMPONENT_ADD_INCLUDEDIRS ".") set(COMPONENT_REQUIRES unity test_utils mbedtls) register_component() + +idf_component_get_property(mbedtls mbedtls COMPONENT_LIB) +target_compile_definitions(${mbedtls} PUBLIC "-DMBEDTLS_DEPRECATED_WARNING") +target_compile_definitions(mbedtls PUBLIC "-DMBEDTLS_DEPRECATED_WARNING") +target_compile_definitions(mbedcrypto PUBLIC "-DMBEDTLS_DEPRECATED_WARNING") +target_compile_definitions(mbedx509 PUBLIC "-DMBEDTLS_DEPRECATED_WARNING") From f0f861ccd9912f93a43b51037655e4cb5bfb0127 Mon Sep 17 00:00:00 2001 From: Renz Christian Bagaporo Date: Fri, 31 May 2019 11:30:44 +0800 Subject: [PATCH 028/486] ldgen: use user input filename for processed template Previously ldgen determines the output file name on its own. This commit makes it so that user can dictate what the output file name will be for the processed template, if the user needs it for something else. --- components/esp32/CMakeLists.txt | 3 ++- tools/cmake/ldgen.cmake | 7 +------ tools/cmake/utilities.cmake | 5 +++-- tools/ldgen/ldgen.py | 10 ++++++++++ 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/components/esp32/CMakeLists.txt b/components/esp32/CMakeLists.txt index 3710237896..0fca8aa0c0 100644 --- a/components/esp32/CMakeLists.txt +++ b/components/esp32/CMakeLists.txt @@ -58,7 +58,8 @@ else() # Process the template file through the linker script generation mechanism, and use the output for linking the # final binary - target_linker_script(${COMPONENT_LIB} "${CMAKE_CURRENT_LIST_DIR}/ld/esp32.project.ld.in" PROCESS) + target_linker_script(${COMPONENT_LIB} "${CMAKE_CURRENT_LIST_DIR}/ld/esp32.project.ld.in" + PROCESS "${CMAKE_CURRENT_BINARY_DIR}/ld/esp32.project.ld") target_linker_script(${COMPONENT_LIB} "ld/esp32.peripherals.ld") target_link_libraries(${COMPONENT_LIB} gcc) diff --git a/tools/cmake/ldgen.cmake b/tools/cmake/ldgen.cmake index 9161ecd8f1..b8cfaad499 100644 --- a/tools/cmake/ldgen.cmake +++ b/tools/cmake/ldgen.cmake @@ -27,7 +27,7 @@ endfunction() # # Passes a linker script template to the linker script generation tool for # processing -function(__ldgen_process_template output_var template) +function(__ldgen_process_template template output) idf_build_get_property(idf_target IDF_TARGET) idf_build_get_property(idf_path IDF_PATH) @@ -36,11 +36,6 @@ function(__ldgen_process_template output_var template) file(GENERATE OUTPUT ${build_dir}/ldgen_libraries.in CONTENT $) file(GENERATE OUTPUT ${build_dir}/ldgen_libraries INPUT ${build_dir}/ldgen_libraries.in) - get_filename_component(filename "${template}" NAME) - - set(output ${CMAKE_CURRENT_BINARY_DIR}/${filename}.ld) - set(${output_var} ${output} PARENT_SCOPE) - set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES "${build_dir}/ldgen_libraries.in" diff --git a/tools/cmake/utilities.cmake b/tools/cmake/utilities.cmake index 4e64c16201..f5f7f9693c 100644 --- a/tools/cmake/utilities.cmake +++ b/tools/cmake/utilities.cmake @@ -131,13 +131,14 @@ endfunction() # and then adds -T with the filename only. This allows INCLUDE directives to be # used to include other linker scripts in the same directory. function(target_linker_script target scriptfiles) - cmake_parse_arguments(_ "PROCESS" "" "" ${ARGN}) + cmake_parse_arguments(_ "" "PROCESS" "" ${ARGN}) foreach(scriptfile ${scriptfiles}) get_filename_component(abs_script "${scriptfile}" ABSOLUTE) message(STATUS "Adding linker script ${abs_script}") if(__PROCESS) - __ldgen_process_template(output ${abs_script}) + get_filename_component(output "${__PROCESS}" ABSOLUTE) + __ldgen_process_template(${abs_script} ${output}) set(abs_script ${output}) endif() diff --git a/tools/ldgen/ldgen.py b/tools/ldgen/ldgen.py index 7829e4dfb8..dcc0db4e07 100755 --- a/tools/ldgen/ldgen.py +++ b/tools/ldgen/ldgen.py @@ -19,6 +19,8 @@ import argparse import sys import tempfile import subprocess +import os +import errno from fragments import FragmentFile from sdkconfig import SDKConfig @@ -111,6 +113,14 @@ def main(): with tempfile.TemporaryFile("w+") as output: script_model.write(output) output.seek(0) + + if not os.path.exists(os.path.dirname(output_path)): + try: + os.makedirs(os.path.dirname(output_path)) + except OSError as exc: + if exc.errno != errno.EEXIST: + raise + with open(output_path, "w") as f: # only create output file after generation has suceeded f.write(output.read()) except LdGenFailure as e: From 6365658d3fb481ed45bd143ce7c1cc54d4f7ec45 Mon Sep 17 00:00:00 2001 From: Renz Christian Bagaporo Date: Fri, 31 May 2019 15:27:11 +0800 Subject: [PATCH 029/486] cmake: revert using EXCLUDE_FROM_ALL when adding component subdirectories Reverting (for now) the change in !4452 to use EXCLUDE_FROM_ALL. Apparently this also affects custom targets with ALL option specified, not causing them to be built with the project. This is apparently a bug which has a merged fix: https://gitlab.kitware.com/cmake/cmake/merge_requests/2816 --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 141f903892..951c14ed36 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -74,9 +74,9 @@ foreach(component_target ${build_component_targets}) idf_build_get_property(build_prefix __PREFIX) set(__idf_component_context 1) if(NOT prefix STREQUAL build_prefix) - add_subdirectory(${dir} ${prefix}_${_name} EXCLUDE_FROM_ALL) + add_subdirectory(${dir} ${prefix}_${_name}) else() - add_subdirectory(${dir} ${_name} EXCLUDE_FROM_ALL) + add_subdirectory(${dir} ${_name}) endif() set(__idf_component_context 0) endforeach() From 3882e48e8adf6cd1f5d9e42be732f2253ba99db0 Mon Sep 17 00:00:00 2001 From: Renz Christian Bagaporo Date: Tue, 4 Jun 2019 19:05:33 +0800 Subject: [PATCH 030/486] cmake: use new signature form of target_link_library to link components !4452 used setting LINK_LIBRARIES and INTERFACE_LINK_LIBRARIES to link components built under ESP-IDF build system. However, LINK_LIBRARIES does not produce behavior same as linking PRIVATE. This MR uses the new signature for target_link_libraries directly instead. This also moves setting dependencies during component registration rather than after all components have been processed. The consequence is that internally, components have to use the new signature form as well. This does not affect linking the components to external targets, such as with idf_as_lib example. This only affects linking additional libraries to ESP-IDF libraries outside component processing (after idf_build_process), which is not even possible for CMake") - target_link_libraries(${COMPONENT_LIB} extra) + target_link_libraries(${COMPONENT_LIB} PUBLIC extra) else() - target_link_libraries(${COMPONENT_LIB} ${LIBC} ${LIBM} gcc) + target_link_libraries(${COMPONENT_LIB} PUBLIC ${LIBC} ${LIBM} gcc) endif() set_source_files_properties(heap.c PROPERTIES COMPILE_FLAGS -fno-builtin) if(EXTRA_LINK_FLAGS) - target_link_libraries(${COMPONENT_LIB} "${EXTRA_LINK_FLAGS}") + target_link_libraries(${COMPONENT_LIB} INTERFACE "${EXTRA_LINK_FLAGS}") endif() diff --git a/components/ulp/project_include.cmake b/components/ulp/project_include.cmake index b5ac4c54e2..95996b32f0 100644 --- a/components/ulp/project_include.cmake +++ b/components/ulp/project_include.cmake @@ -61,7 +61,7 @@ function(ulp_embed_binary app_name s_sources exp_dep_srcs) add_dependencies(${COMPONENT_LIB} ${app_name}_artifacts) - target_linker_script(${COMPONENT_LIB} ${CMAKE_CURRENT_BINARY_DIR}/${app_name}/${app_name}.ld) + target_linker_script(${COMPONENT_LIB} INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/${app_name}/${app_name}.ld) target_add_binary_data(${COMPONENT_LIB} ${CMAKE_CURRENT_BINARY_DIR}/${app_name}/${app_name}.bin BINARY) endif() endfunction() \ No newline at end of file diff --git a/components/vfs/CMakeLists.txt b/components/vfs/CMakeLists.txt index 1d8fb06ec0..0b2f640ca6 100644 --- a/components/vfs/CMakeLists.txt +++ b/components/vfs/CMakeLists.txt @@ -6,4 +6,4 @@ register_component() # Some newlib syscalls are implemented in vfs.c, make sure these are always # seen by the linker -target_link_libraries(${COMPONENT_LIB} "-u vfs_include_syscalls_impl") +target_link_libraries(${COMPONENT_LIB} INTERFACE "-u vfs_include_syscalls_impl") diff --git a/components/xtensa/CMakeLists.txt b/components/xtensa/CMakeLists.txt index 9044f14ae1..cb9c9ce49c 100644 --- a/components/xtensa/CMakeLists.txt +++ b/components/xtensa/CMakeLists.txt @@ -7,4 +7,4 @@ set(COMPONENT_PRIV_REQUIRES soc) register_component() -target_link_libraries(${COMPONENT_LIB} "${CMAKE_CURRENT_SOURCE_DIR}/${IDF_TARGET}/libhal.a") +target_link_libraries(${COMPONENT_LIB} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/${IDF_TARGET}/libhal.a") diff --git a/examples/build_system/cmake/import_lib/main/CMakeLists.txt b/examples/build_system/cmake/import_lib/main/CMakeLists.txt index 8111cbe6a3..fb136b8b97 100644 --- a/examples/build_system/cmake/import_lib/main/CMakeLists.txt +++ b/examples/build_system/cmake/import_lib/main/CMakeLists.txt @@ -23,4 +23,4 @@ endfunction() add_subdirectory(lib/tinyxml2) # Link tinyxml2 to main component -target_link_libraries(${COMPONENT_LIB} tinyxml2) +target_link_libraries(${COMPONENT_LIB} PUBLIC tinyxml2) diff --git a/tools/cmake/build.cmake b/tools/cmake/build.cmake index 6cf9a73b55..84f6f154fc 100644 --- a/tools/cmake/build.cmake +++ b/tools/cmake/build.cmake @@ -436,29 +436,8 @@ endmacro() # generating additional binary files, generating files related to flashing, etc.) function(idf_build_executable elf) # Propagate link dependencies from component library targets to the executable - idf_build_get_property(build_components BUILD_COMPONENTS) - foreach(build_component ${build_components}) - get_target_property(type ${build_component} TYPE) - if(type STREQUAL "INTERFACE_LIBRARY") - get_target_property(iface_link_depends ${build_component} INTERFACE_LINK_DEPENDS) - else() - get_target_property(link_depends ${build_component} LINK_DEPENDS) - get_target_property(iface_link_depends ${build_component} INTERFACE_LINK_DEPENDS) - endif() - if(iface_link_depends) - list(APPEND _link_depends ${iface_link_depends}) - endif() - if(link_depends) - list(APPEND _link_depends ${link_depends}) - endif() - endforeach() - - idf_build_get_property(link_depends LINK_DEPENDS) - if(link_depends) - list(APPEND _link_depends ${link_depends}) - endif() - - set_property(TARGET ${elf} APPEND PROPERTY LINK_DEPENDS "${_link_depends}") + idf_build_get_property(link_depends __LINK_DEPENDS) + set_property(TARGET ${elf} APPEND PROPERTY LINK_DEPENDS "${link_depends}") # Set the EXECUTABLE_NAME and EXECUTABLE properties since there are generator expression # from components that depend on it diff --git a/tools/cmake/component.cmake b/tools/cmake/component.cmake index 6caca93a74..22b3528ec6 100644 --- a/tools/cmake/component.cmake +++ b/tools/cmake/component.cmake @@ -288,6 +288,39 @@ macro(__component_check_target) endif() endmacro() +# __component_set_dependencies, __component_set_all_dependencies +# +# Links public and private requirements for the currently processed component +macro(__component_set_dependencies reqs type) + foreach(req ${reqs}) + if(req IN_LIST build_component_targets) + __component_get_property(req_lib ${req} COMPONENT_LIB) + target_link_libraries(${component_lib} ${type} ${req_lib}) + endif() + endforeach() +endmacro() + +macro(__component_set_all_dependencies) + __component_get_property(type ${component_target} COMPONENT_TYPE) + idf_build_get_property(build_component_targets __BUILD_COMPONENT_TARGETS) + + if(NOT type STREQUAL CONFIG_ONLY) + __component_get_property(reqs ${component_target} __REQUIRES) + __component_set_dependencies("${reqs}" PUBLIC) + + __component_get_property(priv_reqs ${component_target} __PRIV_REQUIRES) + __component_set_dependencies("${priv_reqs}" PRIVATE) + else() + __component_get_property(reqs ${component_target} __REQUIRES) + foreach(req ${reqs}) + if(req IN_LIST build_component_targets) + __component_get_property(req_lib ${req} COMPONENT_LIB) + target_link_libraries(${component_lib} INTERFACE ${req_lib}) + endif() + endforeach() + endif() +endmacro() + # idf_component_get_property # # @brief Retrieve the value of the specified component property @@ -331,6 +364,7 @@ function(idf_component_set_property component property val) endif() endfunction() + # idf_component_register # # @brief Register a component to the build, creating component library targets etc. @@ -431,6 +465,9 @@ function(idf_component_register) __ldgen_add_fragment_files("${__LDFRAGMENTS}") endif() + # Set dependencies + __component_set_all_dependencies() + # Add the component to built components idf_build_set_property(__BUILD_COMPONENTS ${component_lib} APPEND) idf_build_set_property(BUILD_COMPONENTS ${component_alias} APPEND) diff --git a/tools/cmake/ldgen.cmake b/tools/cmake/ldgen.cmake index b8cfaad499..27db72ae36 100644 --- a/tools/cmake/ldgen.cmake +++ b/tools/cmake/ldgen.cmake @@ -75,5 +75,5 @@ function(__ldgen_process_template template output) get_filename_component(_name ${output} NAME) add_custom_target(__ldgen_output_${_name} DEPENDS ${output}) add_dependencies(__idf_build_target __ldgen_output_${_name}) - idf_build_set_property(LINK_DEPENDS ${output} APPEND) + idf_build_set_property(__LINK_DEPENDS ${output} APPEND) endfunction() \ No newline at end of file diff --git a/tools/cmake/utilities.cmake b/tools/cmake/utilities.cmake index f5f7f9693c..83403d9ce9 100644 --- a/tools/cmake/utilities.cmake +++ b/tools/cmake/utilities.cmake @@ -130,7 +130,7 @@ endfunction() # Automatically adds a -L search path for the containing directory (if found), # and then adds -T with the filename only. This allows INCLUDE directives to be # used to include other linker scripts in the same directory. -function(target_linker_script target scriptfiles) +function(target_linker_script target deptype scriptfiles) cmake_parse_arguments(_ "" "PROCESS" "" ${ARGN}) foreach(scriptfile ${scriptfiles}) get_filename_component(abs_script "${scriptfile}" ABSOLUTE) @@ -145,12 +145,7 @@ function(target_linker_script target scriptfiles) get_filename_component(search_dir "${abs_script}" DIRECTORY) get_filename_component(scriptname "${abs_script}" NAME) - get_target_property(type ${target} TYPE) - if(type STREQUAL "INTERFACE_LIBRARY") - set(is_interface "INTERFACE") - endif() - - if(is_interface) + if(deptype STREQUAL INTERFACE OR deptype STREQUAL PUBLIC) get_target_property(link_libraries "${target}" INTERFACE_LINK_LIBRARIES) else() get_target_property(link_libraries "${target}" LINK_LIBRARIES) @@ -158,10 +153,10 @@ function(target_linker_script target scriptfiles) list(FIND "${link_libraries}" "-L ${search_dir}" found_search_dir) if(found_search_dir EQUAL "-1") # not already added as a search path - target_link_libraries("${target}" "${is_interface}" "-L ${search_dir}") + target_link_libraries("${target}" "${deptype}" "-L ${search_dir}") endif() - target_link_libraries("${target}" "${is_interface}" "-T ${scriptname}") + target_link_libraries("${target}" "${deptype}" "-T ${scriptname}") # Note: In ESP-IDF, most targets are libraries and libary LINK_DEPENDS don't propagate to # executable(s) the library is linked to. Attach manually to executable once it is known. @@ -169,11 +164,7 @@ function(target_linker_script target scriptfiles) # Property INTERFACE_LINK_DEPENDS is available in CMake 3.13 which should propagate link # dependencies. if(NOT __PROCESS) - if(is_interface) - set_property(TARGET ${target} APPEND PROPERTY INTERFACE_LINK_DEPENDS ${abs_script}) - else() - set_property(TARGET ${target} APPEND PROPERTY LINK_DEPENDS ${abs_script}) - endif() + idf_build_set_property(__LINK_DEPENDS ${abs_script} APPEND) endif() endforeach() endfunction() From 54ef60f26b15d92622c2e51bc4b5c9df96421e01 Mon Sep 17 00:00:00 2001 From: Renz Christian Bagaporo Date: Tue, 4 Jun 2019 20:32:43 +0800 Subject: [PATCH 031/486] component: revert some of the dependency corrections !4452 and !4897 made some ill-advised corrections to dependency info; revert those in this MR. Handling pre-built binaries as imported libraries is retained, however. --- components/bootloader_support/CMakeLists.txt | 8 ++++---- components/esp32/CMakeLists.txt | 4 ++-- components/esp_wifi/CMakeLists.txt | 3 +-- components/libsodium/CMakeLists.txt | 2 +- components/protocomm/CMakeLists.txt | 3 +-- components/spi_flash/CMakeLists.txt | 3 +-- 6 files changed, 10 insertions(+), 13 deletions(-) diff --git a/components/bootloader_support/CMakeLists.txt b/components/bootloader_support/CMakeLists.txt index d1373be307..2437829d54 100644 --- a/components/bootloader_support/CMakeLists.txt +++ b/components/bootloader_support/CMakeLists.txt @@ -9,8 +9,8 @@ set(COMPONENT_SRCS "src/bootloader_clock.c" if(BOOTLOADER_BUILD) set(COMPONENT_ADD_INCLUDEDIRS "include include_bootloader") - set(COMPONENT_REQUIRES spi_flash soc) #unfortunately the header directly uses SOC registers - set(COMPONENT_PRIV_REQUIRES micro-ecc efuse) + set(COMPONENT_REQUIRES soc) #unfortunately the header directly uses SOC registers + set(COMPONENT_PRIV_REQUIRES micro-ecc spi_flash efuse) list(APPEND COMPONENT_SRCS "src/bootloader_init.c" "src/${IDF_TARGET}/bootloader_sha.c" "src/${IDF_TARGET}/flash_encrypt.c" @@ -55,8 +55,8 @@ else() "src/idf/secure_boot_signatures.c") set(COMPONENT_ADD_INCLUDEDIRS "include") set(COMPONENT_PRIV_INCLUDEDIRS "include_bootloader") - set(COMPONENT_REQUIRES mbedtls soc) #unfortunately the header directly uses SOC registers - set(COMPONENT_PRIV_REQUIRES spi_flash efuse) + set(COMPONENT_REQUIRES soc) #unfortunately the header directly uses SOC registers + set(COMPONENT_PRIV_REQUIRES spi_flash mbedtls efuse) endif() register_component() \ No newline at end of file diff --git a/components/esp32/CMakeLists.txt b/components/esp32/CMakeLists.txt index 89b344b25e..abcc0ada17 100644 --- a/components/esp32/CMakeLists.txt +++ b/components/esp32/CMakeLists.txt @@ -34,12 +34,12 @@ else() "task_wdt.c") set(COMPONENT_ADD_INCLUDEDIRS "include") - set(COMPONENT_REQUIRES app_update driver esp_event efuse pthread soc) #unfortunately rom/uart uses SOC registers directly + set(COMPONENT_REQUIRES driver esp_event efuse soc) #unfortunately rom/uart uses SOC registers directly # driver is a public requirement because esp_sleep.h uses gpio_num_t & touch_pad_t # app_update is added here because cpu_start.c uses esp_ota_get_app_description() function. set(COMPONENT_PRIV_REQUIRES - app_trace app_update bootloader_support log mbedtls nvs_flash + app_trace app_update bootloader_support log mbedtls nvs_flash pthread smartconfig_ack spi_flash vfs wpa_supplicant espcoredump esp_common esp_wifi) set(COMPONENT_ADD_LDFRAGMENTS linker.lf ld/esp32_fragments.lf) diff --git a/components/esp_wifi/CMakeLists.txt b/components/esp_wifi/CMakeLists.txt index 0041234312..c8f31cd53c 100644 --- a/components/esp_wifi/CMakeLists.txt +++ b/components/esp_wifi/CMakeLists.txt @@ -8,8 +8,7 @@ set(COMPONENT_SRCS "src/wifi_init.c") set(COMPONENT_ADD_INCLUDEDIRS "include") set(COMPONENT_PRIV_INCLUDEDIRS) -set(COMPONENT_REQUIRES wpa_supplicant smartconfig_ack) -set(COMPONENT_PRIV_REQUIRES "nvs_flash") +set(COMPONENT_PRIV_REQUIRES wpa_supplicant nvs_flash) if(NOT CONFIG_ESP32_NO_BLOBS) set(COMPONENT_ADD_LDFRAGMENTS "linker.lf") diff --git a/components/libsodium/CMakeLists.txt b/components/libsodium/CMakeLists.txt index f0d5afc9da..caef522233 100644 --- a/components/libsodium/CMakeLists.txt +++ b/components/libsodium/CMakeLists.txt @@ -126,7 +126,7 @@ endif() set(COMPONENT_ADD_INCLUDEDIRS ${SRC}/include port_include) set(COMPONENT_PRIV_INCLUDEDIRS ${SRC}/include/sodium port_include/sodium port) -set(COMPONENT_REQUIRES mbedtls vfs) +set(COMPONENT_REQUIRES mbedtls) register_component() target_compile_definitions(${COMPONENT_LIB} PRIVATE diff --git a/components/protocomm/CMakeLists.txt b/components/protocomm/CMakeLists.txt index 79e824ceb6..9f9343815f 100644 --- a/components/protocomm/CMakeLists.txt +++ b/components/protocomm/CMakeLists.txt @@ -12,8 +12,7 @@ set(COMPONENT_SRCS "src/common/protocomm.c" "src/transports/protocomm_console.c" "src/transports/protocomm_httpd.c") -set(COMPONENT_REQUIRES protobuf-c bt) -set(COMPONENT_PRIV_REQUIRES mbedtls console esp_http_server) +set(COMPONENT_PRIV_REQUIRES protobuf-c mbedtls console esp_http_server bt) if(CONFIG_BT_ENABLED) if(CONFIG_BT_BLUEDROID_ENABLED) diff --git a/components/spi_flash/CMakeLists.txt b/components/spi_flash/CMakeLists.txt index db8fceee0e..be290e7c32 100644 --- a/components/spi_flash/CMakeLists.txt +++ b/components/spi_flash/CMakeLists.txt @@ -9,8 +9,7 @@ else() "flash_ops.c" "partition.c" "spi_flash_rom_patch.c") - set(COMPONENT_REQUIRES app_update) - set(COMPONENT_PRIV_REQUIRES bootloader_support soc) + set(COMPONENT_PRIV_REQUIRES bootloader_support app_update soc) endif() set(COMPONENT_ADD_INCLUDEDIRS include) From 517c61a4ec2f2506a18de7aa4e4c0bce221dcba5 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 10 Jun 2019 23:29:18 +0800 Subject: [PATCH 032/486] docs: fix generation of toolchain links Closes https://github.com/espressif/esp-idf/issues/3609 --- docs/gen-toolchain-links.py | 41 ++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/docs/gen-toolchain-links.py b/docs/gen-toolchain-links.py index ea90f47c70..8858174384 100644 --- a/docs/gen-toolchain-links.py +++ b/docs/gen-toolchain-links.py @@ -9,6 +9,15 @@ from __future__ import print_function import sys import os +from collections import namedtuple + +PlatformInfo = namedtuple("PlatformInfo", [ + "platform_name", + "platform_archive_suffix", + "extension", + "unpack_cmd", + "unpack_code" +]) def main(): @@ -44,35 +53,33 @@ def main(): scratch_build_code_linux_macos = """ :: - git clone -b xtensa-1.22.x https://github.com/espressif/crosstool-NG.git + git clone https://github.com/espressif/crosstool-NG.git cd crosstool-NG + git checkout {} ./bootstrap && ./configure --enable-local && make install """ - platform_info = [["linux64", "tar.gz", "z", unpack_code_linux_macos], - ["linux32", "tar.gz", "z", unpack_code_linux_macos], - ["osx", "tar.gz", "z", unpack_code_linux_macos], - ["win32", "zip", None, None]] + platform_info = [ + PlatformInfo("linux64", "linux-amd64", "tar.gz", "z", unpack_code_linux_macos), + PlatformInfo("linux32", "linux-i686","tar.gz", "z", unpack_code_linux_macos), + PlatformInfo("osx", "macos", "tar.gz", "z", unpack_code_linux_macos), + PlatformInfo("win32", "win32", "zip", None, None) + ] with open(os.path.join(out_dir, 'download-links.inc'), "w") as links_file: for p in platform_info: - platform_name = p[0] - extension = p[1] - unpack_cmd = p[2] - unpack_code = p[3] - - archive_name = 'xtensa-esp32-elf-{}-{}-{}.{}'.format( - platform_name, toolchain_desc, gcc_version, extension) + archive_name = 'xtensa-esp32-elf-gcc{}-{}-{}.{}'.format( + gcc_version.replace('.', '_'), toolchain_desc, p.platform_archive_suffix, p.extension) print('.. |download_link_{}| replace:: {}{}'.format( - platform_name, base_url, archive_name), file=links_file) + p.platform_name, base_url, archive_name), file=links_file) - if unpack_code is not None: - with open(os.path.join(out_dir, 'unpack-code-%s.inc' % platform_name), "w") as f: - print(unpack_code.format(unpack_cmd, archive_name), file=f) + if p.unpack_code is not None: + with open(os.path.join(out_dir, 'unpack-code-%s.inc' % p.platform_name), "w") as f: + print(p.unpack_code.format(p.unpack_cmd, archive_name), file=f) with open(os.path.join(out_dir, 'scratch-build-code.inc'), "w") as code_file: - print(scratch_build_code_linux_macos, file=code_file) + print(scratch_build_code_linux_macos.format(toolchain_desc), file=code_file) if __name__ == "__main__": From 67148e37a61a7e168ea324d70c17b373c4639e9f Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 11 Jun 2019 21:58:03 +0800 Subject: [PATCH 033/486] tools/windows: update toolchain URL in MSYS build script --- tools/windows/windows_install_prerequisites.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/windows/windows_install_prerequisites.sh b/tools/windows/windows_install_prerequisites.sh index f75f54eecb..993931081a 100644 --- a/tools/windows/windows_install_prerequisites.sh +++ b/tools/windows/windows_install_prerequisites.sh @@ -42,10 +42,11 @@ if [ -n "$IDF_PATH" ]; then fi # Automatically download precompiled toolchain, unpack at /opt/xtensa-esp32-elf/ -TOOLCHAIN_ZIP=xtensa-esp32-elf-win32-1.22.0-80-g6c4433a-5.2.0.zip +TOOLCHAIN_ZIP=xtensa-esp32-elf-gcc8_2_0-esp32-2019r1-win32.zip echo "Downloading precompiled toolchain ${TOOLCHAIN_ZIP}..." cd ~ curl -LO --retry 10 http://dl.espressif.com/dl/${TOOLCHAIN_ZIP} +mkdir -p /opt cd /opt rm -rf /opt/xtensa-esp32-elf # for upgrades unzip ~/${TOOLCHAIN_ZIP} From 0e6ffd08b7416ef4e05ff8d1bf2d3ae5389e4ace Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 11 Jun 2019 22:22:14 +0800 Subject: [PATCH 034/486] docs: update msys environment links for esp32-2019r1 toolchain --- docs/en/get-started/windows-setup.rst | 2 +- docs/zh_CN/get-started/windows-setup.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/en/get-started/windows-setup.rst b/docs/en/get-started/windows-setup.rst index ad7d821011..d2a2678283 100644 --- a/docs/en/get-started/windows-setup.rst +++ b/docs/en/get-started/windows-setup.rst @@ -14,7 +14,7 @@ Toolchain Setup The quick setup is to download the Windows all-in-one toolchain & MSYS2 zip file from dl.espressif.com: -https://dl.espressif.com/dl/esp32_win32_msys2_environment_and_toolchain-20181001.zip +https://dl.espressif.com/dl/esp32_win32_msys2_environment_and_toolchain-20190611.zip Unzip the zip file to ``C:\`` (or some other location, but this guide assumes ``C:\``) and it will create an ``msys32`` directory with a pre-prepared environment. diff --git a/docs/zh_CN/get-started/windows-setup.rst b/docs/zh_CN/get-started/windows-setup.rst index 348f5f6e77..3c02077bd3 100644 --- a/docs/zh_CN/get-started/windows-setup.rst +++ b/docs/zh_CN/get-started/windows-setup.rst @@ -15,7 +15,7 @@ Windows 没有内置的 "make" 环境,因此如果要安装工具链,你需 快速设置的方法是从 dl.espressif.com 下载集成在一起的工具链和 MSYS2 压缩文件: -https://dl.espressif.com/dl/esp32_win32_msys2_environment_and_toolchain-20181001.zip +https://dl.espressif.com/dl/esp32_win32_msys2_environment_and_toolchain-20190611.zip 将 zip 压缩文件解压到 ``C:\`` (或其它路径,这里假设是 ``C:\``),它会使用预先准备的环境创建一个 ``msys32`` 目录。 From 87aa341e9758528f8536c9d86f1719e5fe629587 Mon Sep 17 00:00:00 2001 From: Renz Christian Bagaporo Date: Fri, 5 Apr 2019 10:42:36 +0800 Subject: [PATCH 035/486] ldgen: mapping rules should be grouped by archive --- tools/ldgen/generation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/ldgen/generation.py b/tools/ldgen/generation.py index 0c05cca1a7..b411c56b4b 100644 --- a/tools/ldgen/generation.py +++ b/tools/ldgen/generation.py @@ -345,7 +345,7 @@ class GenerationModel: message = GenerationException.UNDEFINED_REFERENCE + " to scheme '" + scheme_name + "'." raise GenerationException(message, mapping) - all_mapping_rules[mapping.name] = mapping_rules + all_mapping_rules[mapping.archive] = mapping_rules # Detect rule conflicts for mapping_rules in all_mapping_rules.items(): From 8ee90ee2f108413fe98b5902814561409d91e981 Mon Sep 17 00:00:00 2001 From: Renz Christian Bagaporo Date: Fri, 5 Apr 2019 14:32:21 +0800 Subject: [PATCH 036/486] ldgen: allow multiple mapping fragments to map same library --- tools/ldgen/fragments.py | 2 + tools/ldgen/generation.py | 31 +++++---- tools/ldgen/test/test_generation.py | 103 ++++++++++++++++++++++++++++ 3 files changed, 121 insertions(+), 15 deletions(-) diff --git a/tools/ldgen/fragments.py b/tools/ldgen/fragments.py index dd8295f869..0339f5849e 100644 --- a/tools/ldgen/fragments.py +++ b/tools/ldgen/fragments.py @@ -279,6 +279,7 @@ class Mapping(Fragment): def __init__(self): Fragment.__init__(self) self.entries = set() + self.deprecated = False def set_key_value(self, key, parse_results): if key == "archive": @@ -382,6 +383,7 @@ class DeprecatedMapping(): fragment = Mapping() fragment.archive = toks[0].archive fragment.name = re.sub(r"[^0-9a-zA-Z]+", "_", fragment.archive) + fragment.deprecated = True fragment.entries = set() condition_true = False diff --git a/tools/ldgen/generation.py b/tools/ldgen/generation.py index b411c56b4b..686defc3aa 100644 --- a/tools/ldgen/generation.py +++ b/tools/ldgen/generation.py @@ -334,8 +334,8 @@ class GenerationModel: # Generate rules based on mapping fragments for mapping in self.mappings.values(): - mapping_rules = list() archive = mapping.archive + mapping_rules = all_mapping_rules[archive] for (obj, symbol, scheme_name) in mapping.entries: try: if not (obj == Mapping.MAPPING_ALL_OBJECTS and symbol is None and @@ -345,8 +345,6 @@ class GenerationModel: message = GenerationException.UNDEFINED_REFERENCE + " to scheme '" + scheme_name + "'." raise GenerationException(message, mapping) - all_mapping_rules[mapping.archive] = mapping_rules - # Detect rule conflicts for mapping_rules in all_mapping_rules.items(): self._detect_conflicts(mapping_rules) @@ -453,21 +451,24 @@ class GenerationModel: for fragment in fragment_file.fragments: dict_to_append_to = None - if isinstance(fragment, Scheme): - dict_to_append_to = self.schemes - elif isinstance(fragment, Sections): - dict_to_append_to = self.sections + if isinstance(fragment, Mapping) and fragment.deprecated and fragment.name in self.mappings.keys(): + self.mappings[fragment.name].entries |= fragment.entries else: - dict_to_append_to = self.mappings + if isinstance(fragment, Scheme): + dict_to_append_to = self.schemes + elif isinstance(fragment, Sections): + dict_to_append_to = self.sections + else: + dict_to_append_to = self.mappings - # Raise exception when the fragment of the same type is already in the stored fragments - if fragment.name in dict_to_append_to.keys(): - stored = dict_to_append_to[fragment.name].path - new = fragment.path - message = "Duplicate definition of fragment '%s' found in %s and %s." % (fragment.name, stored, new) - raise GenerationException(message) + # Raise exception when the fragment of the same type is already in the stored fragments + if fragment.name in dict_to_append_to.keys(): + stored = dict_to_append_to[fragment.name].path + new = fragment.path + message = "Duplicate definition of fragment '%s' found in %s and %s." % (fragment.name, stored, new) + raise GenerationException(message) - dict_to_append_to[fragment.name] = fragment + dict_to_append_to[fragment.name] = fragment class TemplateModel: diff --git a/tools/ldgen/test/test_generation.py b/tools/ldgen/test/test_generation.py index 48f7742f75..ec5de67283 100755 --- a/tools/ldgen/test/test_generation.py +++ b/tools/ldgen/test/test_generation.py @@ -1246,6 +1246,109 @@ entries: self.compare_rules(expected, actual) + def test_rule_generation_multiple_deprecated_mapping_definitions(self): + multiple_deprecated_definitions = u""" +[mapping] +archive: lib.a +entries: + : PERFORMANCE_LEVEL = 0 + : PERFORMANCE_LEVEL = 1 + obj1 (noflash) + : PERFORMANCE_LEVEL = 2 + obj1 (noflash) + : PERFORMANCE_LEVEL = 3 + obj1 (noflash) + +[mapping] +archive: lib.a +entries: + : PERFORMANCE_LEVEL = 1 + obj1 (noflash) # ignore duplicate definition + : PERFORMANCE_LEVEL = 2 + obj2 (noflash) + : PERFORMANCE_LEVEL = 3 + obj2 (noflash) + obj3 (noflash) +""" + + for perf_level in range(0, 4): + self.sdkconfig.config.syms["PERFORMANCE_LEVEL"].set_value(str(perf_level)) + + self.model.mappings = {} + self.add_fragments(multiple_deprecated_definitions) + + actual = self.model.generate_rules(self.sections_info) + expected = self.generate_default_rules() + + if perf_level < 4: + for append_no in range(1, perf_level + 1): + flash_text_default = self.get_default("flash_text", expected) + flash_rodata_default = self.get_default("flash_rodata", expected) + + iram_rule = PlacementRule("lib.a", "obj" + str(append_no), None, self.model.sections["text"].entries, "iram0_text") + dram_rule = PlacementRule("lib.a", "obj" + str(append_no), None, self.model.sections["rodata"].entries, "dram0_data") + + flash_text_default.add_exclusion(iram_rule) + flash_rodata_default.add_exclusion(dram_rule) + + expected["iram0_text"].append(iram_rule) + expected["dram0_data"].append(dram_rule) + + self.compare_rules(expected, actual) + + def test_rule_generation_multiple_mapping_definitions(self): + multiple_deprecated_definitions = u""" +[mapping:base] +archive: lib.a +entries: + if PERFORMANCE_LEVEL = 1: + obj1 (noflash) + elif PERFORMANCE_LEVEL = 2: + obj1 (noflash) + elif PERFORMANCE_LEVEL = 3: + obj1 (noflash) + else: + * (default) + +[mapping:extra] +archive: lib.a +entries: + if PERFORMANCE_LEVEL = 1: + obj1 (noflash) # ignore duplicate definition + elif PERFORMANCE_LEVEL = 2: + obj2 (noflash) + elif PERFORMANCE_LEVEL = 3: + obj2 (noflash) + obj3 (noflash) + else: + * (default) +""" + + for perf_level in range(0, 4): + self.sdkconfig.config.syms["PERFORMANCE_LEVEL"].set_value(str(perf_level)) + + self.model.mappings = {} + self.add_fragments(multiple_deprecated_definitions) + + actual = self.model.generate_rules(self.sections_info) + expected = self.generate_default_rules() + + if perf_level < 4: + for append_no in range(1, perf_level + 1): + flash_text_default = self.get_default("flash_text", expected) + flash_rodata_default = self.get_default("flash_rodata", expected) + + iram_rule = PlacementRule("lib.a", "obj" + str(append_no), None, self.model.sections["text"].entries, "iram0_text") + dram_rule = PlacementRule("lib.a", "obj" + str(append_no), None, self.model.sections["rodata"].entries, "dram0_data") + + flash_text_default.add_exclusion(iram_rule) + flash_rodata_default.add_exclusion(dram_rule) + + expected["iram0_text"].append(iram_rule) + expected["dram0_data"].append(dram_rule) + + self.compare_rules(expected, actual) + if __name__ == "__main__": unittest.main() From 7edef74347ed4901c143b2d6960aee5504b87cae Mon Sep 17 00:00:00 2001 From: Renz Christian Bagaporo Date: Tue, 11 Jun 2019 12:06:04 +0800 Subject: [PATCH 037/486] cmake: set variables set by project call ESP-IDF overrides project() definition for user convenience. This redefinition lacks setting the variables documented at the project command documentation https://cmake.org/cmake/help/v3.5/command/project.html in the parent scope. This commit sets those variables. Closes https://github.com/espressif/esp-idf/issues/3611. --- tools/cmake/project.cmake | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tools/cmake/project.cmake b/tools/cmake/project.cmake index 754e4a2fd4..b048b0d25c 100644 --- a/tools/cmake/project.cmake +++ b/tools/cmake/project.cmake @@ -264,6 +264,31 @@ macro(project project_name) function(project) set(project_ARGV ARGV) __project(${${project_ARGV}}) + + # Set the variables that project() normally sets, documented in the + # command's docs. + # + # https://cmake.org/cmake/help/v3.5/command/project.html + # + # There is some nuance when it comes to setting version variables in terms of whether + # CMP0048 is set to OLD or NEW. However, the proper behavior should have bee already handled by the original + # project call, and we're just echoing the values those variables were set to. + set(PROJECT_NAME "${PROJECT_NAME}" PARENT_SCOPE) + set(PROJECT_BINARY_DIR "${PROJECT_BINARY_DIR}" PARENT_SCOPE) + set(PROJECT_SOURCE_DIR "${PROJECT_SOURCE_DIR}" PARENT_SCOPE) + set(PROJECT_VERSION "${PROJECT_VERSION}" PARENT_SCOPE) + set(PROJECT_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}" PARENT_SCOPE) + set(PROJECT_VERSION_MINOR "${PROJECT_VERSION_MINOR}" PARENT_SCOPE) + set(PROJECT_VERSION_PATCH "${PROJECT_VERSION_PATCH}" PARENT_SCOPE) + set(PROJECT_VERSION_TWEAK "${PROJECT_VERSION_TWEAK}" PARENT_SCOPE) + + set(${PROJECT_NAME}_BINARY_DIR "${${PROJECT_NAME}_BINARY_DIR}" PARENT_SCOPE) + set(${PROJECT_NAME}_SOURCE_DIR "${${PROJECT_NAME}_SOURCE_DIR}" PARENT_SCOPE) + set(${PROJECT_NAME}_VERSION "${${PROJECT_NAME}_VERSION}" PARENT_SCOPE) + set(${PROJECT_NAME}_VERSION_MAJOR "${${PROJECT_NAME}_VERSION_MAJOR}" PARENT_SCOPE) + set(${PROJECT_NAME}_VERSION_MINOR "${${PROJECT_NAME}_VERSION_MINOR}" PARENT_SCOPE) + set(${PROJECT_NAME}_VERSION_PATCH "${${PROJECT_NAME}_VERSION_PATCH}" PARENT_SCOPE) + set(${PROJECT_NAME}_VERSION_TWEAK "${${PROJECT_NAME}_VERSION_TWEAK}" PARENT_SCOPE) endfunction() # Prepare the following arguments for the idf_build_process() call using external From ae675973beacdb250900c066510d3bac806c6f02 Mon Sep 17 00:00:00 2001 From: Sachin Parekh Date: Mon, 15 Apr 2019 15:20:37 +0530 Subject: [PATCH 038/486] optimize: IGMP and MLD6 timers on demand config added Signed-off-by: Sachin Parekh --- components/lwip/Kconfig | 10 ++++++++++ components/lwip/lwip | 2 +- components/lwip/port/esp32/include/lwipopts.h | 8 ++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/components/lwip/Kconfig b/components/lwip/Kconfig index 6ccbdc113e..6fa8a507e2 100644 --- a/components/lwip/Kconfig +++ b/components/lwip/Kconfig @@ -27,6 +27,16 @@ menu "LWIP" If this feature is disabled, all lwip functions will be put into FLASH. + config LWIP_TIMERS_ONDEMAND + bool "Enable LWIP Timers on demand" + default y + help + If this feature is enabled, IGMP and MLD6 timers will be activated only + when joining groups or receiving QUERY packets. + + This feature will reduce the power consumption for applications which do not + use IGMP and MLD6. + config LWIP_MAX_SOCKETS int "Max number of open sockets" range 1 16 diff --git a/components/lwip/lwip b/components/lwip/lwip index fe9a58cb7a..88ccba0ccf 160000 --- a/components/lwip/lwip +++ b/components/lwip/lwip @@ -1 +1 @@ -Subproject commit fe9a58cb7abdc74e7a2196b7c4e0a29ff9042f88 +Subproject commit 88ccba0ccfc11cd968b80a9d50b3182972da9604 diff --git a/components/lwip/port/esp32/include/lwipopts.h b/components/lwip/port/esp32/include/lwipopts.h index 6f3f279e02..fd6e1262f8 100644 --- a/components/lwip/port/esp32/include/lwipopts.h +++ b/components/lwip/port/esp32/include/lwipopts.h @@ -779,6 +779,14 @@ #endif #define ESP_IRAM_ATTR +#ifdef CONFIG_LWIP_TIMERS_ONDEMAND +#define ESP_LWIP_IGMP_TIMERS_ONDEMAND 1 +#define ESP_LWIP_MLD6_TIMERS_ONDEMAND 1 +#else +#define ESP_LWIP_IGMP_TIMERS_ONDEMAND 0 +#define ESP_LWIP_MLD6_TIMERS_ONDEMAND 0 +#endif + #if ESP_PERF #define DBG_PERF_PATH_SET(dir, point) #define DBG_PERF_FILTER_LEN 1000 From a75be3413e6251e17ce5efd7581c04548d2ac2c1 Mon Sep 17 00:00:00 2001 From: Anurag Kar Date: Mon, 10 Jun 2019 17:49:30 +0530 Subject: [PATCH 039/486] Wi-Fi Provisioning : Bugfix in copying SSID and Passphrase These changes guarantee that the SSID and Passphrase received via protocomm are NULL terminated and size limited to their standard lengths. List of changes: * Corrected length of passphrase field in wifi_prov_config_set_data_t structure * Performing length checks on SSID, passphrase and bssid, when populating wifi_prov_config_set_data_t structure with received credentials --- .../include/wifi_provisioning/wifi_config.h | 2 +- .../wifi_provisioning/src/wifi_config.c | 41 ++++++++++++++----- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/components/wifi_provisioning/include/wifi_provisioning/wifi_config.h b/components/wifi_provisioning/include/wifi_provisioning/wifi_config.h index 4058540adb..2fa64448d4 100644 --- a/components/wifi_provisioning/include/wifi_provisioning/wifi_config.h +++ b/components/wifi_provisioning/include/wifi_provisioning/wifi_config.h @@ -76,7 +76,7 @@ typedef struct { */ typedef struct { char ssid[33]; /*!< SSID of the AP to which the slave is to be connected */ - char password[65]; /*!< Password of the AP */ + char password[64]; /*!< Password of the AP */ char bssid[6]; /*!< BSSID of the AP */ uint8_t channel; /*!< Channel of the AP */ } wifi_prov_config_set_data_t; diff --git a/components/wifi_provisioning/src/wifi_config.c b/components/wifi_provisioning/src/wifi_config.c index 09ccc37d53..93e3e85643 100644 --- a/components/wifi_provisioning/src/wifi_config.c +++ b/components/wifi_provisioning/src/wifi_config.c @@ -151,15 +151,36 @@ static esp_err_t cmd_set_config_handler(WiFiConfigPayload *req, wifi_prov_config_set_data_t req_data; memset(&req_data, 0, sizeof(req_data)); - memcpy(req_data.ssid, req->cmd_set_config->ssid.data, - req->cmd_set_config->ssid.len); - memcpy(req_data.password, req->cmd_set_config->passphrase.data, - req->cmd_set_config->passphrase.len); - memcpy(req_data.bssid, req->cmd_set_config->bssid.data, - req->cmd_set_config->bssid.len); - req_data.channel = req->cmd_set_config->channel; - if (h->set_config_handler(&req_data, &h->ctx) == ESP_OK) { - resp_payload->status = STATUS__Success; + + /* Check arguments provided in protobuf packet: + * - SSID / Passphrase string length must be within the standard limits + * - BSSID must either be NULL or have length equal to that imposed by the standard + * If any of these conditions are not satisfied, don't invoke the handler and + * send error status without closing connection */ + resp_payload->status = STATUS__InvalidArgument; + if (req->cmd_set_config->bssid.len != 0 && + req->cmd_set_config->bssid.len != sizeof(req_data.bssid)) { + ESP_LOGD(TAG, "Received invalid BSSID"); + } else if (req->cmd_set_config->ssid.len >= sizeof(req_data.ssid)) { + ESP_LOGD(TAG, "Received invalid SSID"); + } else if (req->cmd_set_config->passphrase.len >= sizeof(req_data.password)) { + ESP_LOGD(TAG, "Received invalid Passphrase"); + } else { + /* The received SSID and Passphrase are not NULL terminated so + * we memcpy over zeroed out arrays. Above length checks ensure + * that there is atleast 1 extra byte for null termination */ + memcpy(req_data.ssid, req->cmd_set_config->ssid.data, + req->cmd_set_config->ssid.len); + memcpy(req_data.password, req->cmd_set_config->passphrase.data, + req->cmd_set_config->passphrase.len); + memcpy(req_data.bssid, req->cmd_set_config->bssid.data, + req->cmd_set_config->bssid.len); + req_data.channel = req->cmd_set_config->channel; + if (h->set_config_handler(&req_data, &h->ctx) == ESP_OK) { + resp_payload->status = STATUS__Success; + } else { + resp_payload->status = STATUS__InternalError; + } } resp->payload_case = WI_FI_CONFIG_PAYLOAD__PAYLOAD_RESP_SET_CONFIG; @@ -188,7 +209,7 @@ static esp_err_t cmd_apply_config_handler(WiFiConfigPayload *req, if (h->apply_config_handler(&h->ctx) == ESP_OK) { resp_payload->status = STATUS__Success; } else { - resp_payload->status = STATUS__InvalidArgument; + resp_payload->status = STATUS__InternalError; } resp->payload_case = WI_FI_CONFIG_PAYLOAD__PAYLOAD_RESP_APPLY_CONFIG; From a8d19e6638ca83b11faf334b9f3e6d8811af9e19 Mon Sep 17 00:00:00 2001 From: Anurag Kar Date: Mon, 10 Jun 2019 17:23:53 +0530 Subject: [PATCH 040/486] Provisioning Examples : Bugfix in copying Wi-Fi SSID and Passphrase --- .../provisioning/ble_prov/main/app_prov_handlers.c | 12 ++++++++---- .../console_prov/main/app_prov_handlers.c | 12 ++++++++---- examples/provisioning/custom_config/main/app_prov.c | 4 ++-- .../custom_config/main/app_prov_handlers.c | 12 ++++++++---- examples/provisioning/softap_prov/main/app_prov.c | 4 ++-- .../softap_prov/main/app_prov_handlers.c | 12 ++++++++---- 6 files changed, 36 insertions(+), 20 deletions(-) diff --git a/examples/provisioning/ble_prov/main/app_prov_handlers.c b/examples/provisioning/ble_prov/main/app_prov_handlers.c index 4a0c0d99a7..05dc895dcc 100644 --- a/examples/provisioning/ble_prov/main/app_prov_handlers.c +++ b/examples/provisioning/ble_prov/main/app_prov_handlers.c @@ -98,10 +98,14 @@ static esp_err_t set_config_handler(const wifi_prov_config_set_data_t *req_data, ESP_LOGI(TAG, "WiFi Credentials Received : \n\tssid %s \n\tpassword %s", req_data->ssid, req_data->password); - memcpy((char *) wifi_cfg->sta.ssid, req_data->ssid, - strnlen(req_data->ssid, sizeof(wifi_cfg->sta.ssid))); - memcpy((char *) wifi_cfg->sta.password, req_data->password, - strnlen(req_data->password, sizeof(wifi_cfg->sta.password))); + + /* Using strncpy allows the max SSID length to be 32 bytes (as per 802.11 standard). + * But this doesn't guarantee that the saved SSID will be null terminated, because + * wifi_cfg->sta.ssid is also 32 bytes long (without extra 1 byte for null character). + * Although, this is not a matter for concern because esp_wifi library reads the SSID + * upto 32 bytes in absence of null termination */ + strncpy((char *) wifi_cfg->sta.ssid, req_data->ssid, sizeof(wifi_cfg->sta.ssid)); + strlcpy((char *) wifi_cfg->sta.password, req_data->password, sizeof(wifi_cfg->sta.password)); return ESP_OK; } diff --git a/examples/provisioning/console_prov/main/app_prov_handlers.c b/examples/provisioning/console_prov/main/app_prov_handlers.c index 49c29739f8..e3fa614962 100644 --- a/examples/provisioning/console_prov/main/app_prov_handlers.c +++ b/examples/provisioning/console_prov/main/app_prov_handlers.c @@ -98,10 +98,14 @@ static esp_err_t set_config_handler(const wifi_prov_config_set_data_t *req_data, ESP_LOGI(TAG, "WiFi Credentials Received : \n\tssid %s \n\tpassword %s", req_data->ssid, req_data->password); - memcpy((char *) wifi_cfg->sta.ssid, req_data->ssid, - strnlen(req_data->ssid, sizeof(wifi_cfg->sta.ssid))); - memcpy((char *) wifi_cfg->sta.password, req_data->password, - strnlen(req_data->password, sizeof(wifi_cfg->sta.password))); + + /* Using strncpy allows the max SSID length to be 32 bytes (as per 802.11 standard). + * But this doesn't guarantee that the saved SSID will be null terminated, because + * wifi_cfg->sta.ssid is also 32 bytes long (without extra 1 byte for null character). + * Although, this is not a matter for concern because esp_wifi library reads the SSID + * upto 32 bytes in absence of null termination */ + strncpy((char *) wifi_cfg->sta.ssid, req_data->ssid, sizeof(wifi_cfg->sta.ssid)); + strlcpy((char *) wifi_cfg->sta.password, req_data->password, sizeof(wifi_cfg->sta.password)); return ESP_OK; } diff --git a/examples/provisioning/custom_config/main/app_prov.c b/examples/provisioning/custom_config/main/app_prov.c index 4c30ac2186..5e0f68ea8e 100644 --- a/examples/provisioning/custom_config/main/app_prov.c +++ b/examples/provisioning/custom_config/main/app_prov.c @@ -303,13 +303,13 @@ static esp_err_t start_wifi_ap(const char *ssid, const char *pass) }; strncpy((char *) wifi_config.ap.ssid, ssid, sizeof(wifi_config.ap.ssid)); - wifi_config.ap.ssid_len = strlen(ssid); + wifi_config.ap.ssid_len = strnlen(ssid, sizeof(wifi_config.ap.ssid)); if (strlen(pass) == 0) { memset(wifi_config.ap.password, 0, sizeof(wifi_config.ap.password)); wifi_config.ap.authmode = WIFI_AUTH_OPEN; } else { - strncpy((char *) wifi_config.ap.password, pass, sizeof(wifi_config.ap.password)); + strlcpy((char *) wifi_config.ap.password, pass, sizeof(wifi_config.ap.password)); wifi_config.ap.authmode = WIFI_AUTH_WPA_WPA2_PSK; } diff --git a/examples/provisioning/custom_config/main/app_prov_handlers.c b/examples/provisioning/custom_config/main/app_prov_handlers.c index c67eeebca4..2f320890e0 100644 --- a/examples/provisioning/custom_config/main/app_prov_handlers.c +++ b/examples/provisioning/custom_config/main/app_prov_handlers.c @@ -110,10 +110,14 @@ static esp_err_t set_config_handler(const wifi_prov_config_set_data_t *req_data, ESP_LOGI(TAG, "WiFi Credentials Received : \n\tssid %s \n\tpassword %s", req_data->ssid, req_data->password); - memcpy((char *) wifi_cfg->sta.ssid, req_data->ssid, - strnlen(req_data->ssid, sizeof(wifi_cfg->sta.ssid))); - memcpy((char *) wifi_cfg->sta.password, req_data->password, - strnlen(req_data->password, sizeof(wifi_cfg->sta.password))); + + /* Using strncpy allows the max SSID length to be 32 bytes (as per 802.11 standard). + * But this doesn't guarantee that the saved SSID will be null terminated, because + * wifi_cfg->sta.ssid is also 32 bytes long (without extra 1 byte for null character). + * Although, this is not a matter for concern because esp_wifi library reads the SSID + * upto 32 bytes in absence of null termination */ + strncpy((char *) wifi_cfg->sta.ssid, req_data->ssid, sizeof(wifi_cfg->sta.ssid)); + strlcpy((char *) wifi_cfg->sta.password, req_data->password, sizeof(wifi_cfg->sta.password)); return ESP_OK; } diff --git a/examples/provisioning/softap_prov/main/app_prov.c b/examples/provisioning/softap_prov/main/app_prov.c index 35ad3edf8e..f7bca2df12 100644 --- a/examples/provisioning/softap_prov/main/app_prov.c +++ b/examples/provisioning/softap_prov/main/app_prov.c @@ -289,13 +289,13 @@ static esp_err_t start_wifi_ap(const char *ssid, const char *pass) }; strncpy((char *) wifi_config.ap.ssid, ssid, sizeof(wifi_config.ap.ssid)); - wifi_config.ap.ssid_len = strlen(ssid); + wifi_config.ap.ssid_len = strnlen(ssid, sizeof(wifi_config.ap.ssid)); if (strlen(pass) == 0) { memset(wifi_config.ap.password, 0, sizeof(wifi_config.ap.password)); wifi_config.ap.authmode = WIFI_AUTH_OPEN; } else { - strncpy((char *) wifi_config.ap.password, pass, sizeof(wifi_config.ap.password)); + strlcpy((char *) wifi_config.ap.password, pass, sizeof(wifi_config.ap.password)); wifi_config.ap.authmode = WIFI_AUTH_WPA_WPA2_PSK; } diff --git a/examples/provisioning/softap_prov/main/app_prov_handlers.c b/examples/provisioning/softap_prov/main/app_prov_handlers.c index 4a0c0d99a7..05dc895dcc 100644 --- a/examples/provisioning/softap_prov/main/app_prov_handlers.c +++ b/examples/provisioning/softap_prov/main/app_prov_handlers.c @@ -98,10 +98,14 @@ static esp_err_t set_config_handler(const wifi_prov_config_set_data_t *req_data, ESP_LOGI(TAG, "WiFi Credentials Received : \n\tssid %s \n\tpassword %s", req_data->ssid, req_data->password); - memcpy((char *) wifi_cfg->sta.ssid, req_data->ssid, - strnlen(req_data->ssid, sizeof(wifi_cfg->sta.ssid))); - memcpy((char *) wifi_cfg->sta.password, req_data->password, - strnlen(req_data->password, sizeof(wifi_cfg->sta.password))); + + /* Using strncpy allows the max SSID length to be 32 bytes (as per 802.11 standard). + * But this doesn't guarantee that the saved SSID will be null terminated, because + * wifi_cfg->sta.ssid is also 32 bytes long (without extra 1 byte for null character). + * Although, this is not a matter for concern because esp_wifi library reads the SSID + * upto 32 bytes in absence of null termination */ + strncpy((char *) wifi_cfg->sta.ssid, req_data->ssid, sizeof(wifi_cfg->sta.ssid)); + strlcpy((char *) wifi_cfg->sta.password, req_data->password, sizeof(wifi_cfg->sta.password)); return ESP_OK; } From a012025deb7e42703a2caa54021dafdebc84b6c6 Mon Sep 17 00:00:00 2001 From: Anurag Kar Date: Mon, 10 Jun 2019 18:02:45 +0530 Subject: [PATCH 041/486] Example restful_server : Minor fix in copying file system base path --- .../protocols/http_server/restful_server/main/rest_server.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/protocols/http_server/restful_server/main/rest_server.c b/examples/protocols/http_server/restful_server/main/rest_server.c index 8c74ccec52..d68bd0a0aa 100644 --- a/examples/protocols/http_server/restful_server/main/rest_server.c +++ b/examples/protocols/http_server/restful_server/main/rest_server.c @@ -172,7 +172,7 @@ esp_err_t start_rest_server(const char *base_path) REST_CHECK(base_path, "wrong base path", err); rest_server_context_t *rest_context = calloc(1, sizeof(rest_server_context_t)); REST_CHECK(rest_context, "No memory for rest context", err); - strncpy(rest_context->base_path, base_path, ESP_VFS_PATH_MAX); + strlcpy(rest_context->base_path, base_path, sizeof(rest_context->base_path)); httpd_handle_t server = NULL; httpd_config_t config = HTTPD_DEFAULT_CONFIG(); From 842384a0d3963e1aa87a8a8d3add1552a44eb3f2 Mon Sep 17 00:00:00 2001 From: Roland Dobai Date: Tue, 11 Jun 2019 10:32:59 +0200 Subject: [PATCH 042/486] spi_flash: Fix Kconfig indentation Closes https://github.com/espressif/esp-idf/issues/3598 --- components/spi_flash/Kconfig | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/components/spi_flash/Kconfig b/components/spi_flash/Kconfig index 7ffb5660a3..969c7cc8a0 100644 --- a/components/spi_flash/Kconfig +++ b/components/spi_flash/Kconfig @@ -69,14 +69,12 @@ menu "SPI Flash driver" ROM functions. These functions should not be called directly from IDF applications. - config SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS - bool "Aborts" - config SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS - bool "Fails" - config SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED - bool "Allowed" + config SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS + bool "Aborts" + config SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS + bool "Fails" + config SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED + bool "Allowed" endchoice endmenu - - From 22d070e0aff16791bd78c1fb1b5aabe94c50349a Mon Sep 17 00:00:00 2001 From: Roland Dobai Date: Tue, 11 Jun 2019 10:45:26 +0200 Subject: [PATCH 043/486] spi_flash: Rename long Kconfig options --- components/spi_flash/Kconfig | 10 +++++----- components/spi_flash/flash_ops.c | 10 +++++----- components/spi_flash/include/esp_spi_flash.h | 4 ++-- components/spi_flash/sdkconfig.rename | 7 +++++++ components/spi_flash/test/test_out_of_bounds_write.c | 4 ++-- tools/ldgen/samples/sdkconfig | 6 +++--- tools/unit-test-app/sdkconfig.defaults | 2 +- 7 files changed, 25 insertions(+), 18 deletions(-) create mode 100644 components/spi_flash/sdkconfig.rename diff --git a/components/spi_flash/Kconfig b/components/spi_flash/Kconfig index 969c7cc8a0..6ba4d5180d 100644 --- a/components/spi_flash/Kconfig +++ b/components/spi_flash/Kconfig @@ -52,9 +52,9 @@ menu "SPI Flash driver" This option is needed to write to flash on ESP32-D2WD, and any configuration where external SPI flash is connected to non-default pins. - choice SPI_FLASH_WRITING_DANGEROUS_REGIONS + choice SPI_FLASH_DANGEROUS_WRITE bool "Writing to dangerous flash regions" - default SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS + default SPI_FLASH_DANGEROUS_WRITE_ABORTS help SPI flash APIs can optionally abort or return a failure code if erasing or writing addresses that fall at the beginning @@ -69,11 +69,11 @@ menu "SPI Flash driver" ROM functions. These functions should not be called directly from IDF applications. - config SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS + config SPI_FLASH_DANGEROUS_WRITE_ABORTS bool "Aborts" - config SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS + config SPI_FLASH_DANGEROUS_WRITE_FAILS bool "Fails" - config SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED + config SPI_FLASH_DANGEROUS_WRITE_ALLOWED bool "Allowed" endchoice diff --git a/components/spi_flash/flash_ops.c b/components/spi_flash/flash_ops.c index 8aae45f0e2..7b3f5b1f8a 100644 --- a/components/spi_flash/flash_ops.c +++ b/components/spi_flash/flash_ops.c @@ -77,7 +77,7 @@ const DRAM_ATTR spi_flash_guard_funcs_t g_flash_guard_default_ops = { .end = spi_flash_enable_interrupts_caches_and_other_cpu, .op_lock = spi_flash_op_lock, .op_unlock = spi_flash_op_unlock, -#if !CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED +#if !CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED .is_safe_write_address = is_safe_write_address #endif }; @@ -87,14 +87,14 @@ const DRAM_ATTR spi_flash_guard_funcs_t g_flash_guard_no_os_ops = { .end = spi_flash_enable_interrupts_caches_no_os, .op_lock = 0, .op_unlock = 0, -#if !CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED +#if !CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED .is_safe_write_address = 0 #endif }; static const spi_flash_guard_funcs_t *s_flash_guard_ops; -#ifdef CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS +#ifdef CONFIG_SPI_FLASH_DANGEROUS_WRITE_ABORTS #define UNSAFE_WRITE_ADDRESS abort() #else #define UNSAFE_WRITE_ADDRESS return false @@ -104,7 +104,7 @@ static const spi_flash_guard_funcs_t *s_flash_guard_ops; /* CHECK_WRITE_ADDRESS macro to fail writes which land in the bootloader, partition table, or running application region. */ -#if CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED +#if CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED #define CHECK_WRITE_ADDRESS(ADDR, SIZE) #else /* FAILS or ABORTS */ #define CHECK_WRITE_ADDRESS(ADDR, SIZE) do { \ @@ -112,7 +112,7 @@ static const spi_flash_guard_funcs_t *s_flash_guard_ops; return ESP_ERR_INVALID_ARG; \ } \ } while(0) -#endif // CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED +#endif // CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED static __attribute__((unused)) bool is_safe_write_address(size_t addr, size_t size) { diff --git a/components/spi_flash/include/esp_spi_flash.h b/components/spi_flash/include/esp_spi_flash.h index 254e408959..74c96fd36c 100644 --- a/components/spi_flash/include/esp_spi_flash.h +++ b/components/spi_flash/include/esp_spi_flash.h @@ -336,7 +336,7 @@ typedef bool (*spi_flash_is_safe_write_address_t)(size_t addr, size_t size); * - 'op_unlock' unlocks access to flash API internal data. * These two functions are recursive and can be used around the outside of multiple calls to * 'start' & 'end', in order to create atomic multi-part flash operations. - * 3) When CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED is disabled, flash writing/erasing + * 3) When CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED is disabled, flash writing/erasing * API checks for addresses provided by user to avoid corruption of critical flash regions * (bootloader, partition table, running application etc.). * @@ -354,7 +354,7 @@ typedef struct { spi_flash_guard_end_func_t end; /**< critical section end function. */ spi_flash_op_lock_func_t op_lock; /**< flash access API lock function.*/ spi_flash_op_unlock_func_t op_unlock; /**< flash access API unlock function.*/ -#if !CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED +#if !CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED spi_flash_is_safe_write_address_t is_safe_write_address; /**< checks flash write addresses.*/ #endif } spi_flash_guard_funcs_t; diff --git a/components/spi_flash/sdkconfig.rename b/components/spi_flash/sdkconfig.rename new file mode 100644 index 0000000000..6f9d592269 --- /dev/null +++ b/components/spi_flash/sdkconfig.rename @@ -0,0 +1,7 @@ +# sdkconfig replacement configurations for deprecated options formatted as +# CONFIG_DEPRECATED_OPTION CONFIG_NEW_OPTION + +CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS CONFIG_SPI_FLASH_DANGEROUS_WRITE +CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS CONFIG_SPI_FLASH_DANGEROUS_WRITE_ABORTS +CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS CONFIG_SPI_FLASH_DANGEROUS_WRITE_FAILS +CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED diff --git a/components/spi_flash/test/test_out_of_bounds_write.c b/components/spi_flash/test/test_out_of_bounds_write.c index 3c9b017476..9b411b0365 100644 --- a/components/spi_flash/test/test_out_of_bounds_write.c +++ b/components/spi_flash/test/test_out_of_bounds_write.c @@ -4,11 +4,11 @@ #include "esp_spi_flash.h" #include "esp_ota_ops.h" -#if CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS || CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS +#if CONFIG_SPI_FLASH_DANGEROUS_WRITE_ABORTS || CONFIG_SPI_FLASH_DANGEROUS_WRITE_FAILS static const char *data = "blah blah blah"; -#if CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS +#if CONFIG_SPI_FLASH_DANGEROUS_WRITE_FAILS #define TEST_TAGS "[spi_flash]" #else // ABORTS #define TEST_TAGS "[spi_flash][ignore]" diff --git a/tools/ldgen/samples/sdkconfig b/tools/ldgen/samples/sdkconfig index fcbe556260..8fbfd61586 100644 --- a/tools/ldgen/samples/sdkconfig +++ b/tools/ldgen/samples/sdkconfig @@ -494,9 +494,9 @@ CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072 CONFIG_SPI_FLASH_VERIFY_WRITE= CONFIG_SPI_FLASH_ENABLE_COUNTERS= CONFIG_SPI_FLASH_ROM_DRIVER_PATCH=y -CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS=y -CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS= -CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED= +CONFIG_SPI_FLASH_DANGEROUS_WRITE_ABORTS=y +CONFIG_SPI_FLASH_DANGEROUS_WRITE_FAILS= +CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED= # # SPIFFS Configuration diff --git a/tools/unit-test-app/sdkconfig.defaults b/tools/unit-test-app/sdkconfig.defaults index fca065641e..ca30ad526d 100644 --- a/tools/unit-test-app/sdkconfig.defaults +++ b/tools/unit-test-app/sdkconfig.defaults @@ -19,7 +19,7 @@ CONFIG_MBEDTLS_HARDWARE_SHA=y CONFIG_SPI_FLASH_ENABLE_COUNTERS=y CONFIG_ESP32_ULP_COPROC_ENABLED=y CONFIG_ESP_TASK_WDT=n -CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS=y +CONFIG_SPI_FLASH_DANGEROUS_WRITE_FAILS=y CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=7 CONFIG_COMPILER_STACK_CHECK_MODE_STRONG=y CONFIG_COMPILER_STACK_CHECK=y From 7971845fd4dedb30acf46bb85ac0be3aed7dd0a8 Mon Sep 17 00:00:00 2001 From: Roland Dobai Date: Tue, 4 Jun 2019 13:20:00 +0200 Subject: [PATCH 044/486] idf.py: Don't expect "_" env. variable to be available from PowerShell --- tools/idf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/idf.py b/tools/idf.py index a075dbd43f..cdaf84ecd0 100755 --- a/tools/idf.py +++ b/tools/idf.py @@ -1113,7 +1113,7 @@ if __name__ == "__main__": WINPTY_VAR = "WINPTY" WINPTY_EXE = "winpty" if ("MSYSTEM" in os.environ) and ( - not os.environ["_"].endswith(WINPTY_EXE) and WINPTY_VAR not in os.environ + not os.environ.get("_", "").endswith(WINPTY_EXE) and WINPTY_VAR not in os.environ ): os.environ[WINPTY_VAR] = "1" # the value is of no interest to us # idf.py calls itself with "winpty" and WINPTY global variable set From e6801912c5c4861f828ab1f447280628bba9a5d7 Mon Sep 17 00:00:00 2001 From: David Cermak Date: Mon, 27 May 2019 16:06:48 +0200 Subject: [PATCH 045/486] mdns: added initial suite of api unit tests --- components/mdns/test/CMakeLists.txt | 3 + components/mdns/test/component.mk | 4 + components/mdns/test/test_mdns.c | 99 +++++++++++++++++++ .../partition_table_unit_test_app.csv | 2 +- 4 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 components/mdns/test/CMakeLists.txt create mode 100644 components/mdns/test/component.mk create mode 100644 components/mdns/test/test_mdns.c diff --git a/components/mdns/test/CMakeLists.txt b/components/mdns/test/CMakeLists.txt new file mode 100644 index 0000000000..7e694470f6 --- /dev/null +++ b/components/mdns/test/CMakeLists.txt @@ -0,0 +1,3 @@ +set(COMPONENT_SRCDIRS ".") +set(COMPONENT_PRIV_REQUIRES unity test_utils mdns) +register_component() \ No newline at end of file diff --git a/components/mdns/test/component.mk b/components/mdns/test/component.mk new file mode 100644 index 0000000000..5be873488b --- /dev/null +++ b/components/mdns/test/component.mk @@ -0,0 +1,4 @@ +# +#Component Makefile +# +COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive \ No newline at end of file diff --git a/components/mdns/test/test_mdns.c b/components/mdns/test/test_mdns.c new file mode 100644 index 0000000000..6ae5e835ff --- /dev/null +++ b/components/mdns/test/test_mdns.c @@ -0,0 +1,99 @@ +#include "test_utils.h" +#include "mdns.h" +#include "unity.h" + + +#define MDNS_HOSTNAME "test-hostname" +#define MDNS_INSTANCE "test-instance" +#define MDNS_SERVICE_NAME "_http" +#define MDNS_SERVICE_PROTO "_tcp" +#define MDNS_SERVICE_PORT 80 + + +static void yield_to_all_priorities(void) +{ + // Lower the test-task priority before testing to ensure other tasks got executed on forced context switch + size_t test_task_prio_before = uxTaskPriorityGet(NULL); + vTaskPrioritySet(NULL, tskIDLE_PRIORITY); + taskYIELD(); // Let the RTOS to switch context + vTaskPrioritySet(NULL, test_task_prio_before); +} + + +TEST_CASE("mdns api to fail in invalid state", "[mdns][leaks=64]") +{ + TEST_ASSERT_NOT_EQUAL(ESP_OK, mdns_init() ); + TEST_ASSERT_NOT_EQUAL(ESP_OK, mdns_hostname_set(MDNS_HOSTNAME) ); + TEST_ASSERT_NOT_EQUAL(ESP_OK, mdns_instance_name_set(MDNS_INSTANCE) ); + TEST_ASSERT_NOT_EQUAL(ESP_OK, mdns_service_add(MDNS_INSTANCE, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, MDNS_SERVICE_PORT, NULL, 0) ); +} + +TEST_CASE("mdns init and deinit", "[mdns][leaks=64]") +{ + test_case_uses_tcpip(); + TEST_ASSERT_EQUAL(ESP_OK, esp_event_loop_create_default()); + TEST_ASSERT_EQUAL(ESP_OK, mdns_init() ); + yield_to_all_priorities(); // Make sure that mdns task has executed to complete initialization + mdns_free(); + esp_event_loop_delete_default(); +} + +TEST_CASE("mdns api return expected err-code and do not leak memory", "[mdns][leaks=64]") +{ + mdns_txt_item_t serviceTxtData[CONFIG_MDNS_MAX_SERVICES] = { {NULL, NULL}, + }; + for (int i=0; i Date: Wed, 12 Jun 2019 19:17:04 +0800 Subject: [PATCH 046/486] Doc/translate idf about --- docs/en/about.rst | 3 ++- docs/zh_CN/about.rst | 17 ++++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/docs/en/about.rst b/docs/en/about.rst index fc86b65748..c23ed2b602 100644 --- a/docs/en/about.rst +++ b/docs/en/about.rst @@ -1,9 +1,10 @@ About ===== +:link_to_translation:`zh_CN:[中文]` This is documentation of `ESP-IDF `_, the framework to develop applications for `ESP32 `_ chip by `Espressif `_. -The ESP32 is 2.4 GHz Wi-Fi and Bluetooth combo, 32 bit dual core chip with 600 DMIPS processing power. +The ESP32 is 2.4 GHz Wi-Fi and Bluetooth combo, which integrates one or two 32-bit microprocessors, with up to 600 DMIPS processing power. .. figure:: ../_static/about-doc.png :align: center diff --git a/docs/zh_CN/about.rst b/docs/zh_CN/about.rst index 63cda26962..af50eddfb6 100644 --- a/docs/zh_CN/about.rst +++ b/docs/zh_CN/about.rst @@ -1 +1,16 @@ -.. include:: ../en/about.rst \ No newline at end of file +关于本指南 +=========== +:link_to_translation:`en:[English]` + +本指南为 `乐鑫 `_ 公司 `ESP32 系列芯片 `_ 官方应用开发框架 `ESP-IDF `_ 的配套文档。 + +ESP32 芯片是一款 2.4 GHz Wi-Fi 和蓝牙双模芯片,内置 1 或 2 个 32 位处理器,运算能力最高可达 600 DMIPS。 + +.. figure:: ../_static/about-doc.png + :align: center + :alt: 乐鑫物联网综合开发框架 + :figclass: align-center + + 乐鑫物联网综合开发框架 + +ESP-IDF 即乐鑫物联网开发框架,可为在 Windows、Linux 和 macOS 系统平台上开发 ESP32 应用程序提供工具链、API、组件和工作流的支持。 From 2deb267be5a66acd478ef0b34ba09216b8a73128 Mon Sep 17 00:00:00 2001 From: He Yin Ling Date: Thu, 13 Jun 2019 11:14:39 +0800 Subject: [PATCH 047/486] test: fix some wifi case issues: * remove heap size check cases as we have bg tasks allocate memory * fix wifi connect to open ap issue --- .../idf_test/integration_test/KnownIssues | 13 ----- .../integration_test/TC_IT_WIFI_CONN.yml | 51 ++++--------------- 2 files changed, 9 insertions(+), 55 deletions(-) diff --git a/components/idf_test/integration_test/KnownIssues b/components/idf_test/integration_test/KnownIssues index f61c722ae1..818c27cba4 100644 --- a/components/idf_test/integration_test/KnownIssues +++ b/components/idf_test/integration_test/KnownIssues @@ -12,19 +12,6 @@ ESP32.WIFI_SCAN_0303 ESP32.WIFI_SCAN_0303_01 ESP32.WIFI_CONN_0302 ESP32.WIFI_CONN_0302_01 -ESP32.WIFI_CONN_0101 -ESP32.WIFI_CONN_0101_01 -ESP32.WIFI_CONN_0102 -ESP32.WIFI_CONN_0103 -ESP32.WIFI_CONN_0103_01 -ESP32.WIFI_SCAN_0101 -ESP32.WIFI_SCAN_0101_01 -ESP32.WIFI_SCAN_0102 -ESP32.WIFI_SCAN_0102_01 -ESP32.WIFI_SCAN_0103 -ESP32.WIFI_SCAN_0103_01 -ESP32.WIFI_SCAN_0104 -ESP32.WIFI_SCAN_0104_01 ESP32.WIFI_MODE_0102 ESP32.WIFI_MODE_0103 ESP32.WIFI_ADDR_0102 diff --git a/components/idf_test/integration_test/TC_IT_WIFI_CONN.yml b/components/idf_test/integration_test/TC_IT_WIFI_CONN.yml index f5cc01e929..7086310cdb 100644 --- a/components/idf_test/integration_test/TC_IT_WIFI_CONN.yml +++ b/components/idf_test/integration_test/TC_IT_WIFI_CONN.yml @@ -13,13 +13,13 @@ test cases: - '' - - SSC SSC1 ap -S -s -t 0 - - R SSC1 C +SAP:OK - - - SSC SSC2 sta -C -s -p + - - SSC SSC2 sta -C -s - - R SSC2 RE "\+JAP:CONNECTED,%%s"%%() - - SSC SSC1 ap -S -s -p -t 2 - - R SSC1 C +SAP:OK - - SSC SSC2 sta -C -s -p - - R SSC2 RE "\+JAP:CONNECTED,%%s"%%() - - - SSC SSC1 ap -S -s -p -t + - - SSC SSC1 ap -S -s -p -t 3 - - R SSC1 C +SAP:OK - - SSC SSC2 sta -C -s -p - - R SSC2 RE "\+JAP:CONNECTED,%%s"%%() @@ -29,6 +29,8 @@ test cases: - - R SSC2 RE "\+JAP:CONNECTED,%%s"%%() - - SSC SSC1 ap -S -s -p -t 1 - - R SSC1 C +SAP:OK + - - SSC SSC2 sta -D + - - R SSC2 C +QAP:OK - - SSC SSC2 sta -S - - R SSC2 RE "\+SCAN:%%s,.+,0,\d+"%%() C +SCANDONE - - SSC SSC1 ap -S -s -p -t 5 @@ -69,16 +71,18 @@ test cases: - '' - - SSC SSC1 ap -S -s -t 0 -n 1 - - R SSC1 C +SAP:OK - - - SSC SSC2 sta -C -s -p + - - SSC SSC2 sta -C -s - - R SSC2 RE "\+JAP:CONNECTED,%%s"%%() - - SSC SSC1 ap -S -s -t 0 -n 13 - - R SSC1 C +SAP:OK - - - SSC SSC2 sta -C -s -p + - - SSC SSC2 sta -C -s - - R SSC2 RE "\+JAP:CONNECTED,%%s"%%() - - SSC SSC1 ap -S -s -n 15 - - R SSC1 C +SAP:OK - - - SSC SSC2 sta -C -s -p + - - SSC SSC2 sta -C -s - - R SSC2 RE "\+JAP:CONNECTED,%%s"%%() + - - SSC SSC2 sta -D + - - R SSC2 C +QAP:OK - - SSC SSC2 sta -S - - R SSC2 RE "\+SCAN:%%s,.+,\d+,1"%%() execution time: 0.0 @@ -1040,43 +1044,6 @@ test cases: test point 1: basic function test point 2: beacon delay version: v1 (2016-8-15) -- CI ready: 'Yes' - ID: WIFI_CONN_1101 - SDK: ESP32_IDF - Test App: SSC - allow fail: '' - auto test: 'Yes' - category: Function - cmd set: - - '' - - - SSC SSC1 ram -Q - - - R SSC1 A :\+FREEHEAP:(\d+)\r\n - - - SSC SSC1 op -W -a start - - - R SSC1 C +MODE:OK - - - SSC SSC1 op -W -a stop - - - R SSC1 C +MODE:OK - - - SSC SSC1 ram -Q - - - R SSC1 P - execution time: 0 - expected result: | - 1. get current heap size - 2.OK - 3.OK - 4.heap size unchanged - initial condition: WIFISTO - level: Integration - module: WIFI MAC - steps: | - 1. check heap size - 2. wifi start - 3. wifi stop - 4. check heap size - sub module: WIFI Connect - summary: wifi start and stop, heap size unchanged - test environment: SSC_T1_4 - test point 1: basic function - test point 2: wifi start and stop, heap size unchanged - version: v1 (2016-12-31) - CI ready: 'Yes' ID: WIFI_CONN_1201 SDK: ESP32_IDF From a98141cc0a61e214e8d4c5fd8c40bf893a94d377 Mon Sep 17 00:00:00 2001 From: Anton Maklakov Date: Thu, 13 Jun 2019 12:42:49 +0700 Subject: [PATCH 048/486] ci: keep executables list sorted to pass tests --- tools/ci/executable-list.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/ci/executable-list.txt b/tools/ci/executable-list.txt index dcb4610b77..dd001885a3 100644 --- a/tools/ci/executable-list.txt +++ b/tools/ci/executable-list.txt @@ -31,10 +31,10 @@ tools/check_python_dependencies.py tools/ci/apply_bot_filter.py tools/ci/build_examples.sh tools/ci/build_examples_cmake.sh -tools/ci/check_idf_version.sh tools/ci/check-executable.sh tools/ci/check-line-endings.sh tools/ci/check_examples_cmake_make.sh +tools/ci/check_idf_version.sh tools/ci/check_ut_cmake_make.sh tools/ci/checkout_project_ref.py tools/ci/envsubst.py From d127849d80cd92f943aedef108d0dd54bbada387 Mon Sep 17 00:00:00 2001 From: David Cermak Date: Fri, 31 May 2019 15:35:05 +0200 Subject: [PATCH 049/486] can: suppress clang tidy warning about nullptr dereference --- components/driver/can.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/components/driver/can.c b/components/driver/can.c index 6001e65f22..0ddf892968 100644 --- a/components/driver/can.c +++ b/components/driver/can.c @@ -353,7 +353,7 @@ static void can_alert_handler(uint32_t alert_code, int *alert_req) } } -static void can_intr_handler_err_warn(can_status_reg_t *status, BaseType_t *task_woken, int *alert_req) +static void can_intr_handler_err_warn(can_status_reg_t *status, int *alert_req) { if (status->bus) { if (status->error) { @@ -478,10 +478,15 @@ static void can_intr_handler_main(void *arg) status.val = can_get_status(); intr_reason.val = (p_can_obj != NULL) ? can_get_interrupt_reason() : 0; //Incase intr occurs whilst driver is being uninstalled +#ifdef __clang_analyzer__ + if (intr_reason.val == 0) { // Teach clang-tidy that all bitfields are zero if a register is zero; othewise it warns about p_can_obj null dereference + intr_reason.err_warn = intr_reason.err_passive = intr_reason.bus_err = intr_reason.arb_lost = intr_reason.rx = intr_reason.tx = 0; + } +#endif //Handle error counter related interrupts if (intr_reason.err_warn) { //Triggers when Bus-Status or Error-status bits change - can_intr_handler_err_warn(&status, &task_woken, &alert_req); + can_intr_handler_err_warn(&status, &alert_req); } if (intr_reason.err_passive) { //Triggers when entering/returning error passive/active state From d18ed01b818836c5226dddbf788e2a338a56ecfd Mon Sep 17 00:00:00 2001 From: xueyunfei Date: Fri, 24 May 2019 11:58:48 +0800 Subject: [PATCH 050/486] lwip:fix bugfix/dns_bug --- components/lwip/lwip | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/lwip/lwip b/components/lwip/lwip index 88ccba0ccf..5d9fce09e3 160000 --- a/components/lwip/lwip +++ b/components/lwip/lwip @@ -1 +1 @@ -Subproject commit 88ccba0ccfc11cd968b80a9d50b3182972da9604 +Subproject commit 5d9fce09e352a7b11949b79f386c907ce8d09fa8 From 353de1487fd53a164ee36c60fe716c4388ac6039 Mon Sep 17 00:00:00 2001 From: David Cermak Date: Fri, 14 Jun 2019 11:42:11 +0200 Subject: [PATCH 051/486] log: fix minor memory leak when cleaning list of log levels --- components/log/log.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/components/log/log.c b/components/log/log.c index 72c232c68b..c59469cc02 100644 --- a/components/log/log.c +++ b/components/log/log.c @@ -174,8 +174,10 @@ void esp_log_level_set(const char* tag, esp_log_level_t level) void clear_log_level_list() { - while( !SLIST_EMPTY(&s_log_tags)) { + uncached_tag_entry_t *it; + while((it = SLIST_FIRST(&s_log_tags)) != NULL) { SLIST_REMOVE_HEAD(&s_log_tags, entries ); + free(it); } s_log_cache_entry_count = 0; s_log_cache_max_generation = 0; From da978bc5a12f7a1b6652863ea724b0c716ad2832 Mon Sep 17 00:00:00 2001 From: Roland Dobai Date: Mon, 4 Mar 2019 16:25:42 +0100 Subject: [PATCH 052/486] tools: Port the filtering option of IDF Monitor to the idf.py toolchain --- docs/en/api-guides/tools/idf-monitor.rst | 2 +- docs/zh_CN/api-guides/tools/idf-monitor.rst | 2 +- tools/idf.py | 25 +++++++++++++++++---- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/docs/en/api-guides/tools/idf-monitor.rst b/docs/en/api-guides/tools/idf-monitor.rst index f48275d8fb..2718a0adc5 100644 --- a/docs/en/api-guides/tools/idf-monitor.rst +++ b/docs/en/api-guides/tools/idf-monitor.rst @@ -115,7 +115,7 @@ In the background, IDF Monitor runs the following command:: Output Filtering ~~~~~~~~~~~~~~~~ -IDF monitor can be invoked as ``make monitor PRINT_FILTER=""`` (for make) or ``idf.py monitor PRINT_FILTER=""`` (for cmake), where ``PRINT_FILTER`` is the parameter for output filtering. The default value is an empty string, which means that everything is printed. +IDF monitor can be invoked as ``make monitor PRINT_FILTER=""`` (for make) or ``idf.py monitor --print-filter=""`` (for cmake), where ``PRINT_FILTER`` is the parameter for output filtering. The default value is an empty string, which means that everything is printed. Restrictions on what to print can be specified as a series of ``:`` items where ```` is the tag string and ```` is a character from the set ``{N, E, W, I, D, V, *}`` referring to a level for :doc:`logging <../../api-reference/system/log>`. diff --git a/docs/zh_CN/api-guides/tools/idf-monitor.rst b/docs/zh_CN/api-guides/tools/idf-monitor.rst index 3d842fbd13..c47a071708 100644 --- a/docs/zh_CN/api-guides/tools/idf-monitor.rst +++ b/docs/zh_CN/api-guides/tools/idf-monitor.rst @@ -103,7 +103,7 @@ IDF 监控器在后台运行如下命令:: 输出筛选 ~~~~~~~~~~~~~~~~ -IDF 监视器有两种启用方式:运行 ``make monitor PRINT_FILTER=""`` (适用于 Make)或者 ``idf.py monitor PRINT_FILTER=""`` (适用于 CMake),其中,``PRINT_FILTER`` 是输出筛选的参数。参数默认值为空字符串,可打印任何内容。 +IDF 监视器有两种启用方式:运行 ``make monitor PRINT_FILTER=""`` (适用于 Make)或者 ``idf.py monitor --print-filter=""`` (适用于 CMake),其中,``PRINT_FILTER`` 是输出筛选的参数。参数默认值为空字符串,可打印任何内容。 若需对打印内容设置限制,可指定 ``:`` 等选项,其中 ```` 是标签字符串,```` 是 ``{N, E, W, I, D, V, *}`` 集合中的一个字母,指的是 :doc:`日志 <../../api-reference/system/log>` 级别。 diff --git a/tools/idf.py b/tools/idf.py index cdaf84ecd0..abcdfc8701 100755 --- a/tools/idf.py +++ b/tools/idf.py @@ -361,7 +361,7 @@ def erase_flash(action, ctx, args): _run_tool("esptool.py", esptool_args, args.build_dir) -def monitor(action, ctx, args): +def monitor(action, ctx, args, print_filter): """ Run idf_monitor.py to watch build output """ @@ -385,6 +385,8 @@ def monitor(action, ctx, args): if args.port is not None: monitor_args += ["-p", args.port] monitor_args += ["-b", project_desc["monitor_baud"]] + if print_filter is not None: + monitor_args += ["--print_filter", print_filter] monitor_args += [elf_file] idf_py = [PYTHON] + get_commandline_options(ctx) # commands to re-run idf.py @@ -920,7 +922,7 @@ def init_cli(): }, "show_efuse_table": { "callback": build_target, - "help": "Print eFuse table", + "help": "Print eFuse table.", "order_dependencies": ["reconfigure"], }, "partition_table": { @@ -986,7 +988,7 @@ def init_cli(): "actions": { "flash": { "callback": flash, - "help": "Flash the project", + "help": "Flash the project.", "dependencies": ["all"], "order_dependencies": ["erase_flash"], }, @@ -997,6 +999,22 @@ def init_cli(): "monitor": { "callback": monitor, "help": "Display serial output.", + "options": [ + { + "names": ["--print-filter", "--print_filter"], + "help": ( + "Filter monitor output.\n" + "Restrictions on what to print can be specified as a series of : items " + "where is the tag string and is a character from the set " + "{N, E, W, I, D, V, *} referring to a level. " + 'For example, "tag1:W" matches and prints only the outputs written with ' + 'ESP_LOGW("tag1", ...) or at lower verbosity level, i.e. ESP_LOGE("tag1", ...). ' + 'Not specifying a or using "*" defaults to Verbose level.\n' + 'Please see the IDF Monitor section of the ESP-IDF documentation ' + 'for a more detailed description and further examples.'), + "default": None, + }, + ], "order_dependencies": [ "flash", "partition_table-flash", @@ -1042,7 +1060,6 @@ def init_cli(): ) # Add actions extensions - try: all_actions.append(action_extensions(base_actions, project_dir)) except NameError: From a77f01ec9dafc8311952e1167e436b6059579971 Mon Sep 17 00:00:00 2001 From: Sergei Silnov Date: Mon, 27 May 2019 09:18:38 +0200 Subject: [PATCH 053/486] Update order of arguments for idf.py in docs --- docs/en/api-guides/unit-tests-cmake.rst | 8 ++++---- docs/zh_CN/api-guides/unit-tests-cmake.rst | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/en/api-guides/unit-tests-cmake.rst b/docs/en/api-guides/unit-tests-cmake.rst index 355b7f4ed0..e08b912f87 100644 --- a/docs/en/api-guides/unit-tests-cmake.rst +++ b/docs/en/api-guides/unit-tests-cmake.rst @@ -130,13 +130,13 @@ Change into tools/unit-test-app directory to configure and build it: * `idf.py menuconfig` - configure unit test app. -* `idf.py build -T all` - build unit test app with tests for each component having tests in the ``test`` subdirectory. -* `idf.py build -T xxx` - build unit test app with tests for specific components. -* `idf.py build -T all -E xxx` - build unit test app with all unit tests, except for unit tests of some components. (For instance: `idf.py build -T all -E ulp mbedtls` - build all unit tests exludes ulp and mbedtls components). +* `idf.py -T all build` - build unit test app with tests for each component having tests in the ``test`` subdirectory. +* `idf.py -T xxx build` - build unit test app with tests for specific components. +* `idf.py -T all -E xxx build` - build unit test app with all unit tests, except for unit tests of some components. (For instance: `idf.py -T all -E ulp -E mbedtls build` - build all unit tests exludes ulp and mbedtls components). When the build finishes, it will print instructions for flashing the chip. You can simply run ``idf.py flash`` to flash all build output. -You can also run ``idf.py flash -T all`` or ``idf.py flash -T xxx`` to build and flash. Everything needed will be rebuilt automatically before flashing. +You can also run ``idf.py -T all flash`` or ``idf.py -T xxx flash`` to build and flash. Everything needed will be rebuilt automatically before flashing. Use menuconfig to set the serial port for flashing. diff --git a/docs/zh_CN/api-guides/unit-tests-cmake.rst b/docs/zh_CN/api-guides/unit-tests-cmake.rst index c5f8898b7d..1f3453f994 100644 --- a/docs/zh_CN/api-guides/unit-tests-cmake.rst +++ b/docs/zh_CN/api-guides/unit-tests-cmake.rst @@ -152,21 +152,21 @@ DUT2(slave)终端: - ``idf.py menuconfig`` - 配置单元测试程序。 -- ``idf.py build -T all`` - 编译单元测试程序,测试每个组件 ``test`` +- ``idf.py -T all build`` - 编译单元测试程序,测试每个组件 ``test`` 子目录下的用例。 -- ``idf.py build -T xxx`` - 编译单元测试程序,测试指定的组件。 +- ``idf.py -T xxx build`` - 编译单元测试程序,测试指定的组件。 -- ``idf.py build -T all -E xxx`` - +- ``idf.py -T all -E xxx build`` - 编译单元测试程序,测试所有(除开指定)的组件。例如 - ``idf.py build -T all -E ulp mbedtls`` - + ``idf.py -T all -E ulp mbedtls build`` - 编译所有的单元测试,不包括 ``ulp`` 和 ``mbedtls``\ 组件。 当编译完成时,它会打印出烧写芯片的指令。您只需要运行 ``idf.py flash`` 即可烧写所有编译输出的文件。 -您还可以运行 ``idf.py flash -T all`` 或者 -``idf.py flash -T xxx`` +您还可以运行 ``idf.py -T all flash`` 或者 +``idf.py -T xxx flash`` 来编译并烧写,所有需要的文件都会在烧写之前自动重新编译。 使用 ``menuconfig`` 可以设置烧写测试程序所使用的串口。 From 8e43cd4d6939bcf90c96a37703815a9e136183d0 Mon Sep 17 00:00:00 2001 From: Sergei Silnov Date: Tue, 11 Jun 2019 14:30:01 +0200 Subject: [PATCH 054/486] Add click package to linux setup instructions --- docs/en/get-started-cmake/linux-setup-scratch.rst | 4 ++-- docs/en/get-started-cmake/linux-setup.rst | 4 ++-- docs/zh_CN/get-started-cmake/linux-setup-scratch.rst | 4 ++-- docs/zh_CN/get-started-cmake/linux-setup.rst | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/en/get-started-cmake/linux-setup-scratch.rst b/docs/en/get-started-cmake/linux-setup-scratch.rst index a95c502842..cde88ccfc4 100644 --- a/docs/en/get-started-cmake/linux-setup-scratch.rst +++ b/docs/en/get-started-cmake/linux-setup-scratch.rst @@ -19,11 +19,11 @@ To compile with ESP-IDF you need to get the following packages: - Ubuntu and Debian:: - sudo apt-get install git wget libncurses-dev flex bison gperf python python-pip python-setuptools python-serial python-cryptography python-future python-pyparsing python-pyelftools cmake ninja-build ccache + sudo apt-get install git wget libncurses-dev flex bison gperf python python-pip python-setuptools python-serial python-click python-cryptography python-future python-pyparsing python-pyelftools cmake ninja-build ccache - Arch:: - sudo pacman -S --needed gcc git make ncurses flex bison gperf python2-pyserial python2-cryptography python2-future python2-pyparsing python2-pyelftools cmake ninja ccache + sudo pacman -S --needed gcc git make ncurses flex bison gperf python2-pyserial python2-click python2-cryptography python2-future python2-pyparsing python2-pyelftools cmake ninja ccache .. note:: CMake version 3.5 or newer is required for use with ESP-IDF. Older Linux distributions may require updating, enabling of a "backports" repository, or installing of a "cmake3" package rather than "cmake". diff --git a/docs/en/get-started-cmake/linux-setup.rst b/docs/en/get-started-cmake/linux-setup.rst index 38766a7b7d..8df911f53c 100644 --- a/docs/en/get-started-cmake/linux-setup.rst +++ b/docs/en/get-started-cmake/linux-setup.rst @@ -17,11 +17,11 @@ To compile with ESP-IDF you need to get the following packages: - Ubuntu and Debian:: - sudo apt-get install git wget libncurses-dev flex bison gperf python python-pip python-setuptools python-serial python-cryptography python-future python-pyparsing python-pyelftools cmake ninja-build ccache + sudo apt-get install git wget libncurses-dev flex bison gperf python python-pip python-setuptools python-serial python-click python-cryptography python-future python-pyparsing python-pyelftools cmake ninja-build ccache - Arch:: - sudo pacman -S --needed gcc git make ncurses flex bison gperf python2-pip python2-pyserial python2-cryptography python2-future python2-pyparsing python2-pyelftools cmake ninja ccache + sudo pacman -S --needed gcc git make ncurses flex bison gperf python2-pip python2-pyserial python2-click python2-cryptography python2-future python2-pyparsing python2-pyelftools cmake ninja ccache .. note:: CMake version 3.5 or newer is required for use with ESP-IDF. Older Linux distributions may require updating, enabling of a "backports" repository, or installing of a "cmake3" package rather than "cmake". diff --git a/docs/zh_CN/get-started-cmake/linux-setup-scratch.rst b/docs/zh_CN/get-started-cmake/linux-setup-scratch.rst index 0ba33490c3..967887f74d 100644 --- a/docs/zh_CN/get-started-cmake/linux-setup-scratch.rst +++ b/docs/zh_CN/get-started-cmake/linux-setup-scratch.rst @@ -19,11 +19,11 @@ - Ubuntu 和 Debian:: - sudo apt-get install git wget libncurses-dev flex bison gperf python python-pip python-setuptools python-serial python-cryptography python-future python-pyparsing python-pyelftools cmake ninja-build ccache + sudo apt-get install git wget libncurses-dev flex bison gperf python python-pip python-setuptools python-serial python-click python-cryptography python-future python-pyparsing python-pyelftools cmake ninja-build ccache - Arch:: - sudo pacman -S --needed gcc git make ncurses flex bison gperf python2-pyserial python2-cryptography python2-future python2-pyparsing python2-pyelftools cmake ninja ccache + sudo pacman -S --needed gcc git make ncurses flex bison gperf python2-pyserial python2-click python2-cryptography python2-future python2-pyparsing python2-pyelftools cmake ninja ccache .. note:: diff --git a/docs/zh_CN/get-started-cmake/linux-setup.rst b/docs/zh_CN/get-started-cmake/linux-setup.rst index 6104d9f431..82afb790b0 100644 --- a/docs/zh_CN/get-started-cmake/linux-setup.rst +++ b/docs/zh_CN/get-started-cmake/linux-setup.rst @@ -17,11 +17,11 @@ Linux 平台工具链的标准设置 (CMake) - Ubuntu 和 Debian:: - sudo apt-get install git wget libncurses-dev flex bison gperf python python-pip python-setuptools python-serial python-cryptography python-future python-pyparsing python-pyelftools cmake ninja-build ccache + sudo apt-get install git wget libncurses-dev flex bison gperf python python-pip python-setuptools python-serial python-click python-cryptography python-future python-pyparsing python-pyelftools cmake ninja-build ccache - Arch:: - sudo pacman -S --needed gcc git make ncurses flex bison gperf python2-pyserial python2-cryptography python2-future python2-pyparsing python2-pyelftools cmake ninja ccache + sudo pacman -S --needed gcc git make ncurses flex bison gperf python2-pyserial python2-click python2-cryptography python2-future python2-pyparsing python2-pyelftools cmake ninja ccache .. note:: 使用 ESP-IDF 需要 CMake 3.5 或以上版本。较早版本的 Linux 可能需要升级才能向后移植仓库,或安装 "cmake3" 软件包,而不是安装 "cmake"。 From c4f3afd4b5dc12e88d46b30350034b6cc9ea4c9b Mon Sep 17 00:00:00 2001 From: David Cermak Date: Tue, 20 Nov 2018 15:00:49 +0100 Subject: [PATCH 055/486] ci: add clang static analysis jobs Clang tidy 9.0.0 is to perform static analysis of IDF sources. All component sources are analysed with default sdkconfig configuration, based on examples/get-started/hello_world project (compilation commands are extracted from default build commands for this project). Configuration of static analysis is defined in tools/ci/static-analysis-rules.yml Closes https://github.com/espressif/esp-idf/issues/145 --- .gitlab-ci.yml | 81 ++++++++++++++++++++++++++++++ tools/ci/static-analysis-rules.yml | 23 +++++++++ 2 files changed, 104 insertions(+) create mode 100644 tools/ci/static-analysis-rules.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e782ae0c38..e8c830caa6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -525,6 +525,87 @@ test_ldgen_on_host: # check no crashes found - test -z "$(ls out/crashes/)" || exit 1 +.clang_tidy_check_template: &clang_tidy_check_template + stage: host_test + image: ${CI_DOCKER_REGISTRY}/clang-static-analysis + tags: + - host_test + dependencies: [] + artifacts: + reports: + junit: $IDF_PATH/output.xml + when: always + paths: + - $IDF_PATH/examples/get-started/hello_world/tidybuild/report/* + expire_in: 1 day + script: + - git clone $IDF_ANALYSIS_UTILS static_analysis_utils && cd static_analysis_utils + # Setup parameters of triggered/regular job + - export TRIGGERED_RELATIVE=${BOT_LABEL_STATIC_ANALYSIS-} && export TRIGGERED_ABSOLUTE=${BOT_LABEL_STATIC_ANALYSIS_ALL-} && export TARGET_BRANCH=${BOT_CUSTOMIZED_REVISION-} + - ./analyze.sh $IDF_PATH/examples/get-started/hello_world/ $IDF_PATH/tools/ci/static-analysis-rules.yml $IDF_PATH/output.xml + +.clang_tidy_deploy_template: &clang_tidy_deploy_template + stage: deploy + image: $CI_DOCKER_REGISTRY/esp32-ci-env + tags: + - deploy + script: + - mkdir -p ~/.ssh + - chmod 700 ~/.ssh + - echo -n $DOCS_DEPLOY_KEY > ~/.ssh/id_rsa_base64 + - base64 --decode --ignore-garbage ~/.ssh/id_rsa_base64 > ~/.ssh/id_rsa + - chmod 600 ~/.ssh/id_rsa + - echo -e "Host $DOCS_SERVER\n\tStrictHostKeyChecking no\n\tUser $DOCS_SERVER_USER\n" >> ~/.ssh/config + - export GIT_VER=$(git describe --always) + - cd $IDF_PATH/examples/get-started/hello_world/tidybuild + - mv report $GIT_VER + - tar czvf $GIT_VER.tar.gz $GIT_VER + - export STATIC_REPORT_PATH="web/static_analysis/esp-idf/" + - ssh $DOCS_SERVER -x "mkdir -p $STATIC_REPORT_PATH/clang-tidy" + - scp $GIT_VER.tar.gz $DOCS_SERVER:$STATIC_REPORT_PATH/clang-tidy + - ssh $DOCS_SERVER -x "cd $STATIC_REPORT_PATH/clang-tidy && tar xzvf $GIT_VER.tar.gz && rm -f latest && ln -s $GIT_VER latest" + # add link to view the report + - echo "[static analysis][clang tidy] $CI_DOCKER_REGISTRY/static_analysis/esp-idf/clang-tidy/${GIT_VER}/index.html" + - test ! -e ${GIT_VER}/FAILED_RULES || { echo 'Failed static analysis rules!'; cat ${GIT_VER}/FAILED_RULES; exit 1; } + +clang_tidy_check: + <<: *clang_tidy_check_template + variables: + BOT_NEEDS_TRIGGER_BY_NAME: 1 + BOT_LABEL_STATIC_ANALYSIS: 1 + +clang_tidy_check_regular: + <<: *clang_tidy_check_template + +clang_tidy_check_all: + <<: *clang_tidy_check_template + variables: + BOT_NEEDS_TRIGGER_BY_NAME: 1 + BOT_LABEL_STATIC_ANALYSIS_ALL: 1 + +clang_tidy_deploy: + <<: *clang_tidy_deploy_template + dependencies: + - clang_tidy_check + - clang_tidy_check_all + variables: + BOT_NEEDS_TRIGGER_BY_NAME: 1 + +clang_tidy_deploy_regular: + <<: *clang_tidy_deploy_template + dependencies: + - clang_tidy_check_regular + only: + refs: + - master + - /^release\/v/ + - /^v\d+\.\d+(\.\d+)?($|-)/ + - triggers + - schedules + variables: + - $BOT_LABEL_STATIC_ANALYSIS + - $BOT_LABEL_STATIC_ANALYSIS_ALL + test_mdns_fuzzer_on_host: <<: *host_fuzzer_test_template variables: diff --git a/tools/ci/static-analysis-rules.yml b/tools/ci/static-analysis-rules.yml new file mode 100644 index 0000000000..6678b85cb3 --- /dev/null +++ b/tools/ci/static-analysis-rules.yml @@ -0,0 +1,23 @@ +limits: + "clang-analyzer-core.NullDereference" : 9 + "clang-analyzer-unix.Malloc" : 9 + +ignore: + - "llvm-header-guard" + - "llvm-include-order" + +skip: + - "components/mbedtls/mbedtls" + - "components/lwip/lwip" + - "components/asio/asio" + - "components/bootloader/subproject/components/micro-ecc/micro-ecc" + - "components/bt/lib" + - "components/coap/libcoap" + - "components/esp_wifi/lib_esp32" + - "components/expat/expat" + - "components/json/cJSON" + - "components/libsodium/libsodium" + - "components/nghttp/nghttp2" + - "components/protobuf-c/protobuf-c" + - "components/spiffs/spiffs" + - "components/unity/unity" From 8a3cf8ca770711157dcc4e5cd2ab7461386bcba6 Mon Sep 17 00:00:00 2001 From: grumpy-dude Date: Wed, 12 Jun 2019 17:05:41 -0700 Subject: [PATCH 056/486] Allow configuration of local netif hostname via new LWIP component configuration menu item Signed-off-by: Sagar Bijwe Merges https://github.com/espressif/esp-idf/pull/3627 --- components/lwip/Kconfig | 6 ++++++ components/lwip/port/esp32/netif/ethernetif.c | 2 +- components/lwip/port/esp32/netif/nettestif.c | 2 +- components/lwip/port/esp32/netif/wlanif.c | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/components/lwip/Kconfig b/components/lwip/Kconfig index 6fa8a507e2..96bdcd5b20 100644 --- a/components/lwip/Kconfig +++ b/components/lwip/Kconfig @@ -1,5 +1,11 @@ menu "LWIP" + config LWIP_LOCAL_HOSTNAME + string "Local netif hostname" + default 'espressif' + help + The name this device will report to other devices on the network + config LWIP_L2_TO_L3_COPY bool "Enable copy between Layer2 and Layer3 packets" default n diff --git a/components/lwip/port/esp32/netif/ethernetif.c b/components/lwip/port/esp32/netif/ethernetif.c index d61653fcac..99ad88c274 100644 --- a/components/lwip/port/esp32/netif/ethernetif.c +++ b/components/lwip/port/esp32/netif/ethernetif.c @@ -222,7 +222,7 @@ ethernetif_init(struct netif *netif) /* Initialize interface hostname */ #if ESP_LWIP - netif->hostname = "espressif"; + netif->hostname = CONFIG_LWIP_LOCAL_HOSTNAME; #else netif->hostname = "lwip"; #endif diff --git a/components/lwip/port/esp32/netif/nettestif.c b/components/lwip/port/esp32/netif/nettestif.c index 741f542797..4422c21952 100644 --- a/components/lwip/port/esp32/netif/nettestif.c +++ b/components/lwip/port/esp32/netif/nettestif.c @@ -39,7 +39,7 @@ err_t nettestif_init(struct netif *netif) g_last_netif = netif; - netif->hostname = "espressif"; + netif->hostname = CONFIG_LWIP_LOCAL_HOSTNAME; /* * Initialize the snmp variables and counters inside the struct netif. diff --git a/components/lwip/port/esp32/netif/wlanif.c b/components/lwip/port/esp32/netif/wlanif.c index 7c60b69349..cd2bdac8d7 100644 --- a/components/lwip/port/esp32/netif/wlanif.c +++ b/components/lwip/port/esp32/netif/wlanif.c @@ -202,7 +202,7 @@ wlanif_init(struct netif *netif) /* Initialize interface hostname */ #if ESP_LWIP - netif->hostname = "espressif"; + netif->hostname = CONFIG_LWIP_LOCAL_HOSTNAME; #else netif->hostname = "lwip"; #endif From 1138133a78fb4e360dca10a71848b21f08499357 Mon Sep 17 00:00:00 2001 From: "Michael (XIAO Xufeng)" Date: Fri, 31 May 2019 15:23:10 +0800 Subject: [PATCH 057/486] spi: fix a possible concurrency issue --- components/driver/spi_slave.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/components/driver/spi_slave.c b/components/driver/spi_slave.c index f9f792a280..43c0b283df 100644 --- a/components/driver/spi_slave.c +++ b/components/driver/spi_slave.c @@ -361,12 +361,14 @@ static void SPI_SLAVE_ISR_ATTR spi_intr(void *arg) } } + //Disable interrupt before checking to avoid concurrency issue. + esp_intr_disable(host->intr); //Grab next transaction r = xQueueReceiveFromISR(host->trans_queue, &trans, &do_yield); - if (!r) { - //No packet waiting. Disable interrupt. - esp_intr_disable(host->intr); - } else { + if (r) { + //enable the interrupt again if there is packet to send + esp_intr_enable(host->intr); + //We have a transaction. Send it. host->cur_trans = trans; From d156f0fc0f5f4331392aa5bf69bcb0bb9674d964 Mon Sep 17 00:00:00 2001 From: suda-morris <362953310@qq.com> Date: Tue, 11 Jun 2019 15:46:02 +0800 Subject: [PATCH 058/486] replace strncpy with strlcpy in wifi examples for safety's sake --- examples/provisioning/custom_config/main/app_prov.c | 3 +-- examples/provisioning/softap_prov/main/app_prov.c | 3 +-- examples/system/console/main/cmd_wifi.c | 6 +++--- examples/wifi/iperf/main/cmd_wifi.c | 12 ++++++------ 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/examples/provisioning/custom_config/main/app_prov.c b/examples/provisioning/custom_config/main/app_prov.c index 5e0f68ea8e..d9859cdb4a 100644 --- a/examples/provisioning/custom_config/main/app_prov.c +++ b/examples/provisioning/custom_config/main/app_prov.c @@ -302,8 +302,7 @@ static esp_err_t start_wifi_ap(const char *ssid, const char *pass) }, }; - strncpy((char *) wifi_config.ap.ssid, ssid, sizeof(wifi_config.ap.ssid)); - wifi_config.ap.ssid_len = strnlen(ssid, sizeof(wifi_config.ap.ssid)); + strlcpy((char *) wifi_config.ap.ssid, ssid, sizeof(wifi_config.ap.ssid)); if (strlen(pass) == 0) { memset(wifi_config.ap.password, 0, sizeof(wifi_config.ap.password)); diff --git a/examples/provisioning/softap_prov/main/app_prov.c b/examples/provisioning/softap_prov/main/app_prov.c index f7bca2df12..73c8bdfc62 100644 --- a/examples/provisioning/softap_prov/main/app_prov.c +++ b/examples/provisioning/softap_prov/main/app_prov.c @@ -288,8 +288,7 @@ static esp_err_t start_wifi_ap(const char *ssid, const char *pass) }, }; - strncpy((char *) wifi_config.ap.ssid, ssid, sizeof(wifi_config.ap.ssid)); - wifi_config.ap.ssid_len = strnlen(ssid, sizeof(wifi_config.ap.ssid)); + strlcpy((char *) wifi_config.ap.ssid, ssid, sizeof(wifi_config.ap.ssid)); if (strlen(pass) == 0) { memset(wifi_config.ap.password, 0, sizeof(wifi_config.ap.password)); diff --git a/examples/system/console/main/cmd_wifi.c b/examples/system/console/main/cmd_wifi.c index 3cca7c3db9..eb2024dabe 100644 --- a/examples/system/console/main/cmd_wifi.c +++ b/examples/system/console/main/cmd_wifi.c @@ -26,7 +26,7 @@ static EventGroupHandle_t wifi_event_group; const int CONNECTED_BIT = BIT0; -static void event_handler(void* arg, esp_event_base_t event_base, +static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { @@ -61,9 +61,9 @@ static bool wifi_join(const char *ssid, const char *pass, int timeout_ms) { initialise_wifi(); wifi_config_t wifi_config = { 0 }; - strncpy((char *) wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid)); + strlcpy((char *) wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid)); if (pass) { - strncpy((char *) wifi_config.sta.password, pass, sizeof(wifi_config.sta.password)); + strlcpy((char *) wifi_config.sta.password, pass, sizeof(wifi_config.sta.password)); } ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); diff --git a/examples/wifi/iperf/main/cmd_wifi.c b/examples/wifi/iperf/main/cmd_wifi.c index 9ef61be48c..a6fbdaa162 100644 --- a/examples/wifi/iperf/main/cmd_wifi.c +++ b/examples/wifi/iperf/main/cmd_wifi.c @@ -53,7 +53,7 @@ static EventGroupHandle_t wifi_event_group; const int CONNECTED_BIT = BIT0; const int DISCONNECTED_BIT = BIT1; -static void scan_done_handler(void* arg, esp_event_base_t event_base, +static void scan_done_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { uint16_t sta_number = 0; @@ -76,14 +76,14 @@ static void scan_done_handler(void* arg, esp_event_base_t event_base, ESP_LOGI(TAG, "sta scan done"); } -static void got_ip_handler(void* arg, esp_event_base_t event_base, +static void got_ip_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { xEventGroupClearBits(wifi_event_group, DISCONNECTED_BIT); xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); } -static void disconnect_handler(void* arg, esp_event_base_t event_base, +static void disconnect_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { if (reconnect) { @@ -128,7 +128,7 @@ static bool wifi_cmd_sta_join(const char* ssid, const char* pass) strlcpy((char*) wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid)); if (pass) { - strncpy((char*) wifi_config.sta.password, pass, sizeof(wifi_config.sta.password)); + strlcpy((char*) wifi_config.sta.password, pass, sizeof(wifi_config.sta.password)); } if (bits & CONNECTED_BIT) { @@ -205,14 +205,14 @@ static bool wifi_cmd_ap_set(const char* ssid, const char* pass) }; reconnect = false; - strncpy((char*) wifi_config.ap.ssid, ssid, sizeof(wifi_config.ap.ssid)); + strlcpy((char*) wifi_config.ap.ssid, ssid, sizeof(wifi_config.ap.ssid)); if (pass) { if (strlen(pass) != 0 && strlen(pass) < 8) { reconnect = true; ESP_LOGE(TAG, "password less than 8"); return false; } - strncpy((char*) wifi_config.ap.password, pass, sizeof(wifi_config.ap.password)); + strlcpy((char*) wifi_config.ap.password, pass, sizeof(wifi_config.ap.password)); } if (strlen(pass) == 0) { From 4faf2de035cd7d353399b0077ed115a29abcd716 Mon Sep 17 00:00:00 2001 From: Kirill Chalov Date: Mon, 17 Jun 2019 14:23:52 +0800 Subject: [PATCH 059/486] Doc/review api ref storage --- components/nvs_flash/README.rst | 132 ++++++------ .../nvs_partition_generator/README.rst | 141 ++++++------ components/spi_flash/README.rst | 151 +++++-------- components/vfs/README.rst | 81 +++---- components/wear_levelling/README.rst | 70 +++--- docs/en/api-reference/storage/fatfs.rst | 44 ++-- docs/en/api-reference/storage/index.rst | 2 +- docs/en/api-reference/storage/nvs_flash.rst | 18 +- docs/en/api-reference/storage/sdmmc.rst | 98 +++++---- docs/en/api-reference/storage/spi_flash.rst | 31 +-- docs/en/api-reference/storage/spiffs.rst | 103 ++++----- .../api-reference/storage/wear-levelling.rst | 2 +- tools/mass_mfg/docs/README.rst | 200 ++++++++++-------- 13 files changed, 538 insertions(+), 535 deletions(-) diff --git a/components/nvs_flash/README.rst b/components/nvs_flash/README.rst index 6a4652f2ff..f8c2f4a1f6 100644 --- a/components/nvs_flash/README.rst +++ b/components/nvs_flash/README.rst @@ -4,54 +4,60 @@ Non-volatile storage library Introduction ------------ -Non-volatile storage (NVS) library is designed to store key-value pairs in flash. This sections introduces some concepts used by NVS. +Non-volatile storage (NVS) library is designed to store key-value pairs in flash. This section introduces some concepts used by NVS. + Underlying storage ^^^^^^^^^^^^^^^^^^ -Currently NVS uses a portion of main flash memory through ``spi_flash_{read|write|erase}`` APIs. The library uses the all the partitions with ``data`` type and ``nvs`` subtype. The application can choose to use the partition with label ``nvs`` through ``nvs_open`` API or any of the other partition by specifying its name through ``nvs_open_from_part`` API. +Currently, NVS uses a portion of main flash memory through ``spi_flash_{read|write|erase}`` APIs. The library uses all the partitions with ``data`` type and ``nvs`` subtype. The application can choose to use the partition with the label ``nvs`` through the ``nvs_open`` API function or any other partition by specifying its name using the ``nvs_open_from_part`` API function. -Future versions of this library may add other storage backends to keep data in another flash chip (SPI or I2C), RTC, FRAM, etc. +Future versions of this library may have other storage backends to keep data in another flash chip (SPI or I2C), RTC, FRAM, etc. .. note:: if an NVS partition is truncated (for example, when the partition table layout is changed), its contents should be erased. ESP-IDF build system provides a ``make erase_flash`` target to erase all contents of the flash chip. -.. note:: NVS works best for storing many small values, rather than a few large values of type 'string' and 'blob'. If storing large blobs or strings is required, consider using the facilities provided by the FAT filesystem on top of the wear levelling library. +.. note:: NVS works best for storing many small values, rather than a few large values of the type 'string' and 'blob'. If you need to store large blobs or strings, consider using the facilities provided by the FAT filesystem on top of the wear levelling library. + Keys and values ^^^^^^^^^^^^^^^ -NVS operates on key-value pairs. Keys are ASCII strings, maximum key length is currently 15 characters. Values can have one of the following types: +NVS operates on key-value pairs. Keys are ASCII strings; the maximum key length is currently 15 characters. Values can have one of the following types: - integer types: ``uint8_t``, ``int8_t``, ``uint16_t``, ``int16_t``, ``uint32_t``, ``int32_t``, ``uint64_t``, ``int64_t`` - zero-terminated string - variable length binary data (blob) .. note:: - String values are currently limited to 4000 bytes. This includes the null terminator. Blob values are limited to 508000 bytes or (97.6% of the partition size - 4000) bytes whichever is lower. -Additional types, such as ``float`` and ``double`` may be added later. + String values are currently limited to 4000 bytes. This includes the null terminator. Blob values are limited to 508000 bytes or 97.6% of the partition size - 4000 bytes, whichever is lower. -Keys are required to be unique. Writing a value for a key which already exists behaves as follows: +Additional types, such as ``float`` and ``double`` might be added later. -- if the new value is of the same type as old one, value is updated -- if the new value has different data type, an error is returned +Keys are required to be unique. Assigning a new value to an existing key works as follows: + +- if the new value is of the same type as the old one, value is updated +- if the new value has a different data type, an error is returned + +Data type check is also performed when reading a value. An error is returned if the data type of the read operation does not match the data type of the value. -Data type check is also performed when reading a value. An error is returned if data type of read operation doesn’t match the data type of the value. Namespaces ^^^^^^^^^^ -To mitigate potential conflicts in key names between different components, NVS assigns each key-value pair to one of namespaces. Namespace names follow the same rules as key names, i.e. 15 character maximum length. Namespace name is specified in the ``nvs_open`` or ``nvs_open_from_part`` call. This call returns an opaque handle, which is used in subsequent calls to ``nvs_read_*``, ``nvs_write_*``, and ``nvs_commit`` functions. This way, handle is associated with a namespace, and key names will not collide with same names in other namespaces. -Please note that the namespaces with same name in different NVS partitions are considered as separate namespaces. +To mitigate potential conflicts in key names between different components, NVS assigns each key-value pair to one of namespaces. Namespace names follow the same rules as key names, i.e., the maximum length is 15 characters. Namespace name is specified in the ``nvs_open`` or ``nvs_open_from_part`` call. This call returns an opaque handle, which is used in subsequent calls to the ``nvs_read_*``, ``nvs_write_*``, and ``nvs_commit`` functions. This way, a handle is associated with a namespace, and key names will not collide with same names in other namespaces. +Please note that the namespaces with the same name in different NVS partitions are considered as separate namespaces. + Security, tampering, and robustness ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ NVS is not directly compatible with the ESP32 flash encryption system. However, data can still be stored in encrypted form if NVS encryption is used together with ESP32 flash encryption. Please refer to :ref:`nvs_encryption` for more details. -If NVS encryption is not used, it is possible for anyone with physical access to the flash chip to alter, erase, or add key-value pairs. With NVS encryption enabled, it is not possible to alter or add a key-value pair and get recognized as a valid pair without knowing corresponding NVS encryption keys. However, there is no tamper-resistance against erase operation. +If NVS encryption is not used, it is possible for anyone with physical access to the flash chip to alter, erase, or add key-value pairs. With NVS encryption enabled, it is not possible to alter or add a key-value pair and get recognized as a valid pair without knowing corresponding NVS encryption keys. However, there is no tamper-resistance against the erase operation. + +The library does try to recover from conditions when flash memory is in an inconsistent state. In particular, one should be able to power off the device at any point and time and then power it back on. This should not result in loss of data, except for the new key-value pair if it was being written at the moment of powering off. The library should also be able to initialize properly with any random data present in flash memory. -The library does try to recover from conditions when flash memory is in an inconsistent state. In particular, one should be able to power off the device at any point and time and then power it back on. This should not result in loss of data, except for the new key-value pair if it was being written at the moment of power off. The library should also be able to initialize properly with any random data present in flash memory. Internals --------- @@ -59,7 +65,7 @@ Internals Log of key-value pairs ^^^^^^^^^^^^^^^^^^^^^^ -NVS stores key-value pairs sequentially, with new key-value pairs being added at the end. When a value of any given key has to be updated, new key-value pair is added at the end of the log and old key-value pair is marked as erased. +NVS stores key-value pairs sequentially, with new key-value pairs being added at the end. When a value of any given key has to be updated, a new key-value pair is added at the end of the log and the old key-value pair is marked as erased. Pages and entries ^^^^^^^^^^^^^^^^^ @@ -67,22 +73,22 @@ Pages and entries NVS library uses two main entities in its operation: pages and entries. Page is a logical structure which stores a portion of the overall log. Logical page corresponds to one physical sector of flash memory. Pages which are in use have a *sequence number* associated with them. Sequence numbers impose an ordering on pages. Higher sequence numbers correspond to pages which were created later. Each page can be in one of the following states: Empty/uninitialized - Flash storage for the page is empty (all bytes are ``0xff``). Page isn't used to store any data at this point and doesn’t have a sequence number. + Flash storage for the page is empty (all bytes are ``0xff``). Page is not used to store any data at this point and does not have a sequence number. Active - Flash storage is initialized, page header has been written to flash, page has a valid sequence number. Page has some empty entries and data can be written there. At most one page can be in this state at any given moment. + Flash storage is initialized, page header has been written to flash, page has a valid sequence number. Page has some empty entries and data can be written there. No more than one page can be in this state at any given moment. Full Flash storage is in a consistent state and is filled with key-value pairs. Writing new key-value pairs into this page is not possible. It is still possible to mark some key-value pairs as erased. Erasing - Non-erased key-value pairs are being moved into another page so that the current page can be erased. This is a transient state, i.e. page should never stay in this state when any API call returns. In case of a sudden power off, move-and-erase process will be completed upon next power on. + Non-erased key-value pairs are being moved into another page so that the current page can be erased. This is a transient state, i.e., page should never stay in this state at the time when any API call returns. In case of a sudden power off, the move-and-erase process will be completed upon the next power-on. Corrupted - Page header contains invalid data, and further parsing of page data was canceled. Any items previously written into this page will not be accessible. Corresponding flash sector will not be erased immediately, and will be kept along with sectors in *uninitialized* state for later use. This may be useful for debugging. + Page header contains invalid data, and further parsing of page data was canceled. Any items previously written into this page will not be accessible. The corresponding flash sector will not be erased immediately and will be kept along with sectors in *uninitialized* state for later use. This may be useful for debugging. -Mapping from flash sectors to logical pages doesn't have any particular order. Library will inspect sequence numbers of pages found in each flash sector and organize pages in a list based on these numbers. +Mapping from flash sectors to logical pages does not have any particular order. The library will inspect sequence numbers of pages found in each flash sector and organize pages in a list based on these numbers. :: @@ -101,11 +107,11 @@ Mapping from flash sectors to logical pages doesn't have any particular order. L Structure of a page ^^^^^^^^^^^^^^^^^^^ -For now we assume that flash sector size is 4096 bytes and that ESP32 flash encryption hardware operates on 32-byte blocks. It is possible to introduce some settings configurable at compile-time (e.g. via menuconfig) to accommodate flash chips with different sector sizes (although it is not clear if other components in the system, e.g. SPI flash driver and SPI flash cache can support these other sizes). +For now, we assume that flash sector size is 4096 bytes and that ESP32 flash encryption hardware operates on 32-byte blocks. It is possible to introduce some settings configurable at compile-time (e.g., via menuconfig) to accommodate flash chips with different sector sizes (although it is not clear if other components in the system, e.g., SPI flash driver and SPI flash cache can support these other sizes). Page consists of three parts: header, entry state bitmap, and entries themselves. To be compatible with ESP32 flash encryption, entry size is 32 bytes. For integer types, entry holds one key-value pair. For strings and blobs, an entry holds part of key-value pair (more on that in the entry structure description). -The following diagram illustrates page structure. Numbers in parentheses indicate size of each part in bytes. :: +The following diagram illustrates the page structure. Numbers in parentheses indicate the size of each part in bytes. :: +-----------+--------------+-------------+-------------------------+ | State (4) | Seq. no. (4) | version (1) | Unused (19) | CRC32 (4) | Header (32) @@ -122,23 +128,23 @@ The following diagram illustrates page structure. Numbers in parentheses indicat | Entry 125 (32) | +------------------------------------------------------------------+ -Page header and entry state bitmap are always written to flash unencrypted. Entries are encrypted if flash encryption feature of the ESP32 is used. +Page header and entry state bitmap are always written to flash unencrypted. Entries are encrypted if flash encryption feature of ESP32 is used. -Page state values are defined in such a way that changing state is possible by writing 0 into some of the bits. Therefore it not necessary to erase the page to change page state, unless that is a change to *erased* state. +Page state values are defined in such a way that changing state is possible by writing 0 into some of the bits. Therefore it is not necessary to erase the page to change its state unless that is a change to the *erased* state. -The version field in the header reflects NVS format version used. For backward compatibility reasons, it is decremented for every version upgrade starting at 0xff (i.e. 0xff for version-1, 0xfe for version-2 and so on). +The version field in the header reflects the NVS format version used. For backward compatibility reasons, it is decremented for every version upgrade starting at 0xff (i.e., 0xff for version-1, 0xfe for version-2 and so on). -CRC32 value in header is calculated over the part which doesn't include state value (bytes 4 to 28). Unused part is currently filled with ``0xff`` bytes. +CRC32 value in the header is calculated over the part which does not include a state value (bytes 4 to 28). The unused part is currently filled with ``0xff`` bytes. -The following sections describe structure of entry state bitmap and entry itself. +The following sections describe the structure of entry state bitmap and entry itself. Entry and entry state bitmap ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Each entry can be in one of the following three states. Each state is represented with two bits in the entry state bitmap. Final four bits in the bitmap (256 - 2 * 126) are unused. +Each entry can be in one of the following three states represented with two bits in the entry state bitmap. The final four bits in the bitmap (256 - 2 * 126) are not used. Empty (2'b11) - Nothing is written into the specific entry yet. It is in an uninitialized state (all bytes ``0xff``). + Nothing is written into the specific entry yet. It is in an uninitialized state (all bytes are ``0xff``). Written (2'b10) A key-value pair (or part of key-value pair which spans multiple entries) has been written into the entry. @@ -152,7 +158,7 @@ Erased (2'b00) Structure of entry ^^^^^^^^^^^^^^^^^^ -For values of primitive types (currently integers from 1 to 8 bytes long), entry holds one key-value pair. For string and blob types, entry holds part of the whole key-value pair. For strings, in case when a key-value pair spans multiple entries, all entries are stored in the same page. Blobs are allowed to span over multiple pages by dividing them into smaller chunks. For the purpose tracking these chunks, an additional fixed length metadata entry is stored called "blob index" entry. Earlier format of blobs are still supported (can be read and modified). However, once the blobs are modified, they are stored using the new format. +For values of primitive types (currently integers from 1 to 8 bytes long), entry holds one key-value pair. For string and blob types, entry holds part of the whole key-value pair. For strings, in case when a key-value pair spans multiple entries, all entries are stored in the same page. Blobs are allowed to span over multiple pages by dividing them into smaller chunks. For tracking these chunks, an additional fixed length metadata entry is stored called "blob index". Earlier formats of blobs are still supported (can be read and modified). However, once the blobs are modified, they are stored using the new format. :: @@ -160,10 +166,10 @@ For values of primitive types (currently integers from 1 to 8 bytes long), entry | NS (1) | Type (1) | Span (1) | ChunkIndex (1) | CRC32 (4) | Key (16) | Data (8) | +--------+----------+----------+----------------+-----------+---------------+----------+ - Primitive +--------------------------------+ - +--------> | Data (8) | + Primitive +--------------------------------+ + +--------> | Data (8) | | Types +--------------------------------+ - +-> Fixed length -- + +-> Fixed length -- | | +---------+--------------+---------------+-------+ | +--------> | Size(4) | ChunkCount(1)| ChunkStart(1) | Rsv(2)| Data format ---+ Blob Index +---------+--------------+---------------+-------+ @@ -176,25 +182,25 @@ For values of primitive types (currently integers from 1 to 8 bytes long), entry Individual fields in entry structure have the following meanings: NS - Namespace index for this entry. See section on namespaces implementation for explanation of this value. + Namespace index for this entry. For more information on this value, see the section on namespaces implementation. Type - One byte indicating data type of value. See ``ItemType`` enumeration in ``nvs_types.h`` for possible values. + One byte indicating the value data type. See the ``ItemType`` enumeration in ``nvs_types.h`` for possible values. Span - Number of entries used by this key-value pair. For integer types, this is equal to 1. For strings and blobs this depends on value length. + Number of entries used by this key-value pair. For integer types, this is equal to 1. For strings and blobs, this depends on value length. ChunkIndex - Used to store index of the blob-data chunk for blob types. For other types, this should be ``0xff``. + Used to store the index of a blob-data chunk for blob types. For other types, this should be ``0xff``. CRC32 Checksum calculated over all the bytes in this entry, except for the CRC32 field itself. Key - Zero-terminated ASCII string containing key name. Maximum string length is 15 bytes, excluding zero terminator. + Zero-terminated ASCII string containing a key name. Maximum string length is 15 bytes, excluding a zero terminator. Data - For integer types, this field contains the value itself. If the value itself is shorter than 8 bytes it is padded to the right, with unused bytes filled with ``0xff``. + For integer types, this field contains the value itself. If the value itself is shorter than 8 bytes, it is padded to the right, with unused bytes filled with ``0xff``. For "blob index" entry, these 8 bytes hold the following information about data-chunks: @@ -205,23 +211,23 @@ Data (Only for blob index.) Total number of blob-data chunks into which the blob was divided during storage. - ChunkStart - (Only for blob index.) ChunkIndex of the first blob-data chunk of this blob. Subsequent chunks have chunkIndex incrementally allocated (step of 1) + (Only for blob index.) ChunkIndex of the first blob-data chunk of this blob. Subsequent chunks have chunkIndex incrementally allocated (step of 1). - For string and blob data chunks, these 8 bytes hold additional data about the value, described next: + For string and blob data chunks, these 8 bytes hold additional data about the value, which are described below: - Size - (Only for strings and blobs.) Size, in bytes, of actual data. For strings, this includes zero terminator. + (Only for strings and blobs.) Size, in bytes, of actual data. For strings, this includes zero terminators. - CRC32 (Only for strings and blobs.) Checksum calculated over all bytes of data. -Variable length values (strings and blobs) are written into subsequent entries, 32 bytes per entry. `Span` field of the first entry indicates how many entries are used. +Variable length values (strings and blobs) are written into subsequent entries, 32 bytes per entry. The `Span` field of the first entry indicates how many entries are used. Namespaces ^^^^^^^^^^ -As mentioned above, each key-value pair belongs to one of the namespaces. Namespaces identifiers (strings) are stored as keys of key-value pairs in namespace with index 0. Values corresponding to these keys are indexes of these namespaces. +As mentioned above, each key-value pair belongs to one of the namespaces. Namespace identifiers (strings) are stored as keys of key-value pairs in namespace with index 0. Values corresponding to these keys are indexes of these namespaces. :: @@ -239,23 +245,23 @@ As mentioned above, each key-value pair belongs to one of the namespaces. Namesp Item hash list ^^^^^^^^^^^^^^ -To reduce the number of reads performed from flash memory, each member of Page class maintains a list of pairs: (item index; item hash). This list makes searches much quicker. Instead of iterating over all entries, reading them from flash one at a time, ``Page::findItem`` first performs search for item hash in the hash list. This gives the item index within the page, if such an item exists. Due to a hash collision it is possible that a different item will be found. This is handled by falling back to iteration over items in flash. +To reduce the number of reads from flash memory, each member of the Page class maintains a list of pairs: item index; item hash. This list makes searches much quicker. Instead of iterating over all entries, reading them from flash one at a time, ``Page::findItem`` first performs a search for the item hash in the hash list. This gives the item index within the page if such an item exists. Due to a hash collision, it is possible that a different item will be found. This is handled by falling back to iteration over items in flash. -Each node in hash list contains a 24-bit hash and 8-bit item index. Hash is calculated based on item namespace, key name and ChunkIndex. CRC32 is used for calculation, result is truncated to 24 bits. To reduce overhead of storing 32-bit entries in a linked list, list is implemented as a doubly-linked list of arrays. Each array holds 29 entries, for the total size of 128 bytes, together with linked list pointers and 32-bit count field. Minimal amount of extra RAM usage per page is therefore 128 bytes, maximum is 640 bytes. +Each node in the hash list contains a 24-bit hash and 8-bit item index. Hash is calculated based on item namespace, key name, and ChunkIndex. CRC32 is used for calculation; the result is truncated to 24 bits. To reduce the overhead for storing 32-bit entries in a linked list, the list is implemented as a double-linked list of arrays. Each array holds 29 entries, for the total size of 128 bytes, together with linked list pointers and a 32-bit count field. The minimum amount of extra RAM usage per page is therefore 128 bytes; maximum is 640 bytes. .. _nvs_encryption: NVS Encryption -------------- -Data stored in NVS partitions can be encrypted using AES-XTS in the manner similar to one mentioned in disc encryption standard IEEE P1619. For the purpose of encryption, each entry is considered as one `sector` and relative address of the entry (w.r.t. partition-start) is fed to the encryption algorithm as `sector-number`. The keys required for nvs encryption are stored in yet another partition, which is protected using :doc:`Flash Encryption <../../security/flash-encryption>`. Therefore, enabling :doc:`Flash Encryption <../../security/flash-encryption>` is a prerequisite for NVS encryption. +Data stored in NVS partitions can be encrypted using AES-XTS in the manner similar to the one mentioned in disk encryption standard IEEE P1619. For the purpose of encryption, each entry is treated as one `sector` and relative address of the entry (w.r.t. partition-start) is fed to the encryption algorithm as `sector-number`. The keys required for NVS encryption are stored in yet another partition, which is protected using :doc:`Flash Encryption <../../security/flash-encryption>`. Therefore, enabling :doc:`Flash Encryption <../../security/flash-encryption>` is a prerequisite for NVS encryption. .. _nvs_key_partition: NVS key partition ^^^^^^^^^^^^^^^^^ -An application requiring NVS encryption support needs to be compiled with a key-partition of type `data` and subtype `key`. This partition should be marked as `encrypted`. Refer to :doc:`Partition Tables <../../api-guides/partition-tables>` for more details. The size of the partition should be 4096 bytes (minimum partition size). The structure of this partition is depicted below. +An application requiring NVS encryption support needs to be compiled with a key-partition of the type `data` and subtype `key`. This partition should be marked as `encrypted`. Refer to :doc:`Partition Tables <../../api-guides/partition-tables>` for more details. The size of the partition should be 4096 bytes (minimum partition size). The structure of this partition is depicted below. :: @@ -267,25 +273,33 @@ An application requiring NVS encryption support needs to be compiled with a key- | CRC32(4) | +---------------------------------------------+ -This partition can be generated using `nvs partition generator` utility and flashed onto the device. Since the partition is marked `encrypted` and :doc:`Flash Encryption <../../security/flash-encryption>` is enabled, bootloader will encrypt this partition using flash encryption key on first boot. Alternatively, the keys can be generated after startup using ``nvs_flash_generate_keys`` API provided by ``nvs_flash.h``, which will then write those keys onto the key-partition in encrypted form. +This partition can be generated using `nvs partition generator` utility and flashed onto the device. Since the partition is marked `encrypted` and :doc:`Flash Encryption <../../security/flash-encryption>` is enabled, bootloader will encrypt this partition using flash encryption key on the first boot. Alternatively, the keys can be generated after startup using the ``nvs_flash_generate_keys`` API function provided by ``nvs_flash.h``, which will then write those keys onto the key-partition in encrypted form. It is possible for an application to use different keys for different NVS partitions and thereby have multiple key-partitions. However, it is a responsibility of the application to provide correct key-partition/keys for the purpose of encryption/decryption. Encrypted Read/Write ^^^^^^^^^^^^^^^^^^^^ -The same NVS APIs ``nvs_read_*`` or ``nvs_write_*`` can be used for reading and writing of encrypted nvs partition as well. However, the APIs for initialising NVS partitions are different. ``nvs_flash_secure_init`` and ``nvs_flash_secure_init_partition`` are used for initialising instead of ``nvs_flash_init`` and ``nvs_flash_init_partition`` respectively. ``nvs_sec_cfg_t`` structure required for these APIs can be populated using ``nvs_flash_generate_keys`` or ``nvs_flash_read_security_cfg``. +The same NVS API functions ``nvs_read_*`` or ``nvs_write_*`` can be used for reading of, and writing to an encrypted nvs partition as well. However, the API functions for initialising NVS partitions are different: ``nvs_flash_secure_init`` and ``nvs_flash_secure_init_partition`` instead of ``nvs_flash_init`` and ``nvs_flash_init_partition`` respectively. The ``nvs_sec_cfg_t`` structure required for these API functions can be populated using ``nvs_flash_generate_keys`` or ``nvs_flash_read_security_cfg``. -Applications are expected to follow the following steps in order to perform NVS read/write operations with encryption enabled. +Applications are expected to follow the steps below in order to perform NVS read/write operations with encryption enabled. - 1. Find key partition and NVS data partition using ``esp_partition_find*`` APIs. - 2. Populate ``nvs_sec_cfg_t`` struct using ``nvs_flash_read_security_cfg`` or ``nvs_flash_generate_keys`` APIs. - 3. Initialise NVS flash partition using ``nvs_flash_secure_init`` or ``nvs_flash_secure_init_partition`` APIs. - 4. Open a namespace using ``nvs_open`` or ``nvs_open_from_part`` APIs - 5. Perform NVS read/write operations using ``nvs_read_*`` or ``nvs_write_*`` - 6. Deinitialise NVS partition using ``nvs_flash_deinit``. + 1. Find key partition and NVS data partition using ``esp_partition_find*`` API functions. + 2. Populate the ``nvs_sec_cfg_t`` struct using the ``nvs_flash_read_security_cfg`` or ``nvs_flash_generate_keys`` API functions. + 3. Initialise NVS flash partition using the ``nvs_flash_secure_init`` or ``nvs_flash_secure_init_partition`` API functions. + 4. Open a namespace using the ``nvs_open`` or ``nvs_open_from_part`` API functions. + 5. Perform NVS read/write operations using ``nvs_read_*`` or ``nvs_write_*``. + 6. Deinitialise an NVS partition using ``nvs_flash_deinit``. NVS iterators ^^^^^^^^^^^^^ -Iterators allow to list key-value pairs stored in NVS based on specified partition name, namespace and data type. ``nvs_entry_find`` returns an opaque handle, which is used in subsequent calls to ``nvs_entry_next`` and ``nvs_entry_info`` function. ``nvs_entry_next`` function returns iterator to the next key-value pair. If none or no other key-value pair was found for given criteria, ``nvs_entry_find`` and ``nvs_entry_next`` functions return NULL. In that case, iterator does not have to be released. Otherwise, ``nvs_release_iterator`` function has to be used, when iterator is no longer needed. Information about each key-value pair can be obtained from ``nvs_entry_info`` function. \ No newline at end of file +Iterators allow to list key-value pairs stored in NVS, based on specified partition name, namespace, and data type. + +There are the following functions available: + +- ``nvs_entry_find`` returns an opaque handle, which is used in subsequent calls to the ``nvs_entry_next`` and ``nvs_entry_info`` functions. +- ``nvs_entry_next`` returns iterator to the next key-value pair. +- ``nvs_entry_info`` returns information about each key-value pair + +If none or no other key-value pair was found for given criteria, ``nvs_entry_find`` and ``nvs_entry_next`` return NULL. In that case, the iterator does not have to be released. If the iterator is no longer needed, you can release it by using the function ``nvs_release_iterator``. diff --git a/components/nvs_flash/nvs_partition_generator/README.rst b/components/nvs_flash/nvs_partition_generator/README.rst index cf31261366..bcb74f7cd1 100644 --- a/components/nvs_flash/nvs_partition_generator/README.rst +++ b/components/nvs_flash/nvs_partition_generator/README.rst @@ -4,64 +4,81 @@ NVS Partition Generator Utility Introduction ------------ -:component_file:`nvs_flash/nvs_partition_generator/nvs_partition_gen.py` utility is designed to help create a binary file, compatible with NVS architecture defined in :doc:`Non-Volatile Storage `, based on user provided key-value pairs in a CSV file. -Utility is ideally suited for generating a binary blob, containing data specific to ODM/OEM, which can be flashed externally at the time of device manufacturing. This helps manufacturers set unique value for various parameters for each device, e.g. serial number, while using same application firmware for all devices. +The utility :component_file:`nvs_flash/nvs_partition_generator/nvs_partition_gen.py` creates a binary file based on key-value pairs provided in a CSV file. The binary file is compatible with NVS architecture defined in :doc:`Non-Volatile Storage `. +This utility is ideally suited for generating a binary blob, containing data specific to ODM/OEM, which can be flashed externally at the time of device manufacturing. This allows manufacturers to generate many instances of the same application firmware with customized parameters for each device, such as a serial number. + Prerequisites ------------- -To use this utility in encryption mode, the following packages need to be installed: +To use this utility in encryption mode, install the following packages: - cryptography package -These dependencies is already captured by including these packages in `requirement.txt` in top level IDF directory. +All the required packages are included in `requirements.txt` in the root of the esp-idf directory. + CSV file format --------------- -Each row of the .csv file should have 4 parameters, separated by comma. Below is the description of each of these parameters: +Each line of a .csv file should contain 4 parameters, separated by a comma. The table below provides the description for each of these parameters. -Key - Key of the data. Data can later be accessed from an application via this key. ++-----+-----------+----------------------------------------------------------------------+-----------------------------------------------------+ +| No. | Parameter | Description | Notes | ++=====+===========+======================================================================+=====================================================+ +| 1 | Key | Key of the data. The data can be accessed later from | | +| | | an application using this key. | | ++-----+-----------+----------------------------------------------------------------------+-----------------------------------------------------+ +| 2 | Type | Supported values are ``file``, ``data`` and ``namespace``. | | ++-----+-----------+----------------------------------------------------------------------+-----------------------------------------------------+ +| 3 | Encoding | Supported values are: ``u8``, ``i8``, ``u16``, ``u32``, | As of now, for the ``file`` type, | +| | | ``i32``, ``string``, ``hex2bin``, ``base64`` and ``binary``. | only ``hex2bin``, ``base64``, ``string``, | +| | | This specifies how actual data values are encoded in the | and ``binary`` encoding is supported. | +| | | resulting binary file. The difference between the ``string`` | | +| | | and ``binary`` encoding is that ``string`` data is terminated | | +| | | with a NULL character, whereas ``binary`` data is not. | | ++-----+-----------+----------------------------------------------------------------------+-----------------------------------------------------+ +| 4 | Value | Data value. | Encoding and Value cells for the ``namespace`` | +| | | | field type should be empty. Encoding and Value | +| | | | of ``namespace`` is fixed and is not configurable. | +| | | | Any values in these cells are ignored. | ++-----+-----------+----------------------------------------------------------------------+-----------------------------------------------------+ -Type - Supported values are ``file``, ``data`` and ``namespace``. +.. note:: The first line of the CSV file should always be the column header and it is not configurable. -Encoding - Supported values are: ``u8``, ``i8``, ``u16``, ``u32``, ``i32``, ``string``, ``hex2bin``, ``base64`` and ``binary``. This specifies how actual data values are encoded in the resultant binary file. Difference between ``string`` and ``binary`` encoding is that ``string`` data is terminated with a NULL character, whereas ``binary`` data is not. - -.. note:: For ``file`` type, only ``hex2bin``, ``base64``, ``string`` and ``binary`` is supported as of now. - -Value - Data value. - -.. note:: Encoding and Value cells for ``namespace`` field type should be empty. Encoding and Value of ``namespace`` is fixed and isn't configurable. Any value in these cells are ignored. - -.. note:: First row of the CSV file should always be column header and isn't configurable. - -Below is an example dump of such CSV file:: +Below is an example dump of such a CSV file:: key,type,encoding,value <-- column header namespace_name,namespace,, <-- First entry should be of type "namespace" key1,data,u8,1 key2,file,string,/path/to/file -.. note:: Make sure there are no spaces before and after ',' or at the end of each line in CSV file. + +.. note:: + + Make sure there are **no spaces**: + - before and after ',' + - at the end of each line in a CSV file + NVS Entry and Namespace association ----------------------------------- -When a new namespace entry is encountered in the CSV file, each follow-up entries will be part of that namespace, until next namespace entry is found, in which case all the follow-up entries will be part of the new namespace. +When a namespace entry is encountered in a CSV file, each following entry will be treated as part of that namespace until the next namespace entry is found. At this point, all the following entries will be treated as part of the new namespace. + +.. note:: First entry in a CSV file should always be a ``namespace`` entry. -.. note:: First entry in a CSV file should always be ``namespace`` entry. Multipage Blob Support ---------------------- -By default, binary blobs are allowed to span over multiple pages and written in the format mentioned in section :ref:`structure_of_entry`. -If older format is intended to be used, the utility provides an option to disable this feature. +By default, binary blobs are allowed to span over multiple pages and are written in the format mentioned in Section :ref:`structure_of_entry`. +If you intend to use an older format, the utility provides an option to disable this feature. + Encryption Support ------------------- -This utility allows you to create an enrypted binary file also. Encryption used is AES-XTS encryption. Refer to :ref:`nvs_encryption` for more details. + +The NVS Partition Generator utility also allows you to create an encrypted binary file. The utility uses the AES-XTS encryption. Please refer to :ref:`nvs_encryption` for more details. + Running the utility ------------------- @@ -74,29 +91,32 @@ Running the utility [--keyfile KEYFILE] [--outdir OUTDIR] -+------------------------+----------------------------------------------------------------------------------------------+ -| Arguments | Description | -+========================+==============================================================================================+ -| --input INPUT | Path to CSV file to parse. | -+------------------------+----------------------------------------------------------------------------------------------+ -| --output OUTPUT | Path to output generated binary file. | -+------------------------+----------------------------------------------------------------------------------------------+ -| --size SIZE | Size of NVS Partition in bytes (must be multiple of 4096) | -+------------------------+----------------------------------------------------------------------------------------------+ -| --version {v1,v2} | Set version. Default: v2 | -+------------------------+----------------------------------------------------------------------------------------------+ -| --keygen {true,false} | Generate keys for encryption. | -+------------------------+----------------------------------------------------------------------------------------------+ -| --encrypt {true,false} | Set encryption mode. Default: false | -+------------------------+----------------------------------------------------------------------------------------------+ -| --keyfile KEYFILE | File having key for encryption (Applicable only if encryption mode is true) | -+------------------------+----------------------------------------------------------------------------------------------+ -| --outdir OUTDIR | The output directory to store the files created (Default: current directory) | -+------------------------+----------------------------------------------------------------------------------------------+ ++------------------------+---------------------------------------------------+-------------------+ +| Arguments | Description | Default Value | ++========================+===================================================+===================+ +| --input INPUT | Path to a CSV file to parse. | | ++------------------------+---------------------------------------------------+-------------------+ +| --output OUTPUT | Path to the generated binary file. | | ++------------------------+---------------------------------------------------+-------------------+ +| --size SIZE | Size of NVS Partition in bytes | | +| | (must be multiple of 4096). | | ++------------------------+---------------------------------------------------+-------------------+ +| --version {v1,v2} | Set version. | v2 | ++------------------------+---------------------------------------------------+-------------------+ +| --keygen {true,false} | Generate keys for encryption. | | ++------------------------+---------------------------------------------------+-------------------+ +| --encrypt {true,false} | Set encryption mode. Default: false. | false | ++------------------------+---------------------------------------------------+-------------------+ +| --keyfile KEYFILE | File containing the key for encryption | | +| | (Applicable only if encryption mode is true). | | ++------------------------+---------------------------------------------------+-------------------+ +| --outdir OUTDIR | The output directory to store the created files. | current directory | ++------------------------+---------------------------------------------------+-------------------+ You can run this utility in two modes: - - Default mode - Binary generated in this mode is an unencrypted binary file. - - Encryption mode - Binary generated in this mode is an encrypted binary file. + + - **Default mode**: You get an unencrypted binary file. + - **Encryption mode**: You get an encrypted binary file. **In default mode:** @@ -109,12 +129,11 @@ You can run this utility in two modes: [--keygen {true,false}] [--encrypt {true,false}] [--keyfile KEYFILE] [--outdir OUTDIR] -You can run the utility using below command:: +You can run the utility using the command below:: python nvs_partition_gen.py --input sample.csv --output sample.bin --size 0x3000 - **In encryption mode:** ----------------------- @@ -126,21 +145,21 @@ You can run the utility using below command:: [--version {v1,v2}] [--outdir OUTDIR] -You can run the utility using below commands: +You can run the utility using one of the commands below: - - By enabling generation of encryption keys:: + - By enabling generation of encryption keys:: - python nvs_partition_gen.py --input sample.csv --output sample_encrypted.bin --size 0x3000 --encrypt true --keygen true + python nvs_partition_gen.py --input sample.csv --output sample_encrypted.bin --size 0x3000 --encrypt true --keygen true - - By taking encryption keys as an input file. A sample encryption keys binary file is provided with the utility:: + - By taking encryption keys as an input file. A sample binary file containing encryption keys is provided with the utility:: - python nvs_partition_gen.py --input sample.csv --output sample_encrypted.bin --size 0x3000 --encrypt true --keyfile testdata/sample_encryption_keys.bin + python nvs_partition_gen.py --input sample.csv --output sample_encrypted.bin --size 0x3000 --encrypt true --keyfile testdata/sample_encryption_keys.bin - - By enabling generation of encryption keys and storing the keys in custom filename:: + - By enabling generation of encryption keys and storing the keys in a binary file with a custom filename:: - python nvs_partition_gen.py --input sample.csv --output sample_encrypted.bin --size 0x3000 --encrypt true --keygen true --keyfile encryption_keys_generated.bin + python nvs_partition_gen.py --input sample.csv --output sample_encrypted.bin --size 0x3000 --encrypt true --keygen true --keyfile encryption_keys_generated.bin -.. note:: If `--keygen` is given with `--keyfile` argument, generated keys will be stored in `--keyfile` file. If `--keygen` argument is absent, `--keyfile` is taken as input file having key for encryption. +.. note:: If `--keygen` is given with the `--keyfile` argument, generated keys will be stored in the `--keyfile` file. If `--keygen` argument is absent, `--keyfile` is taken as input file containing encryption keys. *To generate* **only** *encryption keys with this utility*:: @@ -181,7 +200,7 @@ A sample CSV file is provided with the utility:: Caveats ------- -- Utility doesn't check for duplicate keys and will write data pertaining to both keys. User needs to make sure keys are distinct. -- Once a new page is created, no data will be written in the space left in previous page. Fields in the CSV file need to be ordered in such a way so as to optimize memory. +- Utility does not check for duplicate keys and will write data pertaining to both keys. You need to make sure that the keys are distinct. +- Once a new page is created, no data will be written in the space left on the previous page. Fields in the CSV file need to be ordered in such a way as to optimize memory. - 64-bit datatype is not yet supported. diff --git a/components/spi_flash/README.rst b/components/spi_flash/README.rst index 807b592d28..04376c6140 100644 --- a/components/spi_flash/README.rst +++ b/components/spi_flash/README.rst @@ -1,80 +1,55 @@ -SPI Flash APIs -============== +SPI Flash API +============= Overview -------- -The spi_flash component contains APIs related to reading, writing, erasing, -memory mapping data in the external SPI flash. It also has higher-level -APIs which work with partitions defined in the :doc:`partition table `. +The spi_flash component contains API functions related to reading, writing, erasing, memory mapping for data in the external SPI flash. The spi_flash component also has higher-level API functions which work with partitions defined in the :doc:`partition table `. -Note that all the functionality is limited to the "main" SPI flash chip, -the same SPI flash chip from which program runs. For ``spi_flash_*`` functions, -this is a software limitation. The underlying ROM functions which work with SPI flash -do not have provisions for working with flash chips attached to SPI peripherals -other than SPI0. +Note that all the functionality is limited to the "main" SPI flash chip, the same SPI flash chip from which programs are runs. For ``spi_flash_*`` functions, this is a software limitation. The underlying ROM functions which work with SPI flash do not have provisions for working with flash chips attached to SPI peripherals other than SPI0. -SPI flash access APIs ---------------------- +SPI flash access API +-------------------- -This is the set of APIs for working with data in flash: +This is the set of API functions for working with data in flash: -- :cpp:func:`spi_flash_read` used to read data from flash to RAM -- :cpp:func:`spi_flash_write` used to write data from RAM to flash -- :cpp:func:`spi_flash_erase_sector` used to erase individual sectors of flash -- :cpp:func:`spi_flash_erase_range` used to erase range of addresses in flash +- :cpp:func:`spi_flash_read` reads data from flash to RAM +- :cpp:func:`spi_flash_write` writes data from RAM to flash +- :cpp:func:`spi_flash_erase_sector` erases individual sectors of flash +- :cpp:func:`spi_flash_erase_range` erases ranges of addresses in flash - :cpp:func:`spi_flash_get_chip_size` returns flash chip size, in bytes, as configured in menuconfig -Generally, try to avoid using the raw SPI flash functions in favour of -:ref:`partition-specific functions `. +Generally, try to avoid using the raw SPI flash functions in favor of :ref:`partition-specific functions `. SPI Flash Size -------------- -The SPI flash size is configured by writing a field in the software bootloader -image header, flashed at offset 0x1000. +The SPI flash size is configured by writing a field in the software bootloader image header, flashed at offset 0x1000. -By default, the SPI flash size is detected by esptool.py when this bootloader is -written to flash, and the header is updated with the correct -size. Alternatively, it is possible to generate a fixed flash size by setting -:envvar:`CONFIG_ESPTOOLPY_FLASHSIZE` in ``make menuconfig``. +By default, the SPI flash size is detected by esptool.py when this bootloader is written to flash, and the header is updated with the correct size. Alternatively, it is possible to generate a fixed flash size by setting :envvar:`CONFIG_ESPTOOLPY_FLASHSIZE` in ``make menuconfig``. -If it is necessary to override the configured flash size at runtime, is is -possible to set the ``chip_size`` member of ``g_rom_flashchip`` structure. This -size is used by ``spi_flash_*`` functions (in both software & ROM) for bounds -checking. +If it is necessary to override the configured flash size at runtime, it is possible to set the ``chip_size`` member of the ``g_rom_flashchip`` structure. This size is used by ``spi_flash_*`` functions (in both software & ROM) to check the bounds. Concurrency Constraints ----------------------- -Because the SPI flash is also used for firmware execution (via the instruction & -data caches), these caches must be disabled while reading/writing/erasing. This -means that both CPUs must be running code from IRAM and only reading data from -DRAM while flash write operations occur. +Because the SPI flash is also used for firmware execution via the instruction & data caches, these caches must be disabled while reading/writing/erasing. This means that both CPUs must be running code from IRAM and must only be reading data from DRAM while flash write operations occur. -If you use the APIs documented here, then this happens automatically and -transparently. However note that it will have some performance impact on other -tasks in the system. +If you use the API functions documented here, then these constraints are applied automatically and transparently. However, note that it will have some performance impact on other tasks in the system. -Refer to the :ref:`application memory layout ` documentation for -an explanation of the differences between IRAM, DRAM and flash cache. +For differences between IRAM, DRAM, and flash cache, please refer to the :ref:`application memory layout ` documentation. -To avoid reading flash cache accidentally, when one CPU commences a flash write -or erase operation the other CPU is put into a blocked state and all -non-IRAM-safe interrupts are disabled on both CPUs, until the flash operation -completes. +To avoid reading flash cache accidentally, when one CPU initiates a flash write or erase operation, the other CPU is put into a blocked state, and all non-IRAM-safe interrupts are disabled on both CPUs until the flash operation completes. + +If one CPU initiates a flash write or erase operation, the other CPU is put into a blocked state to avoid reading flash cache accidentally. All interrupts not safe for IRAM are disabled on both CPUs until the flash operation completes. .. _iram-safe-interrupt-handlers: IRAM-Safe Interrupt Handlers ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -If you have an interrupt handler that you want to execute even when a flash -operation is in progress (for example, for low latency operations), set the -``ESP_INTR_FLAG_IRAM`` flag when the :doc:`interrupt handler is registered -`. +If you have an interrupt handler that you want to execute while a flash operation is in progress (for example, for low latency operations), set the ``ESP_INTR_FLAG_IRAM`` flag when the :doc:`interrupt handler is registered `. -You must ensure all data and functions accessed by these interrupt handlers are -located in IRAM or DRAM. This includes any functions that the handler calls. +You must ensure that all data and functions accessed by these interrupt handlers, including the ones that handlers call, are located in IRAM or DRAM. Use the ``IRAM_ATTR`` attribute for functions:: @@ -93,80 +68,56 @@ Use the ``DRAM_ATTR`` and ``DRAM_STR`` attributes for constant data:: const static char *MSG = DRAM_STR("I am a string stored in RAM"); } -Note that knowing which data should be marked with ``DRAM_ATTR`` can be hard, -the compiler will sometimes recognise that a variable or expression is constant -(even if it is not marked ``const``) and optimise it into flash, unless it is -marked with ``DRAM_ATTR``. +Note that knowing which data should be marked with ``DRAM_ATTR`` can be hard, the compiler will sometimes recognize that a variable or expression is constant (even if it is not marked ``const``) and optimize it into flash, unless it is marked with ``DRAM_ATTR``. -If a function or symbol is not correctly put into IRAM/DRAM and the interrupt -handler reads from the flash cache during a flash operation, it will cause a -crash due to Illegal Instruction exception (for code which should be in IRAM) or -garbage data to be read (for constant data which should be in DRAM). +If a function or symbol is not correctly put into IRAM/DRAM, and the interrupt handler reads from the flash cache during a flash operation, it will cause a crash due to Illegal Instruction exception (for code which should be in IRAM) or garbage data to be read (for constant data which should be in DRAM). .. _flash-partition-apis: -Partition table APIs --------------------- +Partition table API +------------------- -ESP-IDF projects use a partition table to maintain information about various regions of -SPI flash memory (bootloader, various application binaries, data, filesystems). -More information about partition tables can be found :doc:`here `. +ESP-IDF projects use a partition table to maintain information about various regions of SPI flash memory (bootloader, various application binaries, data, filesystems). More information on partition tables can be found :doc:`here `. -This component provides APIs to enumerate partitions found in the partition table -and perform operations on them. These functions are declared in ``esp_partition.h``: +This component provides API functions to enumerate partitions found in the partition table and perform operations on them. These functions are declared in ``esp_partition.h``: -- :cpp:func:`esp_partition_find` used to search partition table for entries with - specific type, returns an opaque iterator -- :cpp:func:`esp_partition_get` returns a structure describing the partition, for the given iterator -- :cpp:func:`esp_partition_next` advances iterator to the next partition found -- :cpp:func:`esp_partition_iterator_release` releases iterator returned by ``esp_partition_find`` -- :cpp:func:`esp_partition_find_first` is a convenience function which returns structure - describing the first partition found by ``esp_partition_find`` -- :cpp:func:`esp_partition_read`, :cpp:func:`esp_partition_write`, :cpp:func:`esp_partition_erase_range` - are equivalent to :cpp:func:`spi_flash_read`, :cpp:func:`spi_flash_write`, - :cpp:func:`spi_flash_erase_range`, but operate within partition boundaries +- :cpp:func:`esp_partition_find` checks a partition table for entries with specific type, returns an opaque iterator. +- :cpp:func:`esp_partition_get` returns a structure describing the partition for a given iterator. +- :cpp:func:`esp_partition_next` shifts the iterator to the next found partition. +- :cpp:func:`esp_partition_iterator_release` releases iterator returned by ``esp_partition_find``. +- :cpp:func:`esp_partition_find_first` - a convenience function which returns the structure describing the first partition found by ``esp_partition_find``. +- :cpp:func:`esp_partition_read`, :cpp:func:`esp_partition_write`, :cpp:func:`esp_partition_erase_range` are equivalent to :cpp:func:`spi_flash_read`, :cpp:func:`spi_flash_write`, :cpp:func:`spi_flash_erase_range`, but operate within partition boundaries. .. note:: - Most application code should use these ``esp_partition_*`` APIs instead of lower level - ``spi_flash_*`` APIs. Partition APIs do bounds checking and calculate correct - offsets in flash based on data stored in partition table. + Application code should mostly use these ``esp_partition_*`` API functions instead of lower level ``spi_flash_*`` API functions. Partition table API functions do bounds checking and calculate correct offsets in flash, based on data stored in a partition table. SPI Flash Encryption -------------------- -It is possible to encrypt SPI flash contents, and have it transparenlty decrypted by hardware. +It is possible to encrypt the contents of SPI flash and have it transparently decrypted by hardware. Refer to the :doc:`Flash Encryption documentation ` for more details. -Memory mapping APIs -------------------- +Memory mapping API +------------------ -ESP32 features memory hardware which allows regions of flash memory to be mapped -into instruction and data address spaces. This mapping works only for read operations, -it is not possible to modify contents of flash memory by writing to mapped memory -region. Mapping happens in 64KB pages. Memory mapping hardware can map up to -4 megabytes of flash into data address space, and up to 16 megabytes of flash into -instruction address space. See the technical reference manual for more details -about memory mapping hardware. +ESP32 features memory hardware which allows regions of flash memory to be mapped into instruction and data address spaces. This mapping works only for read operations. It is not possible to modify contents of flash memory by writing to a mapped memory region. -Note that some number of 64KB pages is used to map the application -itself into memory, so the actual number of available 64KB pages may be less. +Mapping happens in 64KB pages. Memory mapping hardware can map up to four megabytes of flash into data address space and up to 16 megabytes of flash into instruction address space. See the technical reference manual for more details about memory mapping hardware. -Reading data from flash using a memory mapped region is the only way to decrypt -contents of flash when :doc:`flash encryption ` is enabled. -Decryption is performed at hardware level. +Note that some 64KB pages are used to map the application itself into memory, so the actual number of available 64KB pages may be less. -Memory mapping APIs are declared in ``esp_spi_flash.h`` and ``esp_partition.h``: +Reading data from flash using a memory mapped region is the only way to decrypt contents of flash when :doc:`flash encryption ` is enabled. Decryption is performed at the hardware level. -- :cpp:func:`spi_flash_mmap` maps a region of physical flash addresses into instruction space or data space of the CPU -- :cpp:func:`spi_flash_munmap` unmaps previously mapped region -- :cpp:func:`esp_partition_mmap` maps part of a partition into the instruction space or data space of the CPU +Memory mapping API are declared in ``esp_spi_flash.h`` and ``esp_partition.h``: + +- :cpp:func:`spi_flash_mmap` maps a region of physical flash addresses into instruction space or data space of the CPU. +- :cpp:func:`spi_flash_munmap` unmaps previously mapped region. +- :cpp:func:`esp_partition_mmap` maps part of a partition into the instruction space or data space of the CPU. Differences between :cpp:func:`spi_flash_mmap` and :cpp:func:`esp_partition_mmap` are as follows: -- :cpp:func:`spi_flash_mmap` must be given a 64KB aligned physical address -- :cpp:func:`esp_partition_mmap` may be given any arbitrary offset within the partition, - it will adjust returned pointer to mapped memory as necessary +- :cpp:func:`spi_flash_mmap` must be given a 64KB aligned physical address. +- :cpp:func:`esp_partition_mmap` may be given any arbitrary offset within the partition, it will adjust the returned pointer to mapped memory as necessary -Note that because memory mapping happens in 64KB blocks, it may be possible to -read data outside of the partition provided to ``esp_partition_mmap``. +Note that since memory mapping happens in 64KB blocks, it may be possible to read data outside of the partition provided to ``esp_partition_mmap``. diff --git a/components/vfs/README.rst b/components/vfs/README.rst index 95ccefd840..20bc9770e7 100644 --- a/components/vfs/README.rst +++ b/components/vfs/README.rst @@ -4,18 +4,17 @@ Virtual filesystem component Overview -------- -Virtual filesystem (VFS) component provides a unified interface for drivers which can perform operations on file-like objects. This can be a real filesystems (FAT, SPIFFS, etc.), or device drivers which exposes file-like interface. +Virtual filesystem (VFS) component provides a unified interface for drivers which can perform operations on file-like objects. These can be real filesystems (FAT, SPIFFS, etc.) or device drivers which provide a file-like interface. -This component allows C library functions, such as fopen and fprintf, to work with FS drivers. At high level, each FS driver is associated with some path prefix. When one of C library functions needs to open a file, VFS component searches for the FS driver associated with the file's path, and forwards the call to that driver. VFS also forwards read, write, and other calls for the given file to the same FS driver. +This component allows C library functions, such as fopen and fprintf, to work with FS drivers. At a high level, each FS driver is associated with some path prefix. When one of C library functions needs to open a file, the VFS component searches for the FS driver associated with the file path and forwards the call to that driver. VFS also forwards read, write, and other calls for the given file to the same FS driver. + +For example, one can register a FAT filesystem driver with the ``/fat`` prefix and call ``fopen("/fat/file.txt", "w")``. The VFS component will then call the function ``open`` of the FAT driver and pass the argument ``/file.txt`` to it together with appropriate mode flags. All subsequent calls to C library functions for the returned ``FILE*`` stream will also be forwarded to the FAT driver. -For example, one can register a FAT filesystem driver with ``/fat`` prefix, and call ``fopen("/fat/file.txt", "w")``. VFS component will then call ``open`` function of FAT driver and pass ``/file.txt`` argument to it (and appropriate mode flags). All subsequent calls to C library functions for the returned ``FILE*`` stream will also be forwarded to the FAT driver. FS registration --------------- - - -To register an FS driver, application needs to define in instance of :cpp:type:`esp_vfs_t` structure and populate it with function pointers to FS APIs: +To register an FS driver, an application needs to define an instance of the :cpp:type:`esp_vfs_t` structure and populate it with function pointers to FS APIs: .. highlight:: c @@ -32,9 +31,9 @@ To register an FS driver, application needs to define in instance of :cpp:type:` ESP_ERROR_CHECK(esp_vfs_register("/data", &myfs, NULL)); -Depending on the way FS driver declares its APIs, either ``read``, ``write``, etc., or ``read_p``, ``write_p``, etc. should be used. +Depending on the way how the FS driver declares its API functions, either ``read``, ``write``, etc., or ``read_p``, ``write_p``, etc., should be used. -Case 1: API functions are declared without an extra context pointer (FS driver is a singleton):: +Case 1: API functions are declared without an extra context pointer (the FS driver is a singleton):: ssize_t myfs_write(int fd, const void * data, size_t size); @@ -46,7 +45,7 @@ Case 1: API functions are declared without an extra context pointer (FS driver i // When registering FS, context pointer (third argument) is NULL: ESP_ERROR_CHECK(esp_vfs_register("/data", &myfs, NULL)); -Case 2: API functions are declared with an extra context pointer (FS driver supports multiple instances):: +Case 2: API functions are declared with an extra context pointer (the FS driver supports multiple instances):: ssize_t myfs_write(myfs_t* fs, int fd, const void * data, size_t size); @@ -68,8 +67,8 @@ Synchronous input/output multiplexing ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If you want to use synchronous input/output multiplexing by :cpp:func:`select` -then you need to register the VFS with :cpp:func:`start_select` and -:cpp:func:`end_select` functions similarly to the following example: +then you need to register VFS with the functions :cpp:func:`start_select` and +:cpp:func:`end_select` similar to the following example: .. highlight:: c @@ -85,22 +84,27 @@ detection of read/write/error conditions on file descriptors belonging to the given VFS. :cpp:func:`end_select` is called to stop/deinitialize/free the environment which was setup by :cpp:func:`start_select`. Please refer to the reference implementation for the UART peripheral in -:component_file:`vfs/vfs_uart.c` and most particularly to functions -:cpp:func:`esp_vfs_dev_uart_register`, :cpp:func:`uart_start_select` and +:component_file:`vfs/vfs_uart.c` and most particularly to the functions +:cpp:func:`esp_vfs_dev_uart_register`, :cpp:func:`uart_start_select`, and :cpp:func:`uart_end_select`. -Examples demonstrating the use of :cpp:func:`select` with VFS file descriptors -are the :example:`peripherals/uart_select` and the :example:`system/select` -examples. +Please check the following examples that demonstrate the use of :cpp:func:`select` with VFS file descriptors: +- :example:`peripherals/uart_select` +- :example:`system/select` +<<<<<<< HEAD If :cpp:func:`select` is used for socket file descriptors only then one can enable the :envvar:`CONFIG_LWIP_USE_ONLY_LWIP_SELECT` option which can reduce the code +======= +If you use :cpp:func:`select` for socket file descriptors, you can enable the :envvar:`CONFIG_LWIP_USE_ONLY_LWIP_SELECT` option to reduce the code +>>>>>>> afc2fdf27... Review all the files in the esp-idf's api_ref/storage directory size and improve performance. + Paths ----- -Each registered FS has a path prefix associated with it. This prefix may be considered a "mount point" of this partition. +Each registered FS has a path prefix associated with it. This prefix can be considered as a "mount point" of this partition. In case when mount points are nested, the mount point with the longest matching path prefix is used when opening the file. For instance, suppose that the following filesystems are registered in VFS: @@ -111,45 +115,49 @@ Then: - FS 1 will be used when opening a file called ``/data/log.txt`` - FS 2 will be used when opening a file called ``/data/static/index.html`` -- Even if ``/index.html"`` doesn't exist in FS 2, FS 1 will *not* be searched for ``/static/index.html``. +- Even if ``/index.html"`` does not exist in FS 2, FS 1 will *not* be searched for ``/static/index.html``. -As a general rule, mount point names must start with the path separator (``/``) and must contain at least one character after path separator. However an empty mount point name is also supported, and may be used in cases when application needs to provide "fallback" filesystem, or override VFS functionality altogether. Such filesystem will be used if no prefix matches the path given. +As a general rule, mount point names must start with the path separator (``/``) and must contain at least one character after path separator. However, an empty mount point name is also supported and might be used in cases when an application needs to provide a "fallback" filesystem or to override VFS functionality altogether. Such filesystem will be used if no prefix matches the path given. -VFS does not handle dots (``.``) in path names in any special way. VFS does not treat ``..`` as a reference to the parent directory. I.e. in the above example, using a path ``/data/static/../log.txt`` will not result in a call to FS 1 to open ``/log.txt``. Specific FS drivers (such as FATFS) may handle dots in file names differently. +VFS does not handle dots (``.``) in path names in any special way. VFS does not treat ``..`` as a reference to the parent directory. In the above example, using a path ``/data/static/../log.txt`` will not result in a call to FS 1 to open ``/log.txt``. Specific FS drivers (such as FATFS) might handle dots in file names differently. -When opening files, FS driver will only be given relative path to files. For example: +When opening files, the FS driver receives only relative paths to files. For example: -- ``myfs`` driver is registered with ``/data`` as path prefix -- and application calls ``fopen("/data/config.json", ...)`` -- then VFS component will call ``myfs_open("/config.json", ...)``. -- ``myfs`` driver will open ``/config.json`` file +1. The ``myfs`` driver is registered with ``/data`` as a path prefix. +2. The application calls ``fopen("/data/config.json", ...)``. +3. The VFS component calls ``myfs_open("/config.json", ...)``. +4. The ``myfs`` driver opens the ``/config.json`` file. + +VFS does not impose any limit on total file path length, but it does limit the FS path prefix to ``ESP_VFS_PATH_MAX`` characters. Individual FS drivers may have their own filename length limitations. -VFS doesn't impose a limit on total file path length, but it does limit FS path prefix to ``ESP_VFS_PATH_MAX`` characters. Individual FS drivers may have their own filename length limitations. File descriptors ---------------- -File descriptors are small positive integers from ``0`` to ``FD_SETSIZE - 1`` where ``FD_SETSIZE`` is defined in newlib's ``sys/types.h``. The largest file descriptors (configured by ``CONFIG_LWIP_MAX_SOCKETS``) are reserved for sockets. The VFS component contains a lookup-table called ``s_fd_table`` for mapping global file descriptors to VFS driver indexes registered in the ``s_vfs`` array. +File descriptors are small positive integers from ``0`` to ``FD_SETSIZE - 1``, where ``FD_SETSIZE`` is defined in newlib's ``sys/types.h``. The largest file descriptors (configured by ``CONFIG_LWIP_MAX_SOCKETS``) are reserved for sockets. The VFS component contains a lookup-table called ``s_fd_table`` for mapping global file descriptors to VFS driver indexes registered in the ``s_vfs`` array. + Standard IO streams (stdin, stdout, stderr) ------------------------------------------- -If "UART for console output" menuconfig option is not set to "None", then ``stdin``, ``stdout``, and ``stderr`` are configured to read from, and write to, a UART. It is possible to use UART0 or UART1 for standard IO. By default, UART0 is used, with 115200 baud rate, TX pin is GPIO1 and RX pin is GPIO3. These parameters can be changed in menuconfig. +If the menuconfig option ``UART for console output`` is not set to ``None``, then ``stdin``, ``stdout``, and ``stderr`` are configured to read from, and write to, a UART. It is possible to use UART0 or UART1 for standard IO. By default, UART0 is used with 115200 baud rate; TX pin is GPIO1; RX pin is GPIO3. These parameters can be changed in menuconfig. Writing to ``stdout`` or ``stderr`` will send characters to the UART transmit FIFO. Reading from ``stdin`` will retrieve characters from the UART receive FIFO. -By default, VFS uses simple functions for reading from and writing to UART. Writes busy-wait until all data is put into UART FIFO, and reads are non-blocking, returning only the data present in the FIFO. Because of this non-blocking read behavior, higher level C library calls, such as ``fscanf("%d\n", &var);`` may not have desired results. +By default, VFS uses simple functions for reading from and writing to UART. Writes busy-wait until all data is put into UART FIFO, and reads are non-blocking, returning only the data present in the FIFO. Due to this non-blocking read behavior, higher level C library calls, such as ``fscanf("%d\n", &var);``, might not have desired results. -Applications which use UART driver may instruct VFS to use the driver's interrupt driven, blocking read and write functions instead. This can be done using a call to ``esp_vfs_dev_uart_use_driver`` function. It is also possible to revert to the basic non-blocking functions using a call to ``esp_vfs_dev_uart_use_nonblocking``. +Applications which use the UART driver can instruct VFS to use the driver's interrupt driven, blocking read and write functions instead. This can be done using a call to the ``esp_vfs_dev_uart_use_driver`` function. It is also possible to revert to the basic non-blocking functions using a call to ``esp_vfs_dev_uart_use_nonblocking``. -VFS also provides optional newline conversion feature for input and output. Internally, most applications send and receive lines terminated by LF (''\n'') character. Different terminal programs may require different line termination, such as CR or CRLF. Applications can configure this separately for input and output either via menuconfig, or by calls to ``esp_vfs_dev_uart_set_rx_line_endings`` and ``esp_vfs_dev_uart_set_tx_line_endings`` functions. +VFS also provides an optional newline conversion feature for input and output. Internally, most applications send and receive lines terminated by the LF (''\n'') character. Different terminal programs may require different line termination, such as CR or CRLF. Applications can configure this separately for input and output either via menuconfig, or by calls to the functions ``esp_vfs_dev_uart_set_rx_line_endings`` and ``esp_vfs_dev_uart_set_tx_line_endings``. Standard streams and FreeRTOS tasks ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -``FILE`` objects for ``stdin``, ``stdout``, and ``stderr`` are shared between all FreeRTOS tasks, but the pointers to these objects are are stored in per-task ``struct _reent``. The following code: +``FILE`` objects for ``stdin``, ``stdout``, and ``stderr`` are shared between all FreeRTOS tasks, but the pointers to these objects are stored in per-task ``struct _reent``. + +The following code is transferred to ``fprintf(__getreent()->_stderr, "42\n");`` by the preprocessor: .. highlight:: c @@ -157,14 +165,11 @@ Standard streams and FreeRTOS tasks fprintf(stderr, "42\n"); -actually is translated to to this (by the preprocessor):: - fprintf(__getreent()->_stderr, "42\n"); - -where the ``__getreent()`` function returns a per-task pointer to ``struct _reent`` (:component_file:`newlib/include/sys/reent.h#L370-L417`). This structure is allocated on the TCB of each task. When a task is initialized, ``_stdin``, ``_stdout`` and ``_stderr`` members of ``struct _reent`` are set to the values of ``_stdin``, ``_stdout`` and ``_stderr`` of ``_GLOBAL_REENT`` (i.e. the structure which is used before FreeRTOS is started). +The ``__getreent()`` function returns a per-task pointer to ``struct _reent`` (:component_file:`newlib/include/sys/reent.h#L370-L417`). This structure is allocated on the TCB of each task. When a task is initialized, ``_stdin``, ``_stdout``, and ``_stderr`` members of ``struct _reent`` are set to the values of ``_stdin``, ``_stdout``, and ``_stderr`` of ``_GLOBAL_REENT`` (i.e., the structure which is used before FreeRTOS is started). Such a design has the following consequences: -- It is possible to set ``stdin``, ``stdout``, and ``stderr`` for any given task without affecting other tasks, e.g. by doing ``stdin = fopen("/dev/uart/1", "r")``. -- Closing default ``stdin``, ``stdout``, or ``stderr`` using ``fclose`` will close the ``FILE`` stream object — this will affect all other tasks. +- It is possible to set ``stdin``, ``stdout``, and ``stderr`` for any given task without affecting other tasks, e.g., by doing ``stdin = fopen("/dev/uart/1", "r")``. +- Closing default ``stdin``, ``stdout``, or ``stderr`` using ``fclose`` will close the ``FILE`` stream object, which will affect all other tasks. - To change the default ``stdin``, ``stdout``, ``stderr`` streams for new tasks, modify ``_GLOBAL_REENT->_stdin`` (``_stdout``, ``_stderr``) before creating the task. diff --git a/components/wear_levelling/README.rst b/components/wear_levelling/README.rst index 88d54ee749..44def742e5 100644 --- a/components/wear_levelling/README.rst +++ b/components/wear_levelling/README.rst @@ -1,55 +1,47 @@ -Wear Levelling APIs -=================== +Wear Levelling API +================== Overview -------- -Most of the flash devices and specially SPI flash devices that are used in ESP32 -have sector based organization and have limited amount of erase/modification cycles -per memory sector. To avoid situation when one sector reach the limit of erases when -other sectors was used not often, we have made a component that avoid this situation. -The wear levelling component share the amount of erases between all sectors in the -memory without user interaction. -The wear_levelling component contains APIs related to reading, writing, erasing, -memory mapping data in the external SPI flash through the partition component. It -also has higher-level APIs which work with FAT filesystem defined in -the :doc:`FAT filesystem `. +Most of flash memory and especially SPI flash that is used in ESP32 has a sector-based organization and also has a limited number of erase/modification cycles per memory sector. The wear levelling component helps to distribute wear and tear among sectors more evenly without requiring any attention from the user. -The wear levelling component, together with FAT FS component, works with FAT FS sector size 4096 -bytes which is standard size of the flash devices. In this mode the component has best performance, -but needs additional memoty in the RAM. To save internal memory the component has two additional modes -to work with sector size 512 bytes: Performance and Safety modes. In Performance mode by erase sector -operation data will be stored to the RAM, sector will be erased and then data will be stored -back to the flash. If by this operation power off situation will occur, the complete 4096 bytes -will be lost. To prevent this the Safety mode was implemented. In safety mode the data will be first -stored to the flash and after sector will be erased, will be stored back. If power off situation will -occur, after power on, the data will be recovered. -By default defined the sector size 512 bytes and Performance mode. To change these values please use -the configuration menu. +The wear levelling component provides API functions related to reading, writing, erasing, and memory mapping of data in external SPI flash through the partition component. The component also has higher-level API functions which work with the FAT filesystem defined in :doc:`FAT filesystem `. + +The wear levelling component, together with the FAT FS component, uses FAT FS sectors of 4096 bytes, which is a standard size for flash memory. With this size, the component shows the best performance but needs additional memory in RAM. + +To save internal memory, the component has two additional modes which both use sectors of 512 bytes: + +- **Performance mode.** Erase sector operation data is stored in RAM, the sector is erased, and then data is copied back to flash memory. However, if a device is powered off for any reason, all 4096 bytes of data is lost. +- **Safety mode.** The data is first saved to flash memory, and after the sector is erased, the data is saved back. If a device is powered off, the data can be recovered as soon as the device boots up. + +The default settings are as follows: +- Sector size is 512 bytes +- Performance mode + +You can change the settings through the configuration menu. -The wear levelling component does not cache data in RAM. Write and erase functions -modify flash directly, and flash contents is consistent when the function returns. +The wear levelling component does not cache data in RAM. The write and erase functions modify flash directly, and flash contents are consistent when the function returns. -Wear Levelling access APIs --------------------------- +Wear Levelling access API functions +----------------------------------- -This is the set of APIs for working with data in flash: +This is the set of API functions for working with data in flash: -- ``wl_mount`` mount wear levelling module for defined partition -- ``wl_unmount`` used to unmount levelling module -- ``wl_erase_range`` used to erase range of addresses in flash -- ``wl_write`` used to write data to the partition -- ``wl_read`` used to read data from the partition -- ``wl_size`` return size of avalible memory in bytes -- ``wl_sector_size`` returns size of one sector +- ``wl_mount`` - initializes the wear levelling module and mounts the specified partition +- ``wl_unmount`` - unmounts the partition and deinitializes the wear levelling module +- ``wl_erase_range`` - erases a range of addresses in flash +- ``wl_write`` - writes data to a partition +- ``wl_read`` - reads data from a partition +- ``wl_size`` - returns the size of available memory in bytes +- ``wl_sector_size`` - returns the size of one sector + +As a rule, try to avoid using raw wear levelling functions and use filesystem-specific functions instead. -Generally, try to avoid using the raw wear levelling functions in favor of -filesystem-specific functions. Memory Size ----------- -The memory size calculated in the wear Levelling module based on parameters of -partition. The module use few sectors of flash for internal data. +The memory size is calculated in the wear levelling module based on partition parameters. The module uses some sectors of flash for internal data. diff --git a/docs/en/api-reference/storage/fatfs.rst b/docs/en/api-reference/storage/fatfs.rst index 6ad0163e12..8abb59da4a 100644 --- a/docs/en/api-reference/storage/fatfs.rst +++ b/docs/en/api-reference/storage/fatfs.rst @@ -1,36 +1,43 @@ FAT Filesystem Support ====================== -ESP-IDF uses `FatFs `_ library to work with FAT filesystems. FatFs library resides in ``fatfs`` component. Although it can be used directly, many of its features can be accessed via VFS using C standard library and POSIX APIs. +ESP-IDF uses the `FatFs `_ library to work with FAT filesystems. FatFs resides in the ``fatfs`` component. Although the library can be used directly, many of its features can be accessed via VFS, using the C standard library and POSIX API functions. + +Additionally, FatFs has been modified to support the runtime pluggable disk I/O layer. This allows mapping of FatFs drives to physical disks at runtime. -Additionally, FatFs has been modified to support run-time pluggable disk IO layer. This allows mapping of FatFs drives to physical disks at run-time. Using FatFs with VFS -------------------- -:component_file:`fatfs/src/esp_vfs_fat.h` header file defines functions to connect FatFs with VFS. :cpp:func:`esp_vfs_fat_register` function allocates a ``FATFS`` structure, and registers a given path prefix in VFS. Subsequent operations on files starting with this prefix are forwarded to FatFs APIs. :cpp:func:`esp_vfs_fat_unregister_path` function deletes the registration with VFS, and frees the ``FATFS`` structure. +The header file :component_file:`fatfs/src/esp_vfs_fat.h` defines the functions for connecting FatFs and VFS. -Most applications will use the following flow when working with ``esp_vfs_fat_`` functions: +The function :cpp:func:`esp_vfs_fat_register` allocates a ``FATFS`` structure and registers a given path prefix in VFS. Subsequent operations on files starting with this prefix are forwarded to FatFs APIs. +The function :cpp:func:`esp_vfs_fat_unregister_path` deletes the registration with VFS, and frees the ``FATFS`` structure. -1. Call :cpp:func:`esp_vfs_fat_register`, specifying path prefix where the filesystem has to be mounted (e.g. ``"/sdcard"``, ``"/spiflash"``), FatFs drive number, and a variable which will receive a pointer to ``FATFS`` structure. +Most applications use the following workflow when working with ``esp_vfs_fat_`` functions: -2. Call :cpp:func:`ff_diskio_register` function to register disk IO driver for the drive number used in step 1. +1. Call :cpp:func:`esp_vfs_fat_register` to specify: + - Path prefix where to mount the filesystem (e.g. ``"/sdcard"``, ``"/spiflash"``) + - FatFs drive number + - A variable which will receive the pointer to the ``FATFS`` structure -3. Call FatFs ``f_mount`` function (and optionally ``f_fdisk``, ``f_mkfs``) to mount the filesystem using the same drive number which was passed to :cpp:func:`esp_vfs_fat_register`. See `FatFs documentation for more details `. +2. Call :cpp:func:`ff_diskio_register` to register the disk I/O driver for the drive number used in Step 1. -4. Call POSIX and C standard library functions to open, read, write, erase, copy files, etc. Use paths starting with the prefix passed to :cpp:func:`esp_vfs_register` (such as ``"/sdcard/hello.txt"``). +3. Call the FatFs function ``f_mount``, and optionally ``f_fdisk``, ``f_mkfs``, to mount the filesystem using the same drive number which was passed to :cpp:func:`esp_vfs_fat_register`. For more information, see `FatFs documentation `. -5. Optionally, call FatFs library functions directly. Use paths without a VFS prefix in this case (``"/hello.txt"``). +4. Call the C standard library and POSIX API functions to perform such actions on files as open, read, write, erase, copy, etc. Use paths starting with the path prefix passed to :cpp:func:`esp_vfs_register` (for example, ``"/sdcard/hello.txt"``). + +5. Optionally, call the FatFs library functions directly. In this case, use paths without a VFS prefix (for example, ``"/hello.txt"``). 6. Close all open files. -7. Call FatFs ``f_mount`` function for the same drive number, with NULL ``FATFS*`` argument, to unmount the filesystem. +7. Call the FatFs function ``f_mount`` for the same drive number, with NULL ``FATFS*`` argument, to unmount the filesystem. -8. Call FatFs :cpp:func:`ff_diskio_register` with NULL ``ff_diskio_impl_t*`` argument and the same drive number. +8. Call the FatFs function :cpp:func:`ff_diskio_register` with NULL ``ff_diskio_impl_t*`` argument and the same drive number to unregister the disk I/O driver. -9. Call :cpp:func:`esp_vfs_fat_unregister_path` with the path where the file system is mounted to remove FatFs from VFS, and free the ``FATFS`` structure allocated on step 1. +9. Call :cpp:func:`esp_vfs_fat_unregister_path` with the path where the file system is mounted to remove FatFs from VFS, and free the ``FATFS`` structure allocated in Step 1. -Convenience functions, ``esp_vfs_fat_sdmmc_mount`` and ``esp_vfs_fat_sdmmc_unmount``, which wrap these steps and also handle SD card initialization, are described in the next section. +The convenience functions ``esp_vfs_fat_sdmmc_mount`` and ``esp_vfs_fat_sdmmc_unmount`` wrap the steps described above and also handle SD card initialization. These two functions are described in the next section. .. doxygenfunction:: esp_vfs_fat_register .. doxygenfunction:: esp_vfs_fat_unregister_path @@ -39,7 +46,9 @@ Convenience functions, ``esp_vfs_fat_sdmmc_mount`` and ``esp_vfs_fat_sdmmc_unmou Using FatFs with VFS and SD cards --------------------------------- -:component_file:`fatfs/src/esp_vfs_fat.h` header file also provides a convenience function to perform steps 1–3 and 7–9, and also handle SD card initialization: :cpp:func:`esp_vfs_fat_sdmmc_mount`. This function does only limited error handling. Developers are encouraged to look at its source code and incorporate more advanced versions into production applications. :cpp:func:`esp_vfs_fat_sdmmc_unmount` function unmounts the filesystem and releases resources acquired by :cpp:func:`esp_vfs_fat_sdmmc_mount`. +The header file :component_file:`fatfs/src/esp_vfs_fat.h` defines convenience functions :cpp:func:`esp_vfs_fat_sdmmc_mount` and :cpp:func:`esp_vfs_fat_sdmmc_unmount`. These function perform Steps 1–3 and 7–9 respectively and handle SD card initialization, but provide only limited error handling. Developers are encouraged to check its source code and incorporate more advanced features into production applications. + +The convenience function :cpp:func:`esp_vfs_fat_sdmmc_unmount` unmounts the filesystem and releases the resources acquired by :cpp:func:`esp_vfs_fat_sdmmc_mount`. .. doxygenfunction:: esp_vfs_fat_sdmmc_mount .. doxygenstruct:: esp_vfs_fat_mount_config_t @@ -50,17 +59,18 @@ Using FatFs with VFS and SD cards Using FatFs with VFS in read-only mode -------------------------------------- -Convenience functions, :cpp:func:`esp_vfs_fat_rawflash_mount` and :cpp:func:`esp_vfs_fat_rawflash_unmount`, are provided by :component_file:`fatfs/src/esp_vfs_fat.h` header file in order to perform steps 1-3 and 7-9 for read-only FAT partitions. These are particularly helpful for data partitions written only once during factory provisioning and need not be changed by production application throughout the lifetime. +The header file :component_file:`fatfs/src/esp_vfs_fat.h` also defines the convenience functions :cpp:func:`esp_vfs_fat_rawflash_mount` and :cpp:func:`esp_vfs_fat_rawflash_unmount`. These functions perform Steps 1-3 and 7-9 respectively for read-only FAT partitions. These are particularly helpful for data partitions written only once during factory provisioning which will not be changed by production application throughout the lifetime of the hardware. .. doxygenfunction:: esp_vfs_fat_rawflash_mount .. doxygenfunction:: esp_vfs_fat_rawflash_unmount + FatFS disk IO layer ------------------- -FatFs has been extended with an API to register disk IO driver at runtime. +FatFs has been extended with API functions that register the disk I/O driver at runtime. -Implementation of disk IO functions for SD/MMC cards is provided. It can be registered for the given FatFs drive number using :cpp:func:`ff_diskio_register_sdmmc` function. +They provide implementation of disk I/O functions for SD/MMC cards and can be registered for the given FatFs drive number using the function :cpp:func:`ff_diskio_register_sdmmc`. .. doxygenfunction:: ff_diskio_register .. doxygenstruct:: ff_diskio_impl_t diff --git a/docs/en/api-reference/storage/index.rst b/docs/en/api-reference/storage/index.rst index b4a6903560..d80e30b56e 100644 --- a/docs/en/api-reference/storage/index.rst +++ b/docs/en/api-reference/storage/index.rst @@ -15,4 +15,4 @@ Storage API Mass Manufacturing Utility -Example code for this API section is provided in :example:`storage` directory of ESP-IDF examples. +Code examples for this API section are provided in the :example:`storage` directory of ESP-IDF examples. diff --git a/docs/en/api-reference/storage/nvs_flash.rst b/docs/en/api-reference/storage/nvs_flash.rst index 4daa17ea21..b3e0e38c92 100644 --- a/docs/en/api-reference/storage/nvs_flash.rst +++ b/docs/en/api-reference/storage/nvs_flash.rst @@ -3,29 +3,29 @@ NVS Partition Generator Utility ------------------------------- -This utility helps in generating NVS-esque partition binary file which can be flashed separately on a dedicated partition via a flashing utility. Key-value pairs to be flashed onto the partition can be provided via a CSV file. Refer to :doc:`NVS Partition Generator Utility ` for more details. +This utility helps generate NVS partition binary files which can be flashed separately on a dedicated partition via a flashing utility. Key-value pairs to be flashed onto the partition can be provided via a CSV file. For more details, please refer to :doc:`NVS Partition Generator Utility `. Application Example ------------------- -Two examples are provided in :example:`storage` directory of ESP-IDF examples: +You can find two code examples in the :example:`storage` directory of ESP-IDF examples: :example:`storage/nvs_rw_value` - Demonstrates how to read and write a single integer value using NVS. + Demonstrates how to read a single integer value from, and write it to NVS. - The value holds the number of ESP32 module restarts. Since it is written to NVS, the value is preserved between restarts. + The value checked in this example holds the number of the ESP32 module restarts. The value's function as a counter is only possible due to its storing in NVS. - Example also shows how to check if read / write operation was successful, or certain value is not initialized in NVS. Diagnostic is provided in plain text to help track program flow and capture any issues on the way. + The example also shows how to check if a read / write operation was successful, or if a certain value has not been initialized in NVS. The diagnostic procedure is provided in plain text to help you track the program flow and capture any issues on the way. :example:`storage/nvs_rw_blob` - Demonstrates how to read and write a single integer value and a blob (binary large object) using NVS to preserve them between ESP32 module restarts. + Demonstrates how to read a single integer value and a blob (binary large object), and write them to NVS to preserve this value between ESP32 module restarts. - * value - tracks number of ESP32 module soft and hard restarts. - * blob - contains a table with module run times. The table is read from NVS to dynamically allocated RAM. New run time is added to the table on each manually triggered soft restart and written back to NVS. Triggering is done by pulling down GPIO0. + * value - tracks the number of the ESP32 module soft and hard restarts. + * blob - contains a table with module run times. The table is read from NVS to dynamically allocated RAM. A new run time is added to the table on each manually triggered soft restart, and then the added run time is written to NVS. Triggering is done by pulling down GPIO0. - Example also shows how to implement diagnostics if read / write operation was successful. + The example also shows how to implement the diagnostic procedure to check if the read / write operation was successful. API Reference diff --git a/docs/en/api-reference/storage/sdmmc.rst b/docs/en/api-reference/storage/sdmmc.rst index 2aae6a09c1..2b7490ddb3 100644 --- a/docs/en/api-reference/storage/sdmmc.rst +++ b/docs/en/api-reference/storage/sdmmc.rst @@ -4,77 +4,93 @@ SD/SDIO/MMC Driver Overview -------- -SD/SDIO/MMC driver currently supports SD memory, SDIO cards, and eMMC chips. This protocol level driver builds on top of SDMMC and SD SPI host drivers. +The SD/SDIO/MMC driver currently supports SD memory, SDIO cards, and eMMC chips. This is a protocol level driver built on top of SDMMC and SD SPI host drivers. -SDMMC and SD SPI host drivers (``driver/sdmmc_host.h``) provide APIs to send commands to the slave device(s), send and receive data, and handle error conditions on the bus. - -- See :doc:`SDMMC Host API <../peripherals/sdmmc_host>` for functions used to initialize and configure SDMMC host. -- See :doc:`SD SPI Host API <../peripherals/sdspi_host>` for functions used to initialize and configure SD SPI host. +SDMMC and SD SPI host drivers (:component:`driver/include/driver/sdmmc_host.h`) provide API functions for: -SDMMC protocol layer (``sdmmc_cmd.h``), described in this document, handles specifics of SD protocol such as card initialization and data transfer commands. +- Sending commands to slave devices +- Sending and receiving data +- Handling error conditions within the bus + +For functions used to initialize and configure: + +- SDMMC host, see :doc:`SDMMC Host API <../peripherals/sdmmc_host>` +- SD SPI host, see :doc:`SD SPI Host API <../peripherals/sdspi_host>` + + +The SDMMC protocol layer described in this document handles the specifics of the SD protocol, such as the card initialization and data transfer commands. + +The protocol layer works with the host via the :cpp:class:`sdmmc_host_t` structure. This structure contains pointers to various functions of the host. -Protocol layer works with the host via :cpp:class:`sdmmc_host_t` structure. This structure contains pointers to various functions of the host. Application Example ------------------- -An example which combines SDMMC driver with FATFS library is provided in ``examples/storage/sd_card`` directory. This example initializes the card, writes and reads data from it using POSIX and C library APIs. See README.md file in the example directory for more information. +An example which combines the SDMMC driver with the FATFS library is provided in the :example:`storage/sd_card` directory of ESP-IDF examples. This example initializes the card, then writes and reads data from it using POSIX and C library APIs. See README.md file in the example directory for more information. -Protocol layer APIs -------------------- -Protocol layer is given :cpp:class:`sdmmc_host_t` structure which describes the SD/MMC host driver, lists its capabilites, and provides pointers to functions of the driver. Protocol layer stores card-specific information in :cpp:class:`sdmmc_card_t` structure. When sending commands to the SD/MMC host driver, protocol layer uses :cpp:class:`sdmmc_command_t` structure to describe the command, argument, expected return value, and data to transfer, if any. +Protocol layer API +------------------ -Usage with SD memory cards -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -1. Call the host driver functions to initialize the host (e.g. :cpp:func:`sdmmc_host_init`, :cpp:func:`sdmmc_host_init_slot`). -2. Call :cpp:func:`sdmmc_card_init` to initialize the card, passing it host driver information (``host``) and a pointer to :cpp:class:`sdmmc_card_t` structure which will be filled in (``card``). -3. To read and write sectors of the card, use :cpp:func:`sdmmc_read_sectors` and :cpp:func:`sdmmc_write_sectors`, passing the pointer to card information structure (``card``). -4. When card is not used anymore, call the host driver function to disable the host peripheral and free resources allocated by the driver (e.g. :cpp:func:`sdmmc_host_deinit`). - -Usage with eMMC chips -^^^^^^^^^^^^^^^^^^^^^ - -From the perspective of the protocol layer, eMMC memory chips behave the same way as SD memory cards. Because of similarity of the protocol, even though eMMC are chips don't have the "card" form factor, same terminology is used as for SD cards (`sdmmc_card_t`, `sdmmc_card_init`). Note that eMMC chips can not be used over SPI, therefore are incompatible with SD SPI host driver. - -To initialize eMMC memory and do read/write operations, follow the steps listed above for SD cards. +The protocol layer is given the :cpp:class:`sdmmc_host_t` structure. This structure describes the SD/MMC host driver, lists its capabilities, and provides pointers to functions of the driver. The protocol layer stores card-specific information in the :cpp:class:`sdmmc_card_t` structure. When sending commands to the SD/MMC host driver, the protocol layer uses the :cpp:class:`sdmmc_command_t` structure to describe the command, arguments, expected return values, and data to transfer if there is any. -Usage with SDIO cards -^^^^^^^^^^^^^^^^^^^^^ +Using API with SD memory cards +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Initialization an probing process is the same as with SD memory cards. Only data transfer commands differ in SDIO mode. +1. To initialize the host, call the host driver functions, e.g., :cpp:func:`sdmmc_host_init`, :cpp:func:`sdmmc_host_init_slot`. +2. To initialize the card, call :cpp:func:`sdmmc_card_init` and pass to it the parameters ``host`` - the host driver information, and ``card`` - a pointer to the structure :cpp:class:`sdmmc_card_t` which will be filled with information about the card when the function completes. +3. To read and write sectors of the card, use :cpp:func:`sdmmc_read_sectors` and :cpp:func:`sdmmc_write_sectors` respectively and pass to it the parameter ``card`` - a pointer to the card information structure. +4. If the card is not used anymore, call the host driver function - e.g., :cpp:func:`sdmmc_host_deinit` - to disable the host peripheral and free the resources allocated by the driver. -During probing and card initialization (done by :cpp:func:`sdmmc_card_init`), the driver only configures the following registers of the IO card: -1. The IO portion of the card is reset by setting RES bit in "I/O Abort" (0x06) register. -2. If 4-line mode is enalbed in host and slot configuration, driver attempts to set "Bus width" field in "Bus Interface Control" (0x07) register. If that succeeds (which means that slave supports 4-line mode), host is also switched to 4-line mode. -3. If high-speed mode is enabled in host configuration, SHS bit is set in "High Speed" (0x13) register. +Using API with eMMC chips +^^^^^^^^^^^^^^^^^^^^^^^^^ -In particular, the driver does not set any of the bits in I/O Enable, Int Enable registers, IO block sizes, etc. Applications can set these by calling :cpp:func:`sdmmc_io_write_byte`. +From the protocol layer's perspective, eMMC memory chips behave exactly like SD memory cards. Even though eMMCs are chips and do not have a card form factor, the terminology for SD cards can still be applied to eMMC due to the similarity of the protocol (`sdmmc_card_t`, `sdmmc_card_init`). Note that eMMC chips cannot be used over SPI, which makes them incompatible with the SD SPI host driver. -For card configuration and data transfer, use one of the following functions: +To initialize eMMC memory and perform read/write operations, follow the steps listed for SD cards in the previous section. -- :cpp:func:`sdmmc_io_read_byte`, :cpp:func:`sdmmc_io_write_byte` — read and write single byte using IO_RW_DIRECT (CMD52). -- :cpp:func:`sdmmc_io_read_bytes`, :cpp:func:`sdmmc_io_write_bytes` — read and write multiple bytes using IO_RW_EXTENDED (CMD53), in byte mode. -- :cpp:func:`sdmmc_io_read_blocks`, :cpp:func:`sdmmc_io_write_blocks` — read and write blocks of data using IO_RW_EXTENDED (CMD53), in block mode. -SDIO interrupts can be enabled by the application using :cpp:func:`sdmmc_io_enable_int` function. When using SDIO in 1-line mode, D1 line also needs to be connected to use SDIO interrupts. +Using API with SDIO cards +^^^^^^^^^^^^^^^^^^^^^^^^^ + +Initialization and the probing process is the same as with SD memory cards. The only difference is in data transfer commands in SDIO mode. + +During the card initialization and probing, performed with :cpp:func:`sdmmc_card_init`, the driver only configures the following registers of the IO card: + +1. The IO portion of the card is reset by setting RES bit in the I/O Abort (0x06) register. +2. If 4-line mode is enabled in host and slot configuration, the driver attempts to set the Bus width field in the Bus Interface Control (0x07) register. If setting the filed is successful, which means that the slave supports 4-line mode, the host is also switched to 4-line mode. +3. If high-speed mode is enabled in the host configuration, the SHS bit is set in the High Speed (0x13) register. + +In particular, the driver does not set any bits in (1) I/O Enable and Int Enable registers, (2) I/O block sizes, etc. Applications can set them by calling :cpp:func:`sdmmc_io_write_byte`. + +For card configuration and data transfer, choose the pair of functions relevant to your case from the table below. + +========================================================================= ================================= ================================= +Action Read Function Write Function +========================================================================= ================================= ================================= +Read and write a single byte using IO_RW_DIRECT (CMD52) :cpp:func:`sdmmc_io_read_byte` :cpp:func:`sdmmc_io_write_byte` +Read and write multiple bytes using IO_RW_EXTENDED (CMD53) in byte mode :cpp:func:`sdmmc_io_read_bytes` :cpp:func:`sdmmc_io_write_bytes` +Read and write blocks of data using IO_RW_EXTENDED (CMD53) in block mode :cpp:func:`sdmmc_io_read_blocks` :cpp:func:`sdmmc_io_write_blocks` +========================================================================= ================================= ================================= + +SDIO interrupts can be enabled by the application using the function :cpp:func:`sdmmc_io_enable_int`. When using SDIO in 1-line mode, the D1 line also needs to be connected to use SDIO interrupts. + +If you want the application to wait until the SDIO interrupt occurs, use :cpp:func:`sdmmc_io_wait_int`. -The application can wait for SDIO interrupt to occur using :cpp:func:`sdmmc_io_wait_int`. Combo (memory + IO) cards ^^^^^^^^^^^^^^^^^^^^^^^^^ -The driver does not support SD combo cards. Combo cards will be treated as IO cards. +The driver does not support SD combo cards. Combo cards are treated as IO cards. Thread safety ^^^^^^^^^^^^^ -Most applications need to use the protocol layer only in one task; therefore the protocol layer doesn't implement any kind of locking on the :cpp:class:`sdmmc_card_t` structure, or when accessing SDMMC or SD SPI host drivers. Such locking is usually implemented in the higher layer (e.g. in the filesystem driver). +Most applications need to use the protocol layer only in one task. For this reason, the protocol layer does not implement any kind of locking on the :cpp:class:`sdmmc_card_t` structure, or when accessing SDMMC or SD SPI host drivers. Such locking is usually implemented on a higher layer, e.g., in the filesystem driver. API Reference diff --git a/docs/en/api-reference/storage/spi_flash.rst b/docs/en/api-reference/storage/spi_flash.rst index 1dbb92bdd5..5595011d77 100644 --- a/docs/en/api-reference/storage/spi_flash.rst +++ b/docs/en/api-reference/storage/spi_flash.rst @@ -5,7 +5,7 @@ See also - :doc:`Partition Table documentation <../../api-guides/partition-tables>` - :doc:`Over The Air Update (OTA) API <../system/ota>` provides high-level API for updating app firmware stored in flash. -- :doc:`Non-Volatile Storage (NVS) API ` provides a structured API for storing small items of data in SPI flash. +- :doc:`Non-Volatile Storage (NVS) API ` provides a structured API for storing small pieces of data in SPI flash. .. _spi-flash-implementation-details: @@ -13,35 +13,20 @@ See also Implementation details ---------------------- -In order to perform some flash operations, we need to make sure both CPUs -are not running any code from flash for the duration of the flash operation. -In a single-core setup this is easy: we disable interrupts/scheduler and do -the flash operation. In the dual-core setup this is slightly more complicated. -We need to make sure that the other CPU doesn't run any code from flash. +In order to perform some flash operations, it is necessary to make sure that both CPUs are not running any code from flash for the duration of the flash operation: +- In a single-core setup, the SDK does it by disabling interrupts/scheduler before performing the flash operation. +- In a dual-core setup, this is slightly more complicated as the SDK needs to make sure that the other CPU is not running any code from flash. -When SPI flash API is called on CPU A (can be PRO or APP), we start -spi_flash_op_block_func function on CPU B using esp_ipc_call API. This API -wakes up high priority task on CPU B and tells it to execute given function, -in this case spi_flash_op_block_func. This function disables cache on CPU B and -signals that cache is disabled by setting s_flash_op_can_start flag. -Then the task on CPU A disables cache as well, and proceeds to execute flash -operation. +When SPI flash API is called on CPU A (can be PRO or APP), start the spi_flash_op_block_func function on CPU B using the esp_ipc_call API. This API wakes up a high priority task on CPU B and tells it to execute a given function, in this case, spi_flash_op_block_func. This function disables cache on CPU B and signals that the cache is disabled by setting the s_flash_op_can_start flag. Then the task on CPU A disables cache as well and proceeds to execute flash operation. -While flash operation is running, interrupts can still run on CPUs A and B. -We assume that all interrupt code is placed into RAM. Once interrupt allocation -API is added, we should add a flag to request interrupt to be disabled for -the duration of flash operations. +While a flash operation is running, interrupts can still run on CPUs A and B. It is assumed that all interrupt code is placed into RAM. Once the interrupt allocation API is added, a flag should be added to request the interrupt to be disabled for the duration of a flash operations. -Once flash operation is complete, function on CPU A sets another flag, -s_flash_op_complete, to let the task on CPU B know that it can re-enable -cache and release the CPU. Then the function on CPU A re-enables the cache on -CPU A as well and returns control to the calling code. +Once the flash operation is complete, the function on CPU A sets another flag, s_flash_op_complete, to let the task on CPU B know that it can re-enable cache and release the CPU. Then the function on CPU A re-enables the cache on CPU A as well and returns control to the calling code. Additionally, all API functions are protected with a mutex (s_flash_op_mutex). -In a single core environment (:ref:`CONFIG_FREERTOS_UNICORE` enabled), we simply -disable both caches, no inter-CPU communication takes place. +In a single core environment (:ref:`CONFIG_FREERTOS_UNICORE` enabled), you need to disable both caches, so that no inter-CPU communication can take place. API Reference - SPI Flash ------------------------- diff --git a/docs/en/api-reference/storage/spiffs.rst b/docs/en/api-reference/storage/spiffs.rst index 4c7b3cf779..dcd31a022f 100644 --- a/docs/en/api-reference/storage/spiffs.rst +++ b/docs/en/api-reference/storage/spiffs.rst @@ -4,15 +4,16 @@ SPIFFS Filesystem Overview -------- -SPIFFS is a file system intended for SPI NOR flash devices on embedded targets. -It supports wear leveling, file system consistency checks and more. +SPIFFS is a file system intended for SPI NOR flash devices on embedded targets. It supports wear levelling, file system consistency checks, and more. + Notes ----- - - Currently, SPIFFS does not support directories. It produces a flat structure. If SPIFFS is mounted under ``/spiffs``, then creating a file with path ``/spiffs/tmp/myfile.txt`` will create a file called ``/tmp/myfile.txt`` in SPIFFS, instead of ``myfile.txt`` under directory ``/spiffs/tmp``. - - It is not a realtime stack. One write operation might last much longer than another. - - Currently, it does not detect or handle bad blocks. + - Currently, SPIFFS does not support directories, it produces a flat structure. If SPIFFS is mounted under ``/spiffs``, then creating a file with the path ``/spiffs/tmp/myfile.txt`` will create a file called ``/tmp/myfile.txt`` in SPIFFS, instead of ``myfile.txt`` in the directory ``/spiffs/tmp``. + - It is not a real-time stack. One write operation might take much longer than another. + - For now, it does not detect or handle bad blocks. + Tools ----- @@ -21,29 +22,25 @@ spiffsgen.py ^^^^^^^^^^^^ :component_file:`spiffsgen.py` is a write-only Python SPIFFS implementation used to create filesystem -images from the contents of a host folder. To use ``spiffsgen.py``, simply invoke it from your favorite terminal:: +images from the contents of a host folder. To use ``spiffsgen.py``, open Terminal and run:: python spiffsgen.py -- image_size: size of the partition on which the created SPIFFS image will be flashed to -- base_dir: directory to create the SPIFFS image of -- output_file: SPIFFS image output file +The required arguments are as follows: -Besides the three required arguments: *image_size*, *base_dir* and *output_file*, there are other arguments -that control image generation. Documentation on these arguments exist in the tool's help:: +- **image_size**: size of the partition onto which the created SPIFFS image will be flashed. +- **base_dir**: directory for which the SPIFFS image needs to be created. +- **output_file**: SPIFFS image output file. + +There are also other arguments that control image generation. Documentation on these arguments can be found in the tool's help:: python spiffsgen.py --help -These optional arguments correspond to possible SPIFFS build configuration. -User should make sure that the image is generated with the same arguments/configuration as -SPIFFS was built with, else the user ends up with an invalid image. As a guide, the help output indicates the SPIFFS -build configuration the argument corresponds to. In cases when these arguments -are not specified, the default values shown in the help output are used. +These optional arguments correspond to a possible SPIFFS build configuration. To generate the right image, please make sure that you use the same arguments/configuration as were used to build SPIFFS. As a guide, the help output indicates the SPIFFS build configuration to which the argument corresponds. In cases when these arguments are not specified, the default values shown in the help output will be used. -Once the image has been created, it can be flashed using ``esptool.py`` or ``parttool.py``. +When the image is created, it can be flashed using ``esptool.py`` or ``parttool.py``. -Aside from invoking ``spiffsgen.py`` standalone, it is also possible to use it directly from the build system by calling -``spiffs_create_partition_image``. +Aside from invoking the ``spiffsgen.py`` standalone by manually running it from the command line or a script, it is also possible to invoke ``spiffsgen.py`` directly from the build system by calling ``spiffs_create_partition_image``. Make:: @@ -53,44 +50,42 @@ CMake:: spiffs_create_partition_image( [FLASH_IN_PROJECT]) -This is more convenient as the build configuration is automatically passed to the tool, -ensuring that the image generated is valid for that build. An example of this is while the *image_size* is required -for the standalone invocation, only the *partition* name is required when using ``spiffs_create_partition_image`` -- -the image size is automatically obtained from the project's partition table. -It is important to note that due to the differences in structure between the two build systems, -when using Make, ``spiffs_create_partition_image`` must be called from the project Makefile; -for CMake, it should be called from one of the component CMakeLists.txt. For both build systems, the image will be created in the build directory -with filename *partition*.bin. +This is more convenient as the build configuration is automatically passed to the tool, ensuring that the generated image is valid for that build. An example of this is while the *image_size* is required for the standalone invocation, only the *partition* name is required when using ``spiffs_create_partition_image`` -- the image size is automatically obtained from the project's partition table. -Optionally, user can opt to have the image automatically flashed together with the app binaries, partition tables, etc. on +Due to the differences in structure between Make and Cmake, it is important to note that: +- for Make ``spiffs_create_partition_image`` must be called from the project Makefile +- for CMake ``spiffs_create_partition_image`` must be called from one of the component CMakeLists.txt files + +For both build systems, the image will be created in the build directory with the filename *partition*.bin. + +Optionally, you can opt to have the image automatically flashed together with the app binaries, partition tables, etc., with ``idf.py flash`` or ``make flash`` by specifying ``FLASH_IN_PROJECT``. For example:: spiffs_create_partition_image(my_spiffs_partition my_folder FLASH_IN_PROJECT) -If FLASH_IN_PROJECT is not specified, the image is still generated, -but user has to flash it manually using ``esptool.py``, ``parttool.py`` or a custom build system target. +If FLASH_IN_PROJECT is not specified, the image will still be generated, but you will have to flash it manually using ``esptool.py``, ``parttool.py``, or a custom build system target. -For an example, see :example:`examples/storage/spiffsgen>`. ++For an example, see :example:`examples/storage/spiffsgen>`. mkspiffs ^^^^^^^^ -Another tool for creating SPIFS partition images is `mkspiffs `_. -Like ``spiffsgen.py``, it can be used to create image from a given folder and then flash that image with ``esptool.py`` +Another tool for creating SPIFFS partition images is `mkspiffs `_. +Similar to ``spiffsgen.py``, it can be used to create an image from a given folder and then flash that image using ``esptool.py`` -To do that you need to obtain some parameters: +For that, you need to obtain the following parameters: -- Block Size: 4096 (standard for SPI Flash) -- Page Size: 256 (standard for SPI Flash) -- Image Size: Size of the partition in bytes (can be obtained from partition table) -- Partition Offset: Starting address of the partition (can be obtained from partition table) +- **Block Size**: 4096 (standard for SPI Flash) +- **Page Size**: 256 (standard for SPI Flash) +- **Image Size**: Size of the partition in bytes (can be obtained from a partition table) +- **Partition Offset**: Starting address of the partition (can be obtained from a partition table) -To pack a folder into 1 Megabyte image:: +To pack a folder into a 1-Megabyte image, run:: mkspiffs -c [src_folder] -b 4096 -p 256 -s 0x100000 spiffs.bin -To flash the image to ESP32 at offset 0x110000:: +To flash the image onto ESP32 at offset 0x110000, run:: python esptool.py --chip esp32 --port [port] --baud [baud] write_flash -z 0x110000 spiffs.bin @@ -98,21 +93,15 @@ To flash the image to ESP32 at offset 0x110000:: Notes on which SPIFFS tool to use ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The two tools presented above offer very similar functionality. There are, however, reasons to prefer one -over the other depending on the use case. +The two tools presented above offer very similar functionality. However, there are reasons to prefer one over the other, depending on the use case. -If the intent is to simply generate a SPIFFS image during build, ``spiffsgen.py`` makes it very convenient -by providing functions/commands from the build system itself. This makes it easy to generate SPIFFS images -that match the build configuration and can be flashed together with the application. -Another case for choosing ``spiffsgen.py`` is when the host has no C/C++ compiler available, since ``mkspiffs`` -requires compilation. +Use ``spiffsgen.py`` in the following cases: +1. If you want to simply generate a SPIFFS image during the build. ``spiffsgen.py`` makes it very convenient by providing functions/commands from the build system itself. +2. If the host has no C/C++ compiler available, because ``spiffsgen.py`` does not require compilation. -On the other hand, ``mkspiffs`` offers unpacking SPIFFS images in addition to image generation. This is not -possible with ``spiffsgen.py``, at least not yet. There might also be environments where a Python interpreter -is not available, but a host compiler is or a pre-compiled ``mkspiffs`` binary -can do the job. However, there is no build system integration for ``mkspiffs`` and the user has to -do the corresponding work: compiling ``mkspiffs`` during build (if a pre-compiled binary is not used), creating build rules/targets -for the output files, passing proper parameters to the tool, etc. +Use ``mkspiffs`` in the following cases: +1. If you need to unpack SPIFFS images in addition to image generation. For now, it is not possible with ``spiffsgen.py``. +2. If you have an environment where a Python interpreter is not available, but a host compiler is available. Otherwise, a pre-compiled ``mkspiffs`` binary can do the job. However, there is no build system integration for ``mkspiffs`` and the user has to do the corresponding work: compiling ``mkspiffs`` during build (if a pre-compiled binary is not used), creating build rules/targets for the output files, passing proper parameters to the tool, etc. See also @@ -120,14 +109,14 @@ See also - :doc:`Partition Table documentation <../../api-guides/partition-tables>` + Application Example ------------------- -An example for using SPIFFS is provided in :example:`storage/spiffs` directory. This example initializes and mounts SPIFFS partition, and writes and reads data from it using POSIX and C library APIs. See README.md file in the example directory for more information. +An example of using SPIFFS is provided in the :example:`storage/spiffs` directory. This example initializes and mounts a SPIFFS partition, then writes and reads data from it using POSIX and C library APIs. See the README.md file in the example directory for more information. -High level API Reference + +High-level API Reference ------------------------ -* :component_file:`spiffs/include/esp_spiffs.h` - .. include:: /_build/inc/esp_spiffs.inc diff --git a/docs/en/api-reference/storage/wear-levelling.rst b/docs/en/api-reference/storage/wear-levelling.rst index 847da56e0c..580dd6f3b5 100644 --- a/docs/en/api-reference/storage/wear-levelling.rst +++ b/docs/en/api-reference/storage/wear-levelling.rst @@ -9,7 +9,7 @@ See also Application Example ------------------- -An example which combines wear levelling driver with FATFS library is provided in ``examples/storage/wear_levelling`` directory. This example initializes the wear levelling driver, mounts FATFS partition, and writes and reads data from it using POSIX and C library APIs. See README.md file in the example directory for more information. +An example which combines the wear levelling driver with the FATFS library is provided in the :example:`storage/wear_levelling` directory. This example initializes the wear levelling driver, mounts FATFS partition, as well as writes and reads data from it using POSIX and C library APIs. See the :example:`storage/wear_levelling/README.md` file for more information. High level API Reference ------------------------ diff --git a/tools/mass_mfg/docs/README.rst b/tools/mass_mfg/docs/README.rst index ba43dd5e9f..ce614f77ef 100644 --- a/tools/mass_mfg/docs/README.rst +++ b/tools/mass_mfg/docs/README.rst @@ -1,50 +1,56 @@ Manufacturing Utility ===================== - Introduction ----------------- +------------ + +This utility is designed to create instances of factory NVS partition images on a per-device basis for mass manufacturing purposes. The NVS partition images are created from CSV files containing user-provided configurations and values. + +Please note that this utility only creates manufacturing binary images which then need to be flashed onto your devices using: + +- esptool.py +- Flash Download tool (available on Windows only) +- Direct flash programming -This utility is designed to create per device instances factory nvs partition images for mass manufacturing purposes. -These images are created from user provided configuration and values csv files. -This utility only creates the manufacturing binary images and you can choose to use esptool.py or Windows based flash programming utility or direct flash programming to program these images at the time of manufacturing. Prerequisites ------------------- +------------- -**This utility is dependent on the esp-idf nvs partition utility.** +**This utility is dependent on esp-idf's NVS partition utility.** -* Operating System requirements: - - Linux / MacOS / Windows (standard distributions) +* Operating System requirements: + - Linux / MacOS / Windows (standard distributions) -* The following packages are needed for using this utility: - - Python version: 2.7 (minimum) is required. - - Link to install python: +* The following packages are needed to use this utility: + - Python version: 2.7 (minimum) is required. You can find it here: -.. note:: Make sure the python path is set in the PATH environment variable before using this utility. +.. note:: + + Before using this utility, please make sure that: + - The path to Python is added to the PATH environment variable. + - You have installed the packages from `requirement.txt`, the file in the root of the esp-idf directory. -Make sure to include packages from `requirement.txt` in top level IDF directory. Workflow ------------ +-------- .. blockdiag:: blockdiag { A [label = "CSV Configuration file"]; - B [label = "Master CSV Values file"]; + B [label = "Master Value CSV file"]; C [label = "Binary files", stacked]; A -- B -> C } -CSV Configuration File: ------------------------- +CSV Configuration File +---------------------- -This file contains the configuration of the device to be manufactured. +This file contains the configuration of the device to be flashed. -The data in configuration file **must** have the following format (`REPEAT` tag is optional):: +The data in the configuration file has the following format (the `REPEAT` tag is optional):: name1,namespace, <-- First entry should be of type "namespace" key1,type1,encoding1 @@ -53,12 +59,12 @@ The data in configuration file **must** have the following format (`REPEAT` tag key3,type3,encoding3 key4,type4,encoding4 -.. note:: First entry in this file should always be ``namespace`` entry. +.. note:: The first line in this file should always be the ``namespace`` entry. -Each row should have these 3 parameters: ``key,type,encoding`` separated by comma. -If ``REPEAT`` tag is present, the value corresponding to this key in the Master CSV Values File will be the same for all devices. +Each line should have three parameters: ``key,type,encoding``, separated by a comma. +If the ``REPEAT`` tag is present, the value corresponding to this key in the master value CSV file will be the same for all devices. -*Please refer to README of nvs_partition utility for detailed description of each parameter.* +*Please refer to README of the NVS Partition Generator utility for detailed description of each parameter.* Below is a sample example of such a configuration file:: @@ -69,115 +75,131 @@ Below is a sample example of such a configuration file:: device_no,data,i32 -.. note:: Make sure there are no spaces before and after ',' in the configuration file. +.. note:: -Master CSV Values File: ------------------------- + Make sure there are **no spaces**: + - before and after ',' + - at the end of each line in a CSV file -This file contains details of the device to be manufactured. Each row in this file corresponds to a device instance. + +Master Value CSV File +--------------------- + +This file contains details of the devices to be flashed. Each line in this file corresponds to a device instance. -The data in values file **must** have the following format:: +The data in the master value CSV file has the following format:: key1,key2,key3,..... value1,value2,value3,.... -.. note:: First line in this file should always be the ``key`` names. All the keys from the configuration file should be present here in the **same order**. This file can have additional columns(keys) and they will act like metadata and would not be part of final binary files. +.. note:: The first line in the file should always contain the ``key`` names. All the keys from the configuration file should be present here in the **same order**. This file can have additional columns (keys). The additional keys will be treated as metadata and would not be part of the final binary files. -Each row should have the ``value`` of the corresponding keys, separated by comma. If key has ``REPEAT`` tag, then its corresponding value **must** be entered in the second line only. Keep the entry empty for this value in the next lines. Below is the description of this parameter: +Each line should contain the ``value`` of the corresponding keys, separated by a comma. If the key has the ``REPEAT`` tag, its corresponding value **must** be entered in the second line only. Keep the entry empty for this value in the following lines. + +The description of this parameter is as follows: ``value`` - Data value. + Data value -Below is a sample example of such a values file:: +Data value is the value of data corresponding to the key. + +Below is a sample example of a master value CSV file:: id,firmware_key,serial_no,device_no 1,1a2b3c4d5e6faabb,A1,101 2,1a2b3c4d5e6fccdd,,102 3,1a2b3c4d5e6feeff,,103 -.. note:: *If 'REPEAT' tag is present, a new Master CSV Values File is created in the same folder as the input Master CSV File with the values inserted at each line for the key with 'REPEAT' tag.* +.. note:: *If the 'REPEAT' tag is present, a new master value CSV file will be created in the same folder as the input Master CSV File with the values inserted at each line for the key with the 'REPEAT' tag*. -.. note:: *Intermediate CSV files are created by this utility which are input to the nvs partition utility to generate the binary files.* +This utility creates intermediate CSV files which are used as input for the NVS partition utility to generate the binary files. -The format of this intermediate csv file will be:: +The format of this intermediate CSV file is as follows:: key,type,encoding,value key,namespace, , key1,type1,encoding1,value1 key2,type2,encoding2,value2 -.. note:: An intermediate csv file will be created for each device instance. +An instance of an intermediate CSV file will be created for each device on an individual basis. + Running the utility ----------------------- +------------------- -The mfg\_gen.py utility is using the generated CSV Configuration file and Master CSV Values file and is generating per device instance factory images. +The mfg\_gen.py utility uses the generated CSV Configuration file and the master value CSV file to generate factory images for each device. -*Sample CSV Configuration file and Master CSV Values file is provided with this utility.* +*A sample CSV Configuration file and a master value CSV file are both provided with this utility.* **Usage**:: - $ ./mfg_gen.py [-h] [--conf CONFIG_FILE] [--values VALUES_FILE] + ./mfg_gen.py [-h] [--conf CONFIG_FILE] [--values VALUES_FILE] [--prefix PREFIX] [--fileid FILEID] [--outdir OUTDIR] [--size PART_SIZE] [--version {v1,v2}] [--keygen {true,false}] [--encrypt {true,false}] [--keyfile KEYFILE] +The description of the arguments is given in the table below. -+------------------------+----------------------------------------------------------------------------------------------+ -| Arguments | Description | -+========================+==============================================================================================+ -| --conf CONFIG_FILE | the input configuration csv file | -+------------------------+----------------------------------------------------------------------------------------------+ -| --values VALUES_FILE | the input values csv file | -+------------------------+----------------------------------------------------------------------------------------------+ -| --prefix PREFIX | the unique name as each filename prefix | -+------------------------+----------------------------------------------------------------------------------------------+ -| --fileid FILEID | the unique file identifier(any key in values file) | -| | as each filename suffix (Default: numeric value(1,2,3...)) | -+------------------------+----------------------------------------------------------------------------------------------+ -| --outdir OUTDIR | the output directory to store the files created (Default: current directory) | -+------------------------+----------------------------------------------------------------------------------------------+ -| --size PART_SIZE | Size of NVS Partition in bytes (must be multiple of 4096) | -+------------------------+----------------------------------------------------------------------------------------------+ -| --version {v1,v2} | Set version. Default: v2 | -+------------------------+----------------------------------------------------------------------------------------------+ -| --keygen {true,false} | Generate keys for encryption. | -| | Default: false | -+------------------------+----------------------------------------------------------------------------------------------+ -| --encrypt {true,false} | Set encryption mode. Default: false | -+------------------------+----------------------------------------------------------------------------------------------+ -| --keyfile KEYFILE | File having key for encryption (Applicable only if encryption mode is true) | -+------------------------+----------------------------------------------------------------------------------------------+ ++------------------------+------------------------------------------------------------+-------------------+ +| Arguments | Description | Default Value | ++========================+============================================================+===================+ +| --conf CONFIG_FILE | Path to existing CSV configuration file | | ++------------------------+------------------------------------------------------------+-------------------+ +| --values VALUES_FILE | Path to existing master value CSV file | | ++------------------------+------------------------------------------------------------+-------------------+ +| --prefix PREFIX | Unique filename prefix | | ++------------------------+------------------------------------------------------------+-------------------+ +| --fileid FILEID | Unique file identifier (any key in the file with values) | numeric value | +| | as a filename suffix | (1,2,3...) | ++------------------------+------------------------------------------------------------+-------------------+ +| --outdir OUTDIR | Output directory to store created files | current directory | ++------------------------+------------------------------------------------------------+-------------------+ +| --size PART_SIZE | Size of NVS Partition in bytes (must be multiple of 4096) | | ++------------------------+------------------------------------------------------------+-------------------+ +| --version {v1,v2} | Set version | v2 | ++------------------------+------------------------------------------------------------+-------------------+ +| --keygen {true,false} | Generate keys for encryption | false | ++------------------------+------------------------------------------------------------+-------------------+ +| --encrypt {true,false} | Set encryption mode | false | ++------------------------+------------------------------------------------------------+-------------------+ +| --keyfile KEYFILE | File storing key for encryption (Applicable only if | | +| | Encryption mode is true). | | ++------------------------+------------------------------------------------------------+-------------------+ -*You can use the below commands to run this utility with the sample files provided*:: - - $ ./mfg_gen.py --conf samples/sample_config.csv --values samples/sample_values_singlepage_blob.csv --prefix Fan --size 0x3000 +*To run this utility with the provided sample files, use the commands below*:: - $ ./mfg_gen.py --conf samples/sample_config.csv --values samples/sample_values_multipage_blob.csv --prefix Fan --size 0x4000 - -.. note:: When you use this utility to generate per device instance factory images --conf, --values, --prefix and --size arguments are mandatory. - - $ ./mfg_gen.py --conf samples/sample_config.csv --values samples/sample_values_singlepage_blob.csv --prefix Fan --size 0x3000 --outdir tmp + ./mfg_gen.py --conf samples/sample_config.csv --values samples/sample_values_singlepage_blob.csv --prefix Fan --size 0x3000 -.. note:: The --outdir directory is created if not present. + ./mfg_gen.py --conf samples/sample_config.csv --values samples/sample_values_multipage_blob.csv --prefix Fan --size 0x4000 -.. note:: The file path given in the ``file`` type in the values file is expected to be relative to the current directory from which you are running the utility. - - $ ./mfg_gen.py --conf samples/sample_config.csv --values samples/sample_values_singlepage_blob.csv --prefix Fan --size 0x3000 --encrypt true --keygen true +When you use this utility to generate factory images on a per device basis, keep in mind that the arguments --conf, --values, --prefix, and --size are mandatory. -.. note:: ``keys/`` directory is generated with the encryption keys filename of the form ``prefix-fileid-keys.bin``. - -*You can also run the below command to use the utility to* **only** *generate encryption keys binary file ( following example 'keys/' directory is created in current path), which can further be used to encrypt per device instance factory images*:: - - $ ./mfg_gen.py --keygen true - - $ ./mfg_gen.py --keygen true --keyfile encr_keys.bin + ./mfg_gen.py --conf samples/sample_config.csv --values samples/sample_values_singlepage_blob.csv --prefix Fan --size 0x3000 --outdir tmp -.. note:: When running utility to generate only ``keys``, if --keyfile is given it will generate encryption keys with filename given in --keyfile argument. +.. note:: If the --outdir directory does not exist, it will be created. -.. note:: When you use this utility to generate only encryption keys --keygen argument is mandatory. +The master value CSV file should have the path in the ``file`` type relative to the directory from which you are running the utility. -.. note:: The default numeric value: 1,2,3... of ``fileid`` argument, corresponds to each row having device instance values in master csv values file. + ./mfg_gen.py --conf samples/sample_config.csv --values samples/sample_values_singlepage_blob.csv --prefix Fan --size 0x3000 --encrypt true --keygen true -.. note:: ``bin/`` **and** ``csv/`` **sub-directories are created in the** ``outdir`` **directory specified while running this utility. The binary files generated will be stored in** ``bin/`` **and the intermediate csv files generated will be stored in** ``csv/``. +.. note:: The generated ``keys/`` directory is named as the file with encryption keys of the form ``prefix-fileid-keys.bin``. + +*If you* **only** *want to generate a binary file with encryption keys, you can run the command below.*:: + + ./mfg_gen.py --keygen true + +.. note:: When you use this utility to generate encryption keys only, the --keygen argument is mandatory. + +In the following example, the 'keys/' directory will be created at the current path. This binary file can further be used to encrypt factory images created on the per device basis*.:: + + ./mfg_gen.py --keygen true --keyfile encr_keys.bin + +.. note:: When running the utility to generate encryption keys only, if --keyfile is given, it will generate encryption keys with the filename given in the --keyfile argument. + +The default numeric value: 1,2,3... of the ``fileid`` argument corresponds to each line bearing device instance values in the master value CSV file. + +While running the manufacturing utility, the following folders will be created in the specified ``outdir`` directory: + +- ``bin/`` for storing the generated binary files +- ``csv/`` for storing the generated intermediate CSV files From 76b3a64b760c41709b968868c19de318b7068c19 Mon Sep 17 00:00:00 2001 From: zhiweijian Date: Thu, 23 May 2019 14:44:16 +0800 Subject: [PATCH 060/486] Component/bt: add new api to get connection parameters --- components/bt/bluedroid/api/esp_gap_ble_api.c | 11 +++++++ .../api/include/api/esp_gap_ble_api.h | 22 +++++++++++++ .../bluedroid/api/include/api/esp_gatt_defs.h | 11 +++++++ .../bluedroid/api/include/api/esp_gattc_api.h | 1 + .../bluedroid/api/include/api/esp_gatts_api.h | 1 + .../bt/bluedroid/bta/gatt/bta_gattc_act.c | 12 ++++++- .../bt/bluedroid/bta/gatt/bta_gattc_utils.c | 5 ++- .../bt/bluedroid/bta/gatt/bta_gatts_act.c | 14 +++++++-- .../bta/gatt/include/bta_gattc_int.h | 3 +- .../bluedroid/bta/include/bta/bta_gatt_api.h | 8 +++++ .../btc/profile/std/gatt/btc_gattc.c | 3 ++ .../btc/profile/std/gatt/btc_gatts.c | 4 ++- components/bt/bluedroid/stack/btm/btm_ble.c | 31 +++++++++++++++++++ .../bt/bluedroid/stack/btm/btm_ble_gap.c | 25 +++++++++++++++ .../bluedroid/stack/btm/include/btm_ble_int.h | 2 ++ .../stack/include/stack/btm_ble_api.h | 13 ++++++++ 16 files changed, 160 insertions(+), 6 deletions(-) diff --git a/components/bt/bluedroid/api/esp_gap_ble_api.c b/components/bt/bluedroid/api/esp_gap_ble_api.c index cc9983b537..aef6b3d6d6 100644 --- a/components/bt/bluedroid/api/esp_gap_ble_api.c +++ b/components/bt/bluedroid/api/esp_gap_ble_api.c @@ -706,6 +706,17 @@ esp_err_t esp_ble_gap_disconnect(esp_bd_addr_t remote_device) return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gap_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } +esp_err_t esp_ble_get_current_conn_params(esp_bd_addr_t bd_addr, esp_gap_conn_params_t *conn_params) +{ + if(!bd_addr || !conn_params) { + return ESP_ERR_INVALID_ARG; + } + if(BTM_GetCurrentConnParams(bd_addr, &conn_params->interval, &conn_params->latency, &conn_params->timeout)) { + return ESP_OK; + } + return ESP_ERR_NOT_FOUND; +} + diff --git a/components/bt/bluedroid/api/include/api/esp_gap_ble_api.h b/components/bt/bluedroid/api/include/api/esp_gap_ble_api.h index 96123e8bce..f6097dfe70 100644 --- a/components/bt/bluedroid/api/include/api/esp_gap_ble_api.h +++ b/components/bt/bluedroid/api/include/api/esp_gap_ble_api.h @@ -377,6 +377,15 @@ typedef struct { advertising reports for each packet received */ } esp_ble_scan_params_t; +/// connection parameters information +typedef struct { + uint16_t interval; /*!< connection interval */ + uint16_t latency; /*!< Slave latency for the connection in number of connection events. Range: 0x0000 to 0x01F3 */ + uint16_t timeout; /*!< Supervision timeout for the LE Link. Range: 0x000A to 0x0C80. + Mandatory Range: 0x000A to 0x0C80 Time = N * 10 msec + Time Range: 100 msec to 32 seconds */ +} esp_gap_conn_params_t; + /// Connection update parameters typedef struct { esp_bd_addr_t bda; /*!< Bluetooth device address */ @@ -1233,6 +1242,19 @@ esp_err_t esp_ble_oob_req_reply(esp_bd_addr_t bd_addr, uint8_t *TK, uint8_t len) */ esp_err_t esp_ble_gap_disconnect(esp_bd_addr_t remote_device); +/** +* @brief This function is called to read the connection +* parameters information of the device +* +* @param[in] bd_addr: BD address of the peer device. +* @param[out] conn_params: the connection parameters information +* +* @return - ESP_OK : success +* - other : failed +* +*/ +esp_err_t esp_ble_get_current_conn_params(esp_bd_addr_t bd_addr, esp_gap_conn_params_t *conn_params); + #ifdef __cplusplus } #endif diff --git a/components/bt/bluedroid/api/include/api/esp_gatt_defs.h b/components/bt/bluedroid/api/include/api/esp_gatt_defs.h index d6c140a140..6deadeadcc 100644 --- a/components/bt/bluedroid/api/include/api/esp_gatt_defs.h +++ b/components/bt/bluedroid/api/include/api/esp_gatt_defs.h @@ -387,6 +387,17 @@ typedef enum { ESP_GATT_WRITE_TYPE_RSP, /*!< Gatt write attribute need remote response */ } esp_gatt_write_type_t; +/** + * @brief Connection parameters information + */ +typedef struct { + uint16_t interval; /*!< connection interval */ + uint16_t latency; /*!< Slave latency for the connection in number of connection events. Range: 0x0000 to 0x01F3 */ + uint16_t timeout; /*!< Supervision timeout for the LE Link. Range: 0x000A to 0x0C80. + Mandatory Range: 0x000A to 0x0C80 Time = N * 10 msec + Time Range: 100 msec to 32 seconds */ +} esp_gatt_conn_params_t; + #define ESP_GATT_IF_NONE 0xff /*!< If callback report gattc_if/gatts_if as this macro, means this event is not correspond to any app */ typedef uint8_t esp_gatt_if_t; /*!< Gatt interface type, different application on GATT client use different gatt_if */ diff --git a/components/bt/bluedroid/api/include/api/esp_gattc_api.h b/components/bt/bluedroid/api/include/api/esp_gattc_api.h index d18b2e4c4e..d8f8bf7ece 100644 --- a/components/bt/bluedroid/api/include/api/esp_gattc_api.h +++ b/components/bt/bluedroid/api/include/api/esp_gattc_api.h @@ -210,6 +210,7 @@ typedef union { struct gattc_connect_evt_param { uint16_t conn_id; /*!< Connection id */ esp_bd_addr_t remote_bda; /*!< Remote bluetooth device address */ + esp_gatt_conn_params_t conn_params; /*!< current connection parameters */ } connect; /*!< Gatt client callback param of ESP_GATTC_CONNECT_EVT */ /** diff --git a/components/bt/bluedroid/api/include/api/esp_gatts_api.h b/components/bt/bluedroid/api/include/api/esp_gatts_api.h index 97296b1c7a..a3aee4cc1a 100644 --- a/components/bt/bluedroid/api/include/api/esp_gatts_api.h +++ b/components/bt/bluedroid/api/include/api/esp_gatts_api.h @@ -197,6 +197,7 @@ typedef union { struct gatts_connect_evt_param { uint16_t conn_id; /*!< Connection id */ esp_bd_addr_t remote_bda; /*!< Remote bluetooth device address */ + esp_gatt_conn_params_t conn_params; /*!< current Connection parameters */ } connect; /*!< Gatt server callback param of ESP_GATTS_CONNECT_EVT */ /** diff --git a/components/bt/bluedroid/bta/gatt/bta_gattc_act.c b/components/bt/bluedroid/bta/gatt/bta_gattc_act.c index ed066d006c..e6df3e5189 100644 --- a/components/bt/bluedroid/bta/gatt/bta_gattc_act.c +++ b/components/bt/bluedroid/bta/gatt/bta_gattc_act.c @@ -729,7 +729,7 @@ void bta_gattc_conncback(tBTA_GATTC_RCB *p_rcb, tBTA_GATTC_DATA *p_data) if (p_rcb) { bta_gattc_send_connect_cback(p_rcb, p_data->int_conn.remote_bda, - p_data->int_conn.hdr.layer_specific); + p_data->int_conn.hdr.layer_specific, p_data->int_conn.conn_params); } } @@ -1654,6 +1654,16 @@ static void bta_gattc_conn_cback(tGATT_IF gattc_if, BD_ADDR bda, UINT16 conn_id, p_buf->int_conn.hdr.event = connected ? BTA_GATTC_INT_CONN_EVT : BTA_GATTC_INT_DISCONN_EVT; + if(p_buf->int_conn.hdr.event == BTA_GATTC_INT_CONN_EVT) { + tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr(bda, BT_TRANSPORT_LE); + if(p_lcb != NULL) { + p_buf->int_conn.conn_params.interval = p_lcb->current_used_conn_interval; + p_buf->int_conn.conn_params.latency = p_lcb->current_used_conn_latency; + p_buf->int_conn.conn_params.timeout = p_lcb->current_used_conn_timeout; + } else { + APPL_TRACE_WARNING("%s not found connection parameters of the device ", __func__); + } + } p_buf->int_conn.hdr.layer_specific = conn_id; p_buf->int_conn.client_if = gattc_if; p_buf->int_conn.role = L2CA_GetBleConnRole(bda); diff --git a/components/bt/bluedroid/bta/gatt/bta_gattc_utils.c b/components/bt/bluedroid/bta/gatt/bta_gattc_utils.c index d202451447..ed9e4c20e4 100644 --- a/components/bt/bluedroid/bta/gatt/bta_gattc_utils.c +++ b/components/bt/bluedroid/bta/gatt/bta_gattc_utils.c @@ -738,7 +738,7 @@ void bta_gattc_send_open_cback( tBTA_GATTC_RCB *p_clreg, tBTA_GATT_STATUS status ** Returns ** *******************************************************************************/ -void bta_gattc_send_connect_cback( tBTA_GATTC_RCB *p_clreg, BD_ADDR remote_bda, UINT16 conn_id) +void bta_gattc_send_connect_cback( tBTA_GATTC_RCB *p_clreg, BD_ADDR remote_bda, UINT16 conn_id, tBTA_GATT_CONN_PARAMS conn_params) { tBTA_GATTC cb_data; @@ -747,6 +747,9 @@ void bta_gattc_send_connect_cback( tBTA_GATTC_RCB *p_clreg, BD_ADDR remote_bda, cb_data.connect.client_if = p_clreg->client_if; cb_data.connect.conn_id = conn_id; + cb_data.connect.conn_params.interval = conn_params.interval; + cb_data.connect.conn_params.latency = conn_params.latency; + cb_data.connect.conn_params.timeout = conn_params.timeout; bdcpy(cb_data.connect.remote_bda, remote_bda); (*p_clreg->p_cback)(BTA_GATTC_CONNECT_EVT, &cb_data); diff --git a/components/bt/bluedroid/bta/gatt/bta_gatts_act.c b/components/bt/bluedroid/bta/gatt/bta_gatts_act.c index 47bad4afba..95cb668c6b 100644 --- a/components/bt/bluedroid/bta/gatt/bta_gatts_act.c +++ b/components/bt/bluedroid/bta/gatt/bta_gatts_act.c @@ -35,6 +35,7 @@ #include "stack/btm_ble_api.h" #include #include "osi/allocator.h" +#include "l2c_int.h" static void bta_gatts_nv_save_cback(BOOLEAN is_saved, tGATTS_HNDL_RANGE *p_hndl_range); static BOOLEAN bta_gatts_nv_srv_chg_cback(tGATTS_SRV_CHG_CMD cmd, tGATTS_SRV_CHG_REQ *p_req, @@ -965,7 +966,7 @@ static void bta_gatts_conn_cback (tGATT_IF gatt_if, BD_ADDR bda, UINT16 conn_id, BOOLEAN connected, tGATT_DISCONN_REASON reason, tGATT_TRANSPORT transport) { - tBTA_GATTS cb_data; + tBTA_GATTS cb_data = {0}; UINT8 evt = connected ? BTA_GATTS_CONNECT_EVT : BTA_GATTS_DISCONNECT_EVT; tBTA_GATTS_RCB *p_reg; @@ -993,7 +994,16 @@ static void bta_gatts_conn_cback (tGATT_IF gatt_if, BD_ADDR bda, UINT16 conn_id, bta_sys_conn_close( BTA_ID_GATTS , BTA_ALL_APP_ID, bda); } } - + if(evt == BTA_GATTS_CONNECT_EVT) { + tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr(bda, BT_TRANSPORT_LE); + if(p_lcb != NULL) { + cb_data.conn.conn_params.interval = p_lcb->current_used_conn_interval; + cb_data.conn.conn_params.latency = p_lcb->current_used_conn_latency; + cb_data.conn.conn_params.timeout = p_lcb->current_used_conn_timeout; + }else { + APPL_TRACE_WARNING("%s not found connection parameters of the device ", __func__); + } + } cb_data.conn.conn_id = conn_id; cb_data.conn.server_if = gatt_if; cb_data.conn.reason = reason; diff --git a/components/bt/bluedroid/bta/gatt/include/bta_gattc_int.h b/components/bt/bluedroid/bta/gatt/include/bta_gattc_int.h index c25f5ddb30..9715ac681e 100644 --- a/components/bt/bluedroid/bta/gatt/include/bta_gattc_int.h +++ b/components/bt/bluedroid/bta/gatt/include/bta_gattc_int.h @@ -216,6 +216,7 @@ typedef struct { tBT_TRANSPORT transport; tGATT_DISCONN_REASON reason; BOOLEAN already_connect; + tBTA_GATT_CONN_PARAMS conn_params; } tBTA_GATTC_INT_CONN; typedef struct { @@ -467,7 +468,7 @@ extern void bta_gattc_init_bk_conn(tBTA_GATTC_API_OPEN *p_data, tBTA_GATTC_RCB * extern void bta_gattc_cancel_bk_conn(tBTA_GATTC_API_CANCEL_OPEN *p_data); extern void bta_gattc_send_open_cback( tBTA_GATTC_RCB *p_clreg, tBTA_GATT_STATUS status, BD_ADDR remote_bda, UINT16 conn_id, tBTA_TRANSPORT transport, UINT16 mtu); -extern void bta_gattc_send_connect_cback( tBTA_GATTC_RCB *p_clreg, BD_ADDR remote_bda, UINT16 conn_id); +extern void bta_gattc_send_connect_cback( tBTA_GATTC_RCB *p_clreg, BD_ADDR remote_bda, UINT16 conn_id, tBTA_GATT_CONN_PARAMS conn_params); extern void bta_gattc_send_disconnect_cback( tBTA_GATTC_RCB *p_clreg, tGATT_DISCONN_REASON reason, BD_ADDR remote_bda, UINT16 conn_id); extern void bta_gattc_process_api_refresh(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_msg); diff --git a/components/bt/bluedroid/bta/include/bta/bta_gatt_api.h b/components/bt/bluedroid/bta/include/bta/bta_gatt_api.h index 896ca151cc..cb69110b60 100644 --- a/components/bt/bluedroid/bta/include/bta/bta_gatt_api.h +++ b/components/bt/bluedroid/bta/include/bta/bta_gatt_api.h @@ -201,6 +201,12 @@ typedef struct { UINT8 name_spc; /* The name space of the description */ } tBTA_GATT_CHAR_PRES; +typedef struct { + UINT16 interval; + UINT16 latency; + UINT16 timeout; +} tBTA_GATT_CONN_PARAMS; + #define BTA_GATT_CLT_CONFIG_NONE GATT_CLT_CONFIG_NONE /* 0x0000 */ #define BTA_GATT_CLT_CONFIG_NOTIFICATION GATT_CLT_CONFIG_NOTIFICATION /* 0x0001 */ #define BTA_GATT_CLT_CONFIG_INDICATION GATT_CLT_CONFIG_INDICATION /* 0x0002 */ @@ -403,6 +409,7 @@ typedef struct { UINT16 conn_id; tBTA_GATTC_IF client_if; BD_ADDR remote_bda; + tBTA_GATT_CONN_PARAMS conn_params; } tBTA_GATTC_CONNECT; typedef struct { @@ -610,6 +617,7 @@ typedef struct { UINT16 conn_id; tBTA_GATT_REASON reason; /* report disconnect reason */ tBTA_GATT_TRANSPORT transport; + tBTA_GATT_CONN_PARAMS conn_params; } tBTA_GATTS_CONN; typedef struct { diff --git a/components/bt/bluedroid/btc/profile/std/gatt/btc_gattc.c b/components/bt/bluedroid/btc/profile/std/gatt/btc_gattc.c index 5eaee361b8..73214a67dc 100644 --- a/components/bt/bluedroid/btc/profile/std/gatt/btc_gattc.c +++ b/components/bt/bluedroid/btc/profile/std/gatt/btc_gattc.c @@ -901,6 +901,9 @@ void btc_gattc_cb_handler(btc_msg_t *msg) gattc_if = connect->client_if; param.connect.conn_id = BTC_GATT_GET_CONN_ID(connect->conn_id); memcpy(param.connect.remote_bda, connect->remote_bda, sizeof(esp_bd_addr_t)); + param.connect.conn_params.interval = connect->conn_params.interval; + param.connect.conn_params.latency = connect->conn_params.latency; + param.connect.conn_params.timeout = connect->conn_params.timeout; btc_gattc_cb_to_app(ESP_GATTC_CONNECT_EVT, gattc_if, ¶m); break; } diff --git a/components/bt/bluedroid/btc/profile/std/gatt/btc_gatts.c b/components/bt/bluedroid/btc/profile/std/gatt/btc_gatts.c index eff4d41a1e..5b29a8b1e1 100644 --- a/components/bt/bluedroid/btc/profile/std/gatt/btc_gatts.c +++ b/components/bt/bluedroid/btc/profile/std/gatt/btc_gatts.c @@ -901,7 +901,9 @@ void btc_gatts_cb_handler(btc_msg_t *msg) gatts_if = p_data->conn.server_if; param.connect.conn_id = BTC_GATT_GET_CONN_ID(p_data->conn.conn_id); memcpy(param.connect.remote_bda, p_data->conn.remote_bda, ESP_BD_ADDR_LEN); - + param.connect.conn_params.interval = p_data->conn.conn_params.interval; + param.connect.conn_params.latency = p_data->conn.conn_params.latency; + param.connect.conn_params.timeout = p_data->conn.conn_params.timeout; btc_gatts_cb_to_app(ESP_GATTS_CONNECT_EVT, gatts_if, ¶m); break; case BTA_GATTS_DISCONNECT_EVT: diff --git a/components/bt/bluedroid/stack/btm/btm_ble.c b/components/bt/bluedroid/stack/btm/btm_ble.c index 73f6387a8d..2b62cde415 100644 --- a/components/bt/bluedroid/stack/btm/btm_ble.c +++ b/components/bt/bluedroid/stack/btm/btm_ble.c @@ -2732,4 +2732,35 @@ void btm_ble_set_keep_rfu_in_auth_req(BOOLEAN keep_rfu) #endif /* BTM_BLE_CONFORMANCE_TESTING */ +/******************************************************************************* +** +** Function btm_get_current_conn_params +** +** Description This function is called to get current connection parameters +** information of the device +** +** Returns TRUE if the information is geted, else FALSE +** +*******************************************************************************/ + +BOOLEAN btm_get_current_conn_params(BD_ADDR bda, UINT16 *interval, UINT16 *latency, UINT16 *timeout) +{ + if( (interval == NULL) || (latency == NULL) || (timeout == NULL) ) { + BTM_TRACE_ERROR("%s invalid parameters ", __func__); + return FALSE; + } + + tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr(bda, BT_TRANSPORT_LE); + if(p_lcb != NULL) { + (*interval) = p_lcb->current_used_conn_interval; + (*latency) = p_lcb->current_used_conn_latency; + (*timeout) = p_lcb->current_used_conn_timeout; + return TRUE; + } + BTM_TRACE_WARNING("%s Device is not connected", __func__); + + return FALSE; +} + + #endif /* BLE_INCLUDED */ diff --git a/components/bt/bluedroid/stack/btm/btm_ble_gap.c b/components/bt/bluedroid/stack/btm/btm_ble_gap.c index fff66affa8..cbf349ded1 100644 --- a/components/bt/bluedroid/stack/btm/btm_ble_gap.c +++ b/components/bt/bluedroid/stack/btm/btm_ble_gap.c @@ -2045,6 +2045,31 @@ void BTM_Recovery_Pre_State(void) return; } +/******************************************************************************* +** +** Function BTM_GetCurrentConnParams +** +** Description This function is called to read the current connection parameters +** of the device +** +** Returns TRUE or FALSE +** +*******************************************************************************/ + +BOOLEAN BTM_GetCurrentConnParams(BD_ADDR bda, uint16_t *interval, uint16_t *latency, uint16_t *timeout) +{ + if( (interval == NULL) || (latency == NULL) || (timeout == NULL) ) { + BTM_TRACE_ERROR("%s error ", __func__); + return FALSE; + } + + if(btm_get_current_conn_params(bda, interval, latency, timeout)) { + return TRUE; + } + + return FALSE; +} + /******************************************************************************* ** ** Function btm_ble_build_adv_data diff --git a/components/bt/bluedroid/stack/btm/include/btm_ble_int.h b/components/bt/bluedroid/stack/btm/include/btm_ble_int.h index 369e60264f..4fc915e471 100644 --- a/components/bt/bluedroid/stack/btm/include/btm_ble_int.h +++ b/components/bt/bluedroid/stack/btm/include/btm_ble_int.h @@ -506,6 +506,8 @@ void btm_set_random_address(BD_ADDR random_bda); void btm_ble_set_keep_rfu_in_auth_req(BOOLEAN keep_rfu); #endif +BOOLEAN btm_get_current_conn_params(BD_ADDR bda, UINT16 *interval, UINT16 *latency, UINT16 *timeout); + /* #ifdef __cplusplus } diff --git a/components/bt/bluedroid/stack/include/stack/btm_ble_api.h b/components/bt/bluedroid/stack/include/stack/btm_ble_api.h index cdbb2db617..2ac01ea2eb 100644 --- a/components/bt/bluedroid/stack/include/stack/btm_ble_api.h +++ b/components/bt/bluedroid/stack/include/stack/btm_ble_api.h @@ -2099,6 +2099,19 @@ tBTM_STATUS BTM_SetBleDataLength(BD_ADDR bd_addr, UINT16 tx_pdu_length); *******************************************************************************/ tBTM_STATUS BTM_UpdateBleDuplicateExceptionalList(uint8_t subcode, uint32_t type, BD_ADDR device_info, tBTM_UPDATE_DUPLICATE_EXCEPTIONAL_LIST_CMPL_CBACK update_exceptional_list_cmp_cb); + +/******************************************************************************* +** +** Function BTM_GetCurrentConnParams +** +** Description This function is called to read the current connection parameters +** of the device +** +** Returns TRUE or FALSE +** +*******************************************************************************/ + +BOOLEAN BTM_GetCurrentConnParams(BD_ADDR bda, uint16_t *interval, uint16_t *latency, uint16_t *timeout); /* #ifdef __cplusplus } From 8f2490cc6f611c6de4a03e44e10913a9fe8a12c7 Mon Sep 17 00:00:00 2001 From: zhiweijian Date: Wed, 8 May 2019 17:20:56 +0800 Subject: [PATCH 061/486] Component/bt: add svc_inst_id param for create service table event - add svc_inst_id param for create service table event - Closes https://github.com/espressif/esp-idf/issues/3256 --- components/bt/bluedroid/api/include/api/esp_gatts_api.h | 1 + components/bt/bluedroid/btc/profile/std/gatt/btc_gatts.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/components/bt/bluedroid/api/include/api/esp_gatts_api.h b/components/bt/bluedroid/api/include/api/esp_gatts_api.h index 97296b1c7a..ca2201497c 100644 --- a/components/bt/bluedroid/api/include/api/esp_gatts_api.h +++ b/components/bt/bluedroid/api/include/api/esp_gatts_api.h @@ -255,6 +255,7 @@ typedef union { struct gatts_add_attr_tab_evt_param{ esp_gatt_status_t status; /*!< Operation status */ esp_bt_uuid_t svc_uuid; /*!< Service uuid type */ + uint8_t svc_inst_id; /*!< Service id */ uint16_t num_handle; /*!< The number of the attribute handle to be added to the gatts database */ uint16_t *handles; /*!< The number to the handles */ } add_attr_tab; /*!< Gatt server callback param of ESP_GATTS_CREAT_ATTR_TAB_EVT */ diff --git a/components/bt/bluedroid/btc/profile/std/gatt/btc_gatts.c b/components/bt/bluedroid/btc/profile/std/gatt/btc_gatts.c index eff4d41a1e..8ea7e21e4a 100644 --- a/components/bt/bluedroid/btc/profile/std/gatt/btc_gatts.c +++ b/components/bt/bluedroid/btc/profile/std/gatt/btc_gatts.c @@ -425,6 +425,8 @@ static void btc_gatts_act_create_attr_tab(esp_gatts_attr_db_t *gatts_attr_db, param.add_attr_tab.handles = btc_creat_tab_env.handles; memcpy(¶m.add_attr_tab.svc_uuid, &btc_creat_tab_env.svc_uuid, sizeof(esp_bt_uuid_t)); + param.add_attr_tab.svc_inst_id = srvc_inst_id; + btc_gatts_cb_to_app(ESP_GATTS_CREAT_ATTR_TAB_EVT, gatts_if, ¶m); //reset the env after sent the data to app memset(&btc_creat_tab_env, 0, sizeof(esp_btc_creat_tab_t)); From 483e82caf8bc50e2661773589b38080e4bd17bb7 Mon Sep 17 00:00:00 2001 From: zhiweijian Date: Fri, 26 Apr 2019 16:11:53 +0800 Subject: [PATCH 062/486] Component/bt: modify some logs level --- components/bt/bluedroid/hci/hci_hal_h4.c | 2 +- components/bt/bluedroid/stack/l2cap/l2c_ble.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/bt/bluedroid/hci/hci_hal_h4.c b/components/bt/bluedroid/hci/hci_hal_h4.c index 89fba87b5d..264d1cc3de 100644 --- a/components/bt/bluedroid/hci/hci_hal_h4.c +++ b/components/bt/bluedroid/hci/hci_hal_h4.c @@ -321,7 +321,7 @@ static void hci_hal_h4_hdl_rx_packet(BT_HDR *packet) #if SCAN_QUEUE_CONGEST_CHECK if(BTU_check_queue_is_congest() && host_recv_adv_packet(packet)) { - HCI_TRACE_ERROR("BtuQueue is congested"); + HCI_TRACE_DEBUG("BtuQueue is congested"); osi_free(packet); return; } diff --git a/components/bt/bluedroid/stack/l2cap/l2c_ble.c b/components/bt/bluedroid/stack/l2cap/l2c_ble.c index 9a26969fac..c64994a80a 100644 --- a/components/bt/bluedroid/stack/l2cap/l2c_ble.c +++ b/components/bt/bluedroid/stack/l2cap/l2c_ble.c @@ -353,7 +353,7 @@ void l2cble_scanner_conn_comp (UINT16 handle, BD_ADDR bda, tBLE_ADDR_TYPE type, (conn_interval > p_dev_rec->conn_params.max_conn_int) || (conn_latency > p_dev_rec->conn_params.slave_latency) || (conn_timeout > p_dev_rec->conn_params.supervision_tout))) { - L2CAP_TRACE_ERROR ("upd_ll_conn_params: HANDLE=%d min_conn_int=%d max_conn_int=%d slave_latency=%d supervision_tout=%d", + L2CAP_TRACE_WARNING ("upd_ll_conn_params: HANDLE=%d min_conn_int=%d max_conn_int=%d slave_latency=%d supervision_tout=%d", handle, p_dev_rec->conn_params.min_conn_int, p_dev_rec->conn_params.max_conn_int, p_dev_rec->conn_params.slave_latency, p_dev_rec->conn_params.supervision_tout); From 4d8a46b3767ed3f0079c4d00c694d326502856c0 Mon Sep 17 00:00:00 2001 From: zhiweijian Date: Fri, 26 Apr 2019 17:24:34 +0800 Subject: [PATCH 063/486] component/bt: add readme doc for BLE demos - Closes:https://github.com/espressif/esp-idf/issues/3341 --- .../bluedroid/api/include/api/esp_gatt_defs.h | 5 + .../bt/bluedroid/stack/btm/btm_ble_gap.c | 7 +- .../bt/bluedroid/stack/hcic/hciblecmds.c | 2 + components/bt/include/esp_bt.h | 1 - docs/en/api-guides/blufi.rst | 270 +++++++++--------- docs/zh_CN/api-guides/blufi.rst | 74 +++-- .../ble_compatibility_test/README.md | 16 ++ .../ble_spp_client/main/spp_client_demo.c | 32 ++- examples/bluetooth/blufi/README.md | 16 +- .../bluetooth/gatt_server/main/gatts_demo.c | 6 +- 10 files changed, 229 insertions(+), 200 deletions(-) create mode 100644 examples/bluetooth/ble_compatibility_test/README.md diff --git a/components/bt/bluedroid/api/include/api/esp_gatt_defs.h b/components/bt/bluedroid/api/include/api/esp_gatt_defs.h index d6c140a140..3e51c0138d 100644 --- a/components/bt/bluedroid/api/include/api/esp_gatt_defs.h +++ b/components/bt/bluedroid/api/include/api/esp_gatt_defs.h @@ -50,11 +50,16 @@ extern "C" { #define ESP_GATT_UUID_HID_SVC 0x1812 /* HID Service*/ #define ESP_GATT_UUID_SCAN_PARAMETERS_SVC 0x1813 /* Scan Parameters Service*/ #define ESP_GATT_UUID_RUNNING_SPEED_CADENCE_SVC 0x1814 /* Running Speed and Cadence Service*/ +#define ESP_GATT_UUID_Automation_IO_SVC 0x1815 /* Automation IO Service*/ #define ESP_GATT_UUID_CYCLING_SPEED_CADENCE_SVC 0x1816 /* Cycling Speed and Cadence Service*/ #define ESP_GATT_UUID_CYCLING_POWER_SVC 0x1818 /* Cycling Power Service*/ #define ESP_GATT_UUID_LOCATION_AND_NAVIGATION_SVC 0x1819 /* Location and Navigation Service*/ +#define ESP_GATT_UUID_ENVIRONMENTAL_SENSING_SVC OX181A /* Environmental Sensing Service*/ +#define ESP_GATT_UUID_BODY_COMPOSITION 0x181B /* Body Composition Service*/ #define ESP_GATT_UUID_USER_DATA_SVC 0x181C /* User Data Service*/ #define ESP_GATT_UUID_WEIGHT_SCALE_SVC 0x181D /* Weight Scale Service*/ +#define ESP_GATT_UUID_BOND_MANAGEMENT_SVC 0x181E /* Bond Management Service*/ +#define ESP_GATT_UUID_CONT_GLUCOSE_MONITOR_SVC 0x181F /* Continuous Glucose Monitoring Service*/ #define ESP_GATT_UUID_PRI_SERVICE 0x2800 #define ESP_GATT_UUID_SEC_SERVICE 0x2801 diff --git a/components/bt/bluedroid/stack/btm/btm_ble_gap.c b/components/bt/bluedroid/stack/btm/btm_ble_gap.c index fff66affa8..10a97bfb54 100644 --- a/components/bt/bluedroid/stack/btm/btm_ble_gap.c +++ b/components/bt/bluedroid/stack/btm/btm_ble_gap.c @@ -1654,6 +1654,10 @@ tBTM_STATUS BTM_BleWriteScanRsp(tBTM_BLE_AD_MASK data_mask, tBTM_BLE_ADV_DATA *p osi_mutex_lock(&adv_data_lock, OSI_MUTEX_MAX_TIMEOUT); memset(rsp_data, 0, BTM_BLE_AD_DATA_LEN); btm_ble_build_adv_data(&data_mask, &p, p_data); + if (data_mask != 0) { + //data length should not exceed 31 bytes + BTM_TRACE_WARNING("%s, Partial data write into ADV", __func__); + } if (btsnd_hcic_ble_set_scan_rsp_data((UINT8)(p - rsp_data), rsp_data)) { osi_sem_take(&adv_data_sem, OSI_SEM_MAX_TIMEOUT); @@ -1798,7 +1802,8 @@ tBTM_STATUS BTM_BleWriteAdvData(tBTM_BLE_AD_MASK data_mask, tBTM_BLE_ADV_DATA *p p_cb_data->p_pad = p; if (mask != 0) { - BTM_TRACE_DEBUG("Partial data write into ADV"); + //data length should not exceed 31 bytes + BTM_TRACE_WARNING("%s, Partial data write into ADV", __func__); } p_cb_data->data_mask &= ~mask; diff --git a/components/bt/bluedroid/stack/hcic/hciblecmds.c b/components/bt/bluedroid/stack/hcic/hciblecmds.c index 87785749b2..1d814cc8d0 100644 --- a/components/bt/bluedroid/stack/hcic/hciblecmds.c +++ b/components/bt/bluedroid/stack/hcic/hciblecmds.c @@ -160,6 +160,7 @@ BOOLEAN btsnd_hcic_ble_set_adv_data (UINT8 data_len, UINT8 *p_data) if (p_data != NULL && data_len > 0) { if (data_len > HCIC_PARAM_SIZE_BLE_WRITE_ADV_DATA) { data_len = HCIC_PARAM_SIZE_BLE_WRITE_ADV_DATA; + HCI_TRACE_WARNING("Data length exceeds 31 bytes, only the first 31 bytes are used.\n"); } UINT8_TO_STREAM (pp, data_len); @@ -193,6 +194,7 @@ BOOLEAN btsnd_hcic_ble_set_scan_rsp_data (UINT8 data_len, UINT8 *p_scan_rsp) if (data_len > HCIC_PARAM_SIZE_BLE_WRITE_SCAN_RSP ) { data_len = HCIC_PARAM_SIZE_BLE_WRITE_SCAN_RSP; + HCI_TRACE_WARNING("Data length exceeds 31 bytes, only the first 31 bytes are used.\n"); } UINT8_TO_STREAM (pp, data_len); diff --git a/components/bt/include/esp_bt.h b/components/bt/include/esp_bt.h index 1157843218..2e331b2e6f 100644 --- a/components/bt/include/esp_bt.h +++ b/components/bt/include/esp_bt.h @@ -304,7 +304,6 @@ esp_err_t esp_bt_controller_init(esp_bt_controller_config_t *cfg); * @brief De-initialize BT controller to free resource and delete task. * * This function should be called only once, after any other BT functions are called. - * This function is not whole completed, esp_bt_controller_init cannot called after this function. * @return ESP_OK - success, other - failed */ esp_err_t esp_bt_controller_deinit(void); diff --git a/docs/en/api-guides/blufi.rst b/docs/en/api-guides/blufi.rst index 61b8a03dfa..97b04dff2c 100644 --- a/docs/en/api-guides/blufi.rst +++ b/docs/en/api-guides/blufi.rst @@ -145,15 +145,15 @@ The format of Ack Frame(8 bit): * The data frame supports to be encrypted and verified. - **1.1 Control Frame (0x0b’00)** + **1.1 Control Frame (0x0 b’00)** +-------------------------+--------------------------------------------------------------+---------------------------------------------------------------+---------------------------------------------------------------+ - | Control Frame / 0x0b’00 | Implication | Explanation | Note | + | Control Frame (Binary) | Implication | Explanation | Note | +=========================+==============================================================+===============================================================+===============================================================+ - | 0x0b’000000 | Ack | The data field of the Ack frame uses the same | The data field consumes a byte and its value is | + | 0x0 (b’000000) | Ack | The data field of the Ack frame uses the same | The data field consumes a byte and its value is | | | | sequence value of the frame to reply to. | the same as the sequence field of the frame to reply to. | +-------------------------+--------------------------------------------------------------+---------------------------------------------------------------+---------------------------------------------------------------+ - | 0x1b’000001 | Set ESP32 to the security mode. | To inform ESP32 of the security mode to use | The data field consumes a byte. | + | 0x1 (b’000001) | Set ESP32 to the security mode. | To inform ESP32 of the security mode to use | The data field consumes a byte. | | | | when sending data, which is allowed to be reset | The higher 4 bits are for the security mode setting | | | | multiple times during the process. | of the control frame, and the lower 4 bits are for | | | | Each setting affects the subsequent security mode used. | the security mode setting of the data frame. | @@ -166,7 +166,7 @@ The format of Ack Frame(8 bit): + + + +---------------------------------------------------------------+ | | | | b’0011: with both checksum and encryption. | +-------------------------+--------------------------------------------------------------+---------------------------------------------------------------+---------------------------------------------------------------+ - | 0x2b’000010 | Set the opmode of Wi-Fi. | The frame contains opmode settings for | data[0] is for opmode settings, including: | + | 0x2 (b’000010) | Set the opmode of Wi-Fi. | The frame contains opmode settings for | data[0] is for opmode settings, including: | + + + configuring for the Wi-Fi mode of ESP32. +---------------------------------------------------------------+ | | | | 0x00: NULL; | + + + +---------------------------------------------------------------+ @@ -179,12 +179,12 @@ The format of Ack Frame(8 bit): | | | | Please set the SSID/Password/Max Connection Number of | | | | | the AP mode in the first place if an AP gets involved. | +-------------------------+--------------------------------------------------------------+---------------------------------------------------------------+---------------------------------------------------------------+ - | 0x3b’000011 | Connect ESP32 to the AP. | To notify ESP32 that the essential information has been sent | No data field is contained. | + | 0x3 (b’000011) | Connect ESP32 to the AP. | To notify ESP32 that the essential information has been sent | No data field is contained. | | | | and it is allowed to connect to the AP. | | +-------------------------+--------------------------------------------------------------+---------------------------------------------------------------+---------------------------------------------------------------+ - | 0x4b’000100 | Disconnect ESP32 from the AP. | | No data field is contained. | + | 0x4 (b’000100) | Disconnect ESP32 from the AP. | | No data field is contained. | +-------------------------+--------------------------------------------------------------+---------------------------------------------------------------+---------------------------------------------------------------+ - | 0x5b’000101 | To get the information of ESP32’s Wi-Fi mode and its status. | | No data field is contained. | + | 0x5 (b’000101) | To get the information of ESP32’s Wi-Fi mode and its status. | | No data field is contained. | | | | | When receiving this control frame, ESP32 will send back | | | | | a follow-up frame of Wi-Fi connection state report to | | | | | the mobile phone with the information of the current opmode, | @@ -192,142 +192,142 @@ The format of Ack Frame(8 bit): | | | | The types of information sent to the mobile phone is | | | | | defined by the application installed on the phone. | +-------------------------+--------------------------------------------------------------+---------------------------------------------------------------+---------------------------------------------------------------+ - | 0x6b’000110 | Disconnect the STA device from the SoftAP (in SoftAP mode). | | Date[0~5] is taken as the MAC address for the STA device. | + | 0x6 (b’000110) | Disconnect the STA device from the SoftAP (in SoftAP mode). | | Date[0~5] is taken as the MAC address for the STA device. | | | | | If there is a second STA device, then it uses data[6-11] | | | | | and the rest can be done in the same manner. | +-------------------------+--------------------------------------------------------------+---------------------------------------------------------------+---------------------------------------------------------------+ - | 0x7b'000111 | Get the version information. | | | + | 0x7 (b'000111) | Get the version information. | | | +-------------------------+--------------------------------------------------------------+---------------------------------------------------------------+---------------------------------------------------------------+ - | 0x8b’001000 | Disconnect the BLE GATT link. | | ESP32 will disconnect the BLE GATT link | + | 0x8 (b’001000) | Disconnect the BLE GATT link. | | ESP32 will disconnect the BLE GATT link | | | | | after receives this command. | +-------------------------+--------------------------------------------------------------+---------------------------------------------------------------+---------------------------------------------------------------+ - | 0x9b’001001 | Get the Wi-Fi list. | To get ESP32 to scan the Wi-Fi access points around. | No data field is contained. | + | 0x9 (b’001001) | Get the Wi-Fi list. | To get ESP32 to scan the Wi-Fi access points around. | No data field is contained. | | | | | When receiving this control frame, | | | | | ESP32 will send back a follow-up frame of Wi-Fi list | | | | | report to the mobile phone. | +-------------------------+--------------------------------------------------------------+---------------------------------------------------------------+---------------------------------------------------------------+ - **1.2 Data Frame (0x1b’01)** + **1.2 Data Frame (0x1 b’01)** - +--------------+----------------------------------------------------+---------------------------------------------------------------+-----------------------------------------------------------------------+ - | Data Frame | Implication | Explanation | Note | - +==============+====================================================+===============================================================+=======================================================================+ - | 0x0b’000000 | Send the negotiation data. | The negotiation data will be sent to the callback | The length of the data depends on the length field. | - | | | function registered in the application layer. | | - +--------------+----------------------------------------------------+---------------------------------------------------------------+-----------------------------------------------------------------------+ - | 0x1b’000001 | Send the BSSID for STA mode. | To send the BSSID of the AP for the STA device to | The length of the data depends on the length field. | - | | | connect under the condition that the SSID is hidden. | When the transmission direction is ESP32 to the mobile phone, | - | | | | it means to provide the mobile phone with the needed information. | - +--------------+----------------------------------------------------+---------------------------------------------------------------+-----------------------------------------------------------------------+ - | 0x2b’000010 | Send the SSID for STA mode. | To send the SSID of the AP for the STA device to connect. | The length of the data depends on the length field. | - | | | | When the transmission direction is ESP32 to the mobile phone, | - | | | | it means to provide the mobile phone with the needed information. | - +--------------+----------------------------------------------------+---------------------------------------------------------------+-----------------------------------------------------------------------+ - | 0x3b’000011 | Send the password for STA mode. | To send the password of the AP for the STA device to connect. | The length of the data depends on the length field. | - | | | | When the transmission direction is ESP32 to the mobile phone, | - | | | | it means to provide the mobile phone with the needed information. | - +--------------+----------------------------------------------------+---------------------------------------------------------------+-----------------------------------------------------------------------+ - | 0x4b’000100 | Send the SSID for SoftAP mode. | | The length of the data depends on the length field. | - | | | | When the transmission direction is ESP32 to the mobile phone, | - | | | | it means to provide the mobile phone with the needed information. | - +--------------+----------------------------------------------------+---------------------------------------------------------------+-----------------------------------------------------------------------+ - | 0x5b’000101 | Send the password for SoftAPmode. | | The length of the data depends on the length field. | - | | | | When the transmission direction is ESP32 to the mobile phone, | - | | | | it means to provide the mobile phone with the needed information. | - +--------------+----------------------------------------------------+---------------------------------------------------------------+-----------------------------------------------------------------------+ - | 0x6b’000110 | Set the maximum connection number for SoftAP mode. | | data[0] represents the value of the connection number, | - | | | | ranging from 1 to 4. When the transmission direction is ESP32 | - | | | | to the mobile phone, it means to provide the mobile phone with | - | | | | the needed information. | - +--------------+----------------------------------------------------+---------------------------------------------------------------+-----------------------------------------------------------------------+ - | 0x7b’000111 | Set the authentication mode for the SoftAP. | | data[0]: | - + + + +-----------------------------------------------------------------------+ - | | | | 0x00: OPEN | - + + + +-----------------------------------------------------------------------+ - | | | | 0x01: WEP | - + + + +-----------------------------------------------------------------------+ - | | | | 0x02: WPA_PSK | - + + + +-----------------------------------------------------------------------+ - | | | | 0x03: WPA2_PSK | - + + + +-----------------------------------------------------------------------+ - | | | | 0x04: WPA_WPA2_PSK | - + + + +-----------------------------------------------------------------------+ - | | | | When the transmission direction is ESP32 to the mobile phone, | - | | | | it means to provide the mobile phone with the needed information. | - +--------------+----------------------------------------------------+---------------------------------------------------------------+-----------------------------------------------------------------------+ - | 0x8b’001000 | Set the channel amount for SoftAP mode. | | data[0] represents the quantity of the supported channels, | - | | | | ranging from 1 to 14. | - | | | | When the transmission direction is ESP32 to the mobile phone, | - | | | | it means to provide the mobile phone with the needed information. | - +--------------+----------------------------------------------------+---------------------------------------------------------------+-----------------------------------------------------------------------+ - | 0x9b’001001 | Username | It provides the username of the GATT client when using | The length of the data depends on the length field. | - | | | encryption of enterprise level. | | - +--------------+----------------------------------------------------+---------------------------------------------------------------+-----------------------------------------------------------------------+ - | 0xab’001010 | CA Certification | It provides the CA Certification when using encryption | The length of the data depends on the length field. | - | | | of enterprise level. | The frame supports to be fragmented if the data length is not enough. | - +--------------+----------------------------------------------------+---------------------------------------------------------------+-----------------------------------------------------------------------+ - | 0xbb’001011 | Client Certification | It provides the client certification when | The length of the data depends on the length field. | - | | | using encryption of enterprise level. | The frame supports to be fragmented if the data length is not enough. | - | | | Whether the private key is contained or not | | - | | | depends on the content of the certification. | | - +--------------+----------------------------------------------------+---------------------------------------------------------------+-----------------------------------------------------------------------+ - | 0xcb’001100 | Server Certification | It provides the sever certification when using | The length of the data depends on the length field. | - | | | encryption of enterprise level. Whether the private key is | The frame supports to be fragmented if the data length is not enough. | - | | | contained or not depends on the content of the certification. | | - +--------------+----------------------------------------------------+---------------------------------------------------------------+-----------------------------------------------------------------------+ - | 0xdb’001101 | ClientPrivate Key | It provides the private key of the client when | The length of the data depends on the length field. | - | | | using encryption of enterprise level. | The frame supports to be fragmented if the data length is not enough. | - +--------------+----------------------------------------------------+---------------------------------------------------------------+-----------------------------------------------------------------------+ - | 0xeb’001110 | ServerPrivate Key | It provides the private key of the sever when | The length of the data depends on the length field. | - | | | using encryption of enterprise level. | The frame supports to be fragmented if the data length is not enough. | - +--------------+----------------------------------------------------+---------------------------------------------------------------+-----------------------------------------------------------------------+ - | 0xfb’001111 | Wi-Fi Connection State Report | To notify the phone of the ESP32's Wi-Fi status, | data[0] represents opmode, including: | - + + + including STA status and SoftAP status. +-----------------------------------------------------------------------+ - | | | It is for the STA device to connect to the | 0x00: NULL | - + + + mobile phone or the SoftAP. +-----------------------------------------------------------------------+ - | | | However, when the mobile phone receives the Wi-Fi status, | 0x01: STA | - + + + it can reply to other frames in addition to this frame. +-----------------------------------------------------------------------+ - | | | | 0x02: SoftAP | - + + + +-----------------------------------------------------------------------+ - | | | | 0x03: SoftAP&STA | - + + + +-----------------------------------------------------------------------+ - | | | | data[1]:the connection state of the STA device, | - | | | | 0x0 indicates a connection state, | - | | | | and others represent a disconnected state; | - + + + +-----------------------------------------------------------------------+ - | | | | data[2]:the connection state of the SoftAP, | - | | | | that is, how many STA devices have been connected. | - + + + +-----------------------------------------------------------------------+ - | | | | data[3] and the subsequent is in accordance with the | - | | | | format of SSID/BSSID information. | - +--------------+----------------------------------------------------+---------------------------------------------------------------+-----------------------------------------------------------------------+ - | 0x10b’010000 | Version | | data[0]= great versiondata[1]= sub version | - +--------------+----------------------------------------------------+---------------------------------------------------------------+-----------------------------------------------------------------------+ - | 0x11B’010001 | Wi-Fi List | To send the Wi-Fi list to ESP32. | The format of the data frame is length + RSSI + SSID | - | | | | and it supports to be sent into fragments | - | | | | if the data length is too long. | - +--------------+----------------------------------------------------+---------------------------------------------------------------+-----------------------------------------------------------------------+ - | 0x12B’010010 | Report Error | To notify the mobile phone that there is an error with BluFi. | 0x00: sequence error | - + + + +-----------------------------------------------------------------------+ - | | | | 0x01: checksum error | - + + + +-----------------------------------------------------------------------+ - | | | | 0x02: decrypt error | - + + + +-----------------------------------------------------------------------+ - | | | | 0x03: encrypt error | - + + + +-----------------------------------------------------------------------+ - | | | | 0x04: init security error | - + + + +-----------------------------------------------------------------------+ - | | | | 0x05: dh malloc error | - + + + +-----------------------------------------------------------------------+ - | | | | 0x06: dh param error | - + + + +-----------------------------------------------------------------------+ - | | | | 0x07: read param error | - + + + +-----------------------------------------------------------------------+ - | | | | 0x08: make public error | - +--------------+----------------------------------------------------+---------------------------------------------------------------+-----------------------------------------------------------------------+ - | 0x13B’010011 | Custom Data | To send or receive custom data. | The data frame supports to be sent into | - | | | | fragments if the data length is too long. | - +--------------+----------------------------------------------------+---------------------------------------------------------------+-----------------------------------------------------------------------+ + +------------------+----------------------------------------------------+---------------------------------------------------------------+-----------------------------------------------------------------------+ + |Data Frame(Binary)| Implication | Explanation | Note | + +==================+====================================================+===============================================================+=======================================================================+ + | 0x0 (b’000000) | Send the negotiation data. | The negotiation data will be sent to the callback | The length of the data depends on the length field. | + | | | function registered in the application layer. | | + +------------------+----------------------------------------------------+---------------------------------------------------------------+-----------------------------------------------------------------------+ + | 0x1 (b’000001) | Send the BSSID for STA mode. | To send the BSSID of the AP for the STA device to | The length of the data depends on the length field. | + | | | connect under the condition that the SSID is hidden. | When the transmission direction is ESP32 to the mobile phone, | + | | | | it means to provide the mobile phone with the needed information. | + +------------------+----------------------------------------------------+---------------------------------------------------------------+-----------------------------------------------------------------------+ + | 0x2 (b’000010) | Send the SSID for STA mode. | To send the SSID of the AP for the STA device to connect. | The length of the data depends on the length field. | + | | | | When the transmission direction is ESP32 to the mobile phone, | + | | | | it means to provide the mobile phone with the needed information. | + +------------------+----------------------------------------------------+---------------------------------------------------------------+-----------------------------------------------------------------------+ + | 0x3 (b’000011) | Send the password for STA mode. | To send the password of the AP for the STA device to connect. | The length of the data depends on the length field. | + | | | | When the transmission direction is ESP32 to the mobile phone, | + | | | | it means to provide the mobile phone with the needed information. | + +------------------+----------------------------------------------------+---------------------------------------------------------------+-----------------------------------------------------------------------+ + | 0x4 (b’000100) | Send the SSID for SoftAP mode. | | The length of the data depends on the length field. | + | | | | When the transmission direction is ESP32 to the mobile phone, | + | | | | it means to provide the mobile phone with the needed information. | + +------------------+----------------------------------------------------+---------------------------------------------------------------+-----------------------------------------------------------------------+ + | 0x5 (b’000101) | Send the password for SoftAPmode. | | The length of the data depends on the length field. | + | | | | When the transmission direction is ESP32 to the mobile phone, | + | | | | it means to provide the mobile phone with the needed information. | + +------------------+----------------------------------------------------+---------------------------------------------------------------+-----------------------------------------------------------------------+ + | 0x6 (b’000110) | Set the maximum connection number for SoftAP mode. | | data[0] represents the value of the connection number, | + | | | | ranging from 1 to 4. When the transmission direction is ESP32 | + | | | | to the mobile phone, it means to provide the mobile phone with | + | | | | the needed information. | + +------------------+----------------------------------------------------+---------------------------------------------------------------+-----------------------------------------------------------------------+ + | 0x7 (b’000111) | Set the authentication mode for the SoftAP. | | data[0]: | + + + + +-----------------------------------------------------------------------+ + | | | | 0x00: OPEN | + + + + +-----------------------------------------------------------------------+ + | | | | 0x01: WEP | + + + + +-----------------------------------------------------------------------+ + | | | | 0x02: WPA_PSK | + + + + +-----------------------------------------------------------------------+ + | | | | 0x03: WPA2_PSK | + + + + +-----------------------------------------------------------------------+ + | | | | 0x04: WPA_WPA2_PSK | + + + + +-----------------------------------------------------------------------+ + | | | | When the transmission direction is ESP32 to the mobile phone, | + | | | | it means to provide the mobile phone with the needed information. | + +------------------+----------------------------------------------------+---------------------------------------------------------------+-----------------------------------------------------------------------+ + | 0x8 (b’001000) | Set the channel amount for SoftAP mode. | | data[0] represents the quantity of the supported channels, | + | | | | ranging from 1 to 14. | + | | | | When the transmission direction is ESP32 to the mobile phone, | + | | | | it means to provide the mobile phone with the needed information. | + +------------------+----------------------------------------------------+---------------------------------------------------------------+-----------------------------------------------------------------------+ + | 0x9 (b’001001) | Username | It provides the username of the GATT client when using | The length of the data depends on the length field. | + | | | encryption of enterprise level. | | + +------------------+----------------------------------------------------+---------------------------------------------------------------+-----------------------------------------------------------------------+ + | 0xa (b’001010) | CA Certification | It provides the CA Certification when using encryption | The length of the data depends on the length field. | + | | | of enterprise level. | The frame supports to be fragmented if the data length is not enough. | + +------------------+----------------------------------------------------+---------------------------------------------------------------+-----------------------------------------------------------------------+ + | 0xb (b’001011) | Client Certification | It provides the client certification when | The length of the data depends on the length field. | + | | | using encryption of enterprise level. | The frame supports to be fragmented if the data length is not enough. | + | | | Whether the private key is contained or not | | + | | | depends on the content of the certification. | | + +------------------+----------------------------------------------------+---------------------------------------------------------------+-----------------------------------------------------------------------+ + | 0xc (b’001100) | Server Certification | It provides the sever certification when using | The length of the data depends on the length field. | + | | | encryption of enterprise level. Whether the private key is | The frame supports to be fragmented if the data length is not enough. | + | | | contained or not depends on the content of the certification. | | + +------------------+----------------------------------------------------+---------------------------------------------------------------+-----------------------------------------------------------------------+ + | 0xd (b’001101) | ClientPrivate Key | It provides the private key of the client when | The length of the data depends on the length field. | + | | | using encryption of enterprise level. | The frame supports to be fragmented if the data length is not enough. | + +------------------+----------------------------------------------------+---------------------------------------------------------------+-----------------------------------------------------------------------+ + | 0xe (b’001110) | ServerPrivate Key | It provides the private key of the sever when | The length of the data depends on the length field. | + | | | using encryption of enterprise level. | The frame supports to be fragmented if the data length is not enough. | + +------------------+----------------------------------------------------+---------------------------------------------------------------+-----------------------------------------------------------------------+ + | 0xf (b’001111) | Wi-Fi Connection State Report | To notify the phone of the ESP32's Wi-Fi status, | data[0] represents opmode, including: | + + + + including STA status and SoftAP status. +-----------------------------------------------------------------------+ + | | | It is for the STA device to connect to the | 0x00: NULL | + + + + mobile phone or the SoftAP. +-----------------------------------------------------------------------+ + | | | However, when the mobile phone receives the Wi-Fi status, | 0x01: STA | + + + + it can reply to other frames in addition to this frame. +-----------------------------------------------------------------------+ + | | | | 0x02: SoftAP | + + + + +-----------------------------------------------------------------------+ + | | | | 0x03: SoftAP&STA | + + + + +-----------------------------------------------------------------------+ + | | | | data[1]:the connection state of the STA device, | + | | | | 0x0 indicates a connection state, | + | | | | and others represent a disconnected state; | + + + + +-----------------------------------------------------------------------+ + | | | | data[2]:the connection state of the SoftAP, | + | | | | that is, how many STA devices have been connected. | + + + + +-----------------------------------------------------------------------+ + | | | | data[3] and the subsequent is in accordance with the | + | | | | format of SSID/BSSID information. | + +------------------+----------------------------------------------------+---------------------------------------------------------------+-----------------------------------------------------------------------+ + | 0x10 (b’010000) | Version | | data[0]= great versiondata[1]= sub version | + +------------------+----------------------------------------------------+---------------------------------------------------------------+-----------------------------------------------------------------------+ + | 0x11 (b’010001) | Wi-Fi List | To send the Wi-Fi list to ESP32. | The format of the data frame is length + RSSI + SSID | + | | | | and it supports to be sent into fragments | + | | | | if the data length is too long. | + +------------------+----------------------------------------------------+---------------------------------------------------------------+-----------------------------------------------------------------------+ + | 0x12 (b’010010) | Report Error | To notify the mobile phone that there is an error with BluFi. | 0x00: sequence error | + + + + +-----------------------------------------------------------------------+ + | | | | 0x01: checksum error | + + + + +-----------------------------------------------------------------------+ + | | | | 0x02: decrypt error | + + + + +-----------------------------------------------------------------------+ + | | | | 0x03: encrypt error | + + + + +-----------------------------------------------------------------------+ + | | | | 0x04: init security error | + + + + +-----------------------------------------------------------------------+ + | | | | 0x05: dh malloc error | + + + + +-----------------------------------------------------------------------+ + | | | | 0x06: dh param error | + + + + +-----------------------------------------------------------------------+ + | | | | 0x07: read param error | + + + + +-----------------------------------------------------------------------+ + | | | | 0x08: make public error | + +------------------+----------------------------------------------------+---------------------------------------------------------------+-----------------------------------------------------------------------+ + | 0x13 (b’010011) | Custom Data | To send or receive custom data. | The data frame supports to be sent into | + | | | | fragments if the data length is too long. | + +------------------+----------------------------------------------------+---------------------------------------------------------------+-----------------------------------------------------------------------+ 2. Frame Control @@ -453,9 +453,3 @@ BluFi Service UUID: 0xFFFF,16 bit BluFi (the mobile -> ESP32): 0xFF01, writable Blufi (ESP32 -> the mobile phone): 0xFF02, readable and callable - -.. note:: - - 1. The Ack mechanism is already defined in the profile, but there is no implementation based on the code for the time being. - - 2. Other parts have been implemented. \ No newline at end of file diff --git a/docs/zh_CN/api-guides/blufi.rst b/docs/zh_CN/api-guides/blufi.rst index 667aca7514..6b15a98731 100644 --- a/docs/zh_CN/api-guides/blufi.rst +++ b/docs/zh_CN/api-guides/blufi.rst @@ -149,15 +149,15 @@ Ack 帧格式(8 bit): * 数据帧,可加密,可校验。 - **1.1 控制帧 (0x0b’00)** + **1.1 控制帧 (0x0 b’00)** +------------------+-----------------------------------+----------------------------------------------------------------+----------------------------------------------------------------------+ - | 控制帧 / 0x0b’00 | 含义 | 解释 | 备注 | + | 控制帧 (二进制) | 含义 | 解释 | 备注 | +==================+===================================+================================================================+======================================================================+ - | 0x0b’000000 | Ack | 用来回复对方发的帧, | Data 域使用1 byte Sequence 值, | + | 0x0 (b’000000) | Ack | 用来回复对方发的帧, | Data 域使用1 byte Sequence 值, | | | | Ack 帧的 Data 域使用回复对象帧的 Sequence 值。 | 与恢复对象帧的Sequence 值相同。 | +------------------+-----------------------------------+----------------------------------------------------------------+----------------------------------------------------------------------+ - | 0x1b’000001 | Set ESP32 to the security mode. | 通知 ESP32 发送数据时使用的安全模式, | Data 域占用 1 byte。 | + | 0x1 (b’000001) | Set ESP32 to the security mode. | 通知 ESP32 发送数据时使用的安全模式, | Data 域占用 1 byte。 | | | | 在该过程中可设置多次,每次设置后影响后续安全模式。 | 高 4 bit 为控制帧的安全模式,低 4bit 为数据帧的安全模式。 | + + + 在不设置的情况下,ESP32 默认控制帧和数据帧均为无校验、无加密。 +----------------------------------------------------------------------+ | | | 手机到 ESP32 方向依赖于帧 Control 域。 | b’0000:无校验、无加密; | @@ -168,7 +168,7 @@ Ack 帧格式(8 bit): + + + +----------------------------------------------------------------------+ | | | | b’0011:有校验、有加密。 | +------------------+-----------------------------------+----------------------------------------------------------------+----------------------------------------------------------------------+ - | 0x2b’000010 | Set the Wi-Fi opmode of ESP32. | 设置 ESP32 的 Wi-Fi 模式,帧包含 opmode 信息。 | data[0] 用于表示 opmode 类型,包括: | + | 0x2 (b’000010) | Set the Wi-Fi opmode of ESP32. | 设置 ESP32 的 Wi-Fi 模式,帧包含 opmode 信息。 | data[0] 用于表示 opmode 类型,包括: | + + + +----------------------------------------------------------------------+ | | | | 0x00: NULL; | + + + +----------------------------------------------------------------------+ @@ -181,67 +181,67 @@ Ack 帧格式(8 bit): | | | | 如果设置有包含 AP,请尽量优先 | | | | | 设置 AP 模式的SSID/Password/Max Conn Number 等。 | +------------------+-----------------------------------+----------------------------------------------------------------+----------------------------------------------------------------------+ - | 0x3b’000011 | Connect ESP32 to the AP. | 通知 ESP32,必要的信息已经发送完毕,可以连接 AP。 | 不包含 Data 域。 | + | 0x3 (b’000011) | Connect ESP32 to the AP. | 通知 ESP32,必要的信息已经发送完毕,可以连接 AP。 | 不包含 Data 域。 | +------------------+-----------------------------------+----------------------------------------------------------------+----------------------------------------------------------------------+ - | 0x4b’000100 | Disconnect ESP32 from the AP. | 通知 ESP32 断开与 AP 的连接 | 不包含 Data 域。 | + | 0x4 (b’000100) | Disconnect ESP32 from the AP. | 通知 ESP32 断开与 AP 的连接 | 不包含 Data 域。 | +------------------+-----------------------------------+----------------------------------------------------------------+----------------------------------------------------------------------+ - | 0x5b’000101 | Get the status of Wi-Fi. | 获取 ESP32 的 Wi-Fi 模式和状态等信息。 | 不包含 Data 域。 | + | 0x5 (b’000101) | Get the status of Wi-Fi. | 获取 ESP32 的 Wi-Fi 模式和状态等信息。 | 不包含 Data 域。 | | | | | ESP32 收到此控制帧后,后续会通过 Wi-Fi 连接状态 | | | | | 报告 (Wi-Fi Connection State Report) 数据帧来回复手机端当前 | | | | | 所处的 opmode、连接状态、SSID 等信息。提供给手机端的信息由应用决定。 | +------------------+-----------------------------------+----------------------------------------------------------------+----------------------------------------------------------------------+ - | 0x6b’000110 | Disconnect the STA device | 处于 SoftAP 模式时,踢掉某个 STA 设备。 | data[0~5] 为 STA 设备的 MAC 地址, | + | 0x6 (b’000110) | Disconnect the STA device | 处于 SoftAP 模式时,踢掉某个 STA 设备。 | data[0~5] 为 STA 设备的 MAC 地址, | | | from the SoftAP in SoftAP mode. | | 如有多个 STA,则 [6-11] 为第二个,依次类推。 | +------------------+-----------------------------------+----------------------------------------------------------------+----------------------------------------------------------------------+ - | 0x7b'000111 | Get the version. | | | + | 0x7 (b'000111) | Get the version. | | | +------------------+-----------------------------------+----------------------------------------------------------------+----------------------------------------------------------------------+ - | 0x8b’001000 | Tell ESP32 to disconnect | 通知 ESP32 断开蓝牙连接。 | ESP32 收到该指令后主动断开蓝牙连接。 | + | 0x8 (b’001000) | Tell ESP32 to disconnect | 通知 ESP32 断开蓝牙连接。 | ESP32 收到该指令后主动断开蓝牙连接。 | | | the BLE GATT link. | | | +------------------+-----------------------------------+----------------------------------------------------------------+----------------------------------------------------------------------+ - | 0x9b’001001 | Tell ESP32 to get the Wi-Fi list. | 通知 ESP32 扫描周围的 Wi-Fi 热点 | 不包含 Data 域。 | + | 0x9 (b’001001) | Tell ESP32 to get the Wi-Fi list. | 通知 ESP32 扫描周围的 Wi-Fi 热点 | 不包含 Data 域。 | | | | | ESP32 收到此控制帧后,会发送包含 Wi-Fi 热点 | | | | | 报告 (Wi-Fi List Report) 的数据帧回复 | | | | | 手机端 ESP32 周围的 Wi-Fi 热点。 | +------------------+-----------------------------------+----------------------------------------------------------------+----------------------------------------------------------------------+ - **1.2 数据帧 (0x1b’01)** + **1.2 数据帧 (0x1 b’01)** +---------------+----------------------------------------+------------------------------------------------+------------------------------------------------------+ - | 数据帧 | 含义 | 解释 | 备注 | + |数据帧 (二进制)| 含义 | 解释 | 备注 | +===============+========================================+================================================+======================================================+ - | 0x0 b’000000 | Negotiation data. | 用来发送协商数据,传输到应用层注册的回调函数。 | 数据长度与 Length 域有关。 | + | 0x0 (b’000000)| Negotiation data. | 用来发送协商数据,传输到应用层注册的回调函数。 | 数据长度与 Length 域有关。 | +---------------+----------------------------------------+------------------------------------------------+------------------------------------------------------+ - | 0x1 b’000001 | BSSID for STA mode. | STA 将要连接的 AP 的 BSSID(用于隐藏SSID)。 | 数据长度与 Length 域有关。 | + | 0x1 (b’000001)| BSSID for STA mode. | STA 将要连接的 AP 的 BSSID(用于隐藏SSID)。 | 数据长度与 Length 域有关。 | + + + +------------------------------------------------------+ | | | | 当传输方向为 ESP32 到手机时, | | | | | 表示向手机端提供信息。 | +---------------+----------------------------------------+------------------------------------------------+------------------------------------------------------+ - | 0x2 b’000010 | SSID for STA mode. | STA 将要连接的 AP 的 SSID。 | 数据长度与 Length 域有关。 | + | 0x2 (b’000010)| SSID for STA mode. | STA 将要连接的 AP 的 SSID。 | 数据长度与 Length 域有关。 | + + + +------------------------------------------------------+ | | | | 当传输方向为 ESP32 到手机时, | | | | | 表示向手机端提供信息。 | +---------------+----------------------------------------+------------------------------------------------+------------------------------------------------------+ - | 0x3 b’000011 | Password for STA mode. | STA 将要连接的 AP 的密码。 | 数据长度与 Length 域有关。 | + | 0x3 (b’000011)| Password for STA mode. | STA 将要连接的 AP 的密码。 | 数据长度与 Length 域有关。 | + + + +------------------------------------------------------+ | | | | 当传输方向为 ESP32 到手机时, | | | | | 表示向手机端提供信息。 | +---------------+----------------------------------------+------------------------------------------------+------------------------------------------------------+ - | 0x4 b’000100 | SSID for SoftAP mode. | SoftAP 模式使用的 SSID。 | 数据长度与 Length 域有关。 | + | 0x4 (b’000100)| SSID for SoftAP mode. | SoftAP 模式使用的 SSID。 | 数据长度与 Length 域有关。 | + + + +------------------------------------------------------+ | | | | 当传输方向为 ESP32 到手机时, | | | | | 表示向手机端提供信息。 | +---------------+----------------------------------------+------------------------------------------------+------------------------------------------------------+ - | 0x5 b’000101 | Password for SoftAPmode. | SoftAP 模式使用的密码。 | 数据长度与 Length 域有关。 | + | 0x5 (b’000101)| Password for SoftAPmode. | SoftAP 模式使用的密码。 | 数据长度与 Length 域有关。 | + + + +------------------------------------------------------+ | | | | 当传输方向为 ESP32 到手机时, | | | | | 表示向手机端提供信息。 | +---------------+----------------------------------------+------------------------------------------------+------------------------------------------------------+ - | 0x6 b’000110 | Max connection number for SoftAP mode. | AP 模式的最大连接数。 | data[0] 表示连接数的值,范围 1~4。 | + | 0x6 (b’000110)| Max connection number for SoftAP mode. | AP 模式的最大连接数。 | data[0] 表示连接数的值,范围 1~4。 | + + + +------------------------------------------------------+ | | | | 当传输方向为 ESP32 到手机时, | | | | | 表示向手机端提供信息。 | +---------------+----------------------------------------+------------------------------------------------+------------------------------------------------------+ - | 0x7b’000111 | Authentication mode for SoftAP mode. | AP 模式的认证模式。 | data[0]: | + | 0x7 (b’000111)| Authentication mode for SoftAP mode. | AP 模式的认证模式。 | data[0]: | + + + +------------------------------------------------------+ | | | | 0x00: OPEN; | + + + +------------------------------------------------------+ @@ -256,31 +256,31 @@ Ack 帧格式(8 bit): | | | | 当传输方向为 ESP32 到手机时, | | | | | 表示向手机端提供信息。 | +---------------+----------------------------------------+------------------------------------------------+------------------------------------------------------+ - | 0x8b’001000 | Channel for SoftAP mode. | SoftAP 模式的通道数量。 | data[0] 表示通道的数量,范围 1~14。 | + | 0x8 (b’001000)| Channel for SoftAP mode. | SoftAP 模式的通道数量。 | data[0] 表示通道的数量,范围 1~14。 | + + + +------------------------------------------------------+ | | | | 当传输方向为 ESP32 到手机时, | | | | | 表示向手机端提供信息。 | +---------------+----------------------------------------+------------------------------------------------+------------------------------------------------------+ - | 0x9b’001001 | Username. | 使用企业级加密时,Client 端的用户名。 | 数据长度与 Length 域有关。 | + | 0x9 (b’001001)| Username. | 使用企业级加密时,Client 端的用户名。 | 数据长度与 Length 域有关。 | +---------------+----------------------------------------+------------------------------------------------+------------------------------------------------------+ - | 0xab’001010 | CA certification. | 进行企业级加密时使用的 CA 证书。 | 数据长度与 Length 域有关, | + | 0xa (b’001010)| CA certification. | 进行企业级加密时使用的 CA 证书。 | 数据长度与 Length 域有关, | | | | | 长度不够,可用分片。 | +---------------+----------------------------------------+------------------------------------------------+------------------------------------------------------+ - | 0xbb’001011 | Client certification. | 进行企业级加密时,Client 端的证书。 | 数据长度与 Length 域有关, | + | 0xb (b’001011)| Client certification. | 进行企业级加密时,Client 端的证书。 | 数据长度与 Length 域有关, | + + +------------------------------------------------+ 长度不够,可用分片。 + | | | 可包含或不包含私钥,由证书内容决定。 | | +---------------+----------------------------------------+------------------------------------------------+------------------------------------------------------+ - | 0xcb’001100 | Server certification. | 进行企业级加密时,Server 端的证书。 | 数据长度与 Length 域有关, | + | 0xc (b’001100)| Server certification. | 进行企业级加密时,Server 端的证书。 | 数据长度与 Length 域有关, | + + +------------------------------------------------+ 长度不够,可用分片。 + | | | 可包含或不包含私钥,由证书内容决定。 | | +---------------+----------------------------------------+------------------------------------------------+------------------------------------------------------+ - | 0xdb’001101 | Client private key. | 进行企业级加密时,Client 端的私钥。 | 数据长度与 Length 域有关, | + | 0xd (b’001101)| Client private key. | 进行企业级加密时,Client 端的私钥。 | 数据长度与 Length 域有关, | | | | | 长度不够,可用分片。 | +---------------+----------------------------------------+------------------------------------------------+------------------------------------------------------+ - | 0xeb’001110 | Server private key. | 进行企业级加密时,Server 端的私钥。 | 数据长度与 Length 域有关, | + | 0xe (b’001110)| Server private key. | 进行企业级加密时,Server 端的私钥。 | 数据长度与 Length 域有关, | | | | | 长度不够,可用分片。 | +---------------+----------------------------------------+------------------------------------------------+------------------------------------------------------+ - | 0xf b’001111 | Wi-Fi connection state report. | 通知手机 ESP32 的 Wi-Fi 状态, | data[0] 表示 opmode,包括: | + | 0xf (b’001111)| Wi-Fi connection state report. | 通知手机 ESP32 的 Wi-Fi 状态, | data[0] 表示 opmode,包括: | | | | 包括 STA状态和 SoftAP 状态, | | | | | 用于手机配置 STA 连接时的通知, | | | | | 或有 STA 连接上 SoftAP 时的通知。 | | @@ -306,10 +306,10 @@ Ack 帧格式(8 bit): + + + +------------------------------------------------------+ | | | | data[1]=sub version | +---------------+----------------------------------------+------------------------------------------------+------------------------------------------------------+ - | 0x11 B’010001 | Wi-Fi list. | 通知手机 ESP32 周围的 Wi-Fi 热点列表。 | 数据帧数据格式为 Length + RSSI + SSID, | + |0x11 (b’010001)| Wi-Fi list. | 通知手机 ESP32 周围的 Wi-Fi 热点列表。 | 数据帧数据格式为 Length + RSSI + SSID, | | | | | 数据较长时可分片发送。 | +---------------+----------------------------------------+------------------------------------------------+------------------------------------------------------+ - | 0x12 B’010010 | Report error. | 通知手机 BluFi 过程出现异常错误。 | 0x00: sequence error; | + |0x12 (b’010010)| Report error. | 通知手机 BluFi 过程出现异常错误。 | 0x00: sequence error; | + + + +------------------------------------------------------+ | | | | 0x01: checksum error; | + + + +------------------------------------------------------+ @@ -327,7 +327,7 @@ Ack 帧格式(8 bit): + + + +------------------------------------------------------+ | | | | 0x08: make public error. | +---------------+----------------------------------------+------------------------------------------------+------------------------------------------------------+ - | 0x13 B’010011 | Custom data. | 用户发送或者接收自定义数据。 | 数据较长时可分片发送。 | + |0x13 (b’010011)| Custom data. | 用户发送或者接收自定义数据。 | 数据较长时可分片发送。 | +---------------+----------------------------------------+------------------------------------------------+------------------------------------------------------+ 2. Frame Control @@ -448,10 +448,4 @@ BluFi Service UUID: 0xFFFF,16 bit BluFi (手机 -> ESP32) 特性:0xFF01,主要权限:可写 -BluFi (ESP32 -> 手机) 特性:0xFF02,主要权限:可读可通知 - -.. note:: - - 1. 目前 Ack 机制已经在该 Profile 协议中定义,但是还没有代码实现。 - - 2. 其他部分均已实现。 \ No newline at end of file +BluFi (ESP32 -> 手机) 特性:0xFF02,主要权限:可读可通知 \ No newline at end of file diff --git a/examples/bluetooth/ble_compatibility_test/README.md b/examples/bluetooth/ble_compatibility_test/README.md new file mode 100644 index 0000000000..928a4b6a77 --- /dev/null +++ b/examples/bluetooth/ble_compatibility_test/README.md @@ -0,0 +1,16 @@ +ESP-IDF BLE Compatibility Test Example +======================================= + +This demo is to test the compatibility of Bluetooth and mobile phones. + +* ESP32 Module: ESP-WROOM-32 + +* IDF version: 7c29a39d6f9f2dfbefc49d34d34e9267afc7200d + +* [Test case](https://github.com/espressif/esp-idf/blob/master/examples/bluetooth/ble_compatibility_test/ble_compatibility_test_case.md) + +* Test APK: LightBlue V1.1.3 + +* [Test report](https://github.com/espressif/esp-idf/blob/master/examples/bluetooth/ble_compatibility_test/esp_ble_compatibility_test_report.md) + + diff --git a/examples/bluetooth/ble_spp_client/main/spp_client_demo.c b/examples/bluetooth/ble_spp_client/main/spp_client_demo.c index 03561acb8e..ae3da8e1c6 100644 --- a/examples/bluetooth/ble_spp_client/main/spp_client_demo.c +++ b/examples/bluetooth/ble_spp_client/main/spp_client_demo.c @@ -129,6 +129,10 @@ static void notify_event_handler(esp_ble_gattc_cb_param_t * p_data) ESP_LOGI(GATTC_TAG,"+INDICATE:handle = %d,length = %d ", p_data->notify.handle, p_data->notify.value_len); } handle = p_data->notify.handle; + if(db == NULL) { + ESP_LOGE(GATTC_TAG, " %s db is NULL\n", __func__); + return; + } if(handle == db[SPP_IDX_SPP_DATA_NTY_VAL].attribute_handle){ #ifdef SPP_DEBUG_MODE esp_log_buffer_char(GATTC_TAG, (char *)p_data->notify.value, p_data->notify.value_len); @@ -464,19 +468,21 @@ void spp_client_reg_task(void* arg) for(;;) { vTaskDelay(100 / portTICK_PERIOD_MS); if(xQueueReceive(cmd_reg_queue, &cmd_id, portMAX_DELAY)) { - if(cmd_id == SPP_IDX_SPP_DATA_NTY_VAL){ - ESP_LOGI(GATTC_TAG,"Index = %d,UUID = 0x%04x, handle = %d \n", cmd_id, (db+SPP_IDX_SPP_DATA_NTY_VAL)->uuid.uuid.uuid16, (db+SPP_IDX_SPP_DATA_NTY_VAL)->attribute_handle); - esp_ble_gattc_register_for_notify(spp_gattc_if, gl_profile_tab[PROFILE_APP_ID].remote_bda, (db+SPP_IDX_SPP_DATA_NTY_VAL)->attribute_handle); - }else if(cmd_id == SPP_IDX_SPP_STATUS_VAL){ - ESP_LOGI(GATTC_TAG,"Index = %d,UUID = 0x%04x, handle = %d \n", cmd_id, (db+SPP_IDX_SPP_STATUS_VAL)->uuid.uuid.uuid16, (db+SPP_IDX_SPP_STATUS_VAL)->attribute_handle); - esp_ble_gattc_register_for_notify(spp_gattc_if, gl_profile_tab[PROFILE_APP_ID].remote_bda, (db+SPP_IDX_SPP_STATUS_VAL)->attribute_handle); - } + if(db != NULL) { + if(cmd_id == SPP_IDX_SPP_DATA_NTY_VAL){ + ESP_LOGI(GATTC_TAG,"Index = %d,UUID = 0x%04x, handle = %d \n", cmd_id, (db+SPP_IDX_SPP_DATA_NTY_VAL)->uuid.uuid.uuid16, (db+SPP_IDX_SPP_DATA_NTY_VAL)->attribute_handle); + esp_ble_gattc_register_for_notify(spp_gattc_if, gl_profile_tab[PROFILE_APP_ID].remote_bda, (db+SPP_IDX_SPP_DATA_NTY_VAL)->attribute_handle); + }else if(cmd_id == SPP_IDX_SPP_STATUS_VAL){ + ESP_LOGI(GATTC_TAG,"Index = %d,UUID = 0x%04x, handle = %d \n", cmd_id, (db+SPP_IDX_SPP_STATUS_VAL)->uuid.uuid.uuid16, (db+SPP_IDX_SPP_STATUS_VAL)->attribute_handle); + esp_ble_gattc_register_for_notify(spp_gattc_if, gl_profile_tab[PROFILE_APP_ID].remote_bda, (db+SPP_IDX_SPP_STATUS_VAL)->attribute_handle); + } #ifdef SUPPORT_HEARTBEAT - else if(cmd_id == SPP_IDX_SPP_HEARTBEAT_VAL){ - ESP_LOGI(GATTC_TAG,"Index = %d,UUID = 0x%04x, handle = %d \n", cmd_id, (db+SPP_IDX_SPP_HEARTBEAT_VAL)->uuid.uuid.uuid16, (db+SPP_IDX_SPP_HEARTBEAT_VAL)->attribute_handle); - esp_ble_gattc_register_for_notify(spp_gattc_if, gl_profile_tab[PROFILE_APP_ID].remote_bda, (db+SPP_IDX_SPP_HEARTBEAT_VAL)->attribute_handle); - } + else if(cmd_id == SPP_IDX_SPP_HEARTBEAT_VAL){ + ESP_LOGI(GATTC_TAG,"Index = %d,UUID = 0x%04x, handle = %d \n", cmd_id, (db+SPP_IDX_SPP_HEARTBEAT_VAL)->uuid.uuid.uuid16, (db+SPP_IDX_SPP_HEARTBEAT_VAL)->attribute_handle); + esp_ble_gattc_register_for_notify(spp_gattc_if, gl_profile_tab[PROFILE_APP_ID].remote_bda, (db+SPP_IDX_SPP_HEARTBEAT_VAL)->attribute_handle); + } #endif + } } } } @@ -490,7 +496,7 @@ void spp_heart_beat_task(void * arg) vTaskDelay(50 / portTICK_PERIOD_MS); if(xQueueReceive(cmd_heartbeat_queue, &cmd_id, portMAX_DELAY)) { while(1){ - if((is_connect == true)&&((db+SPP_IDX_SPP_HEARTBEAT_VAL)->properties & (ESP_GATT_CHAR_PROP_BIT_WRITE_NR | ESP_GATT_CHAR_PROP_BIT_WRITE))){ + if((is_connect == true) && (db != NULL) && ((db+SPP_IDX_SPP_HEARTBEAT_VAL)->properties & (ESP_GATT_CHAR_PROP_BIT_WRITE_NR | ESP_GATT_CHAR_PROP_BIT_WRITE))){ esp_ble_gattc_write_char( spp_gattc_if, spp_conn_id, (db+SPP_IDX_SPP_HEARTBEAT_VAL)->attribute_handle, @@ -551,7 +557,7 @@ void uart_task(void *pvParameters) switch (event.type) { //Event of UART receving data case UART_DATA: - if (event.size && (is_connect == true) && ((db+SPP_IDX_SPP_DATA_RECV_VAL)->properties & (ESP_GATT_CHAR_PROP_BIT_WRITE_NR | ESP_GATT_CHAR_PROP_BIT_WRITE))) { + if (event.size && (is_connect == true) && (db != NULL) && ((db+SPP_IDX_SPP_DATA_RECV_VAL)->properties & (ESP_GATT_CHAR_PROP_BIT_WRITE_NR | ESP_GATT_CHAR_PROP_BIT_WRITE))) { uint8_t * temp = NULL; temp = (uint8_t *)malloc(sizeof(uint8_t)*event.size); if(temp == NULL){ diff --git a/examples/bluetooth/blufi/README.md b/examples/bluetooth/blufi/README.md index 3b074c72d9..c8246023ae 100644 --- a/examples/bluetooth/blufi/README.md +++ b/examples/bluetooth/blufi/README.md @@ -3,8 +3,16 @@ ESP-IDF Blufi demo This is the demo for bluetooth config wifi connection to ap. -attentions: - 1. Please use the BLEDEMO APK - 2. As the GATTServer start a litte slowly, so Please Wait for a period, then use apk scan the apk util find a random address devices named Espressif_008 - 3. Just a unstable version.. +To test this demo, you need to prepare a mobile phone with blufi application installed. You can download the blufi application from [Android version](https://github.com/EspressifApp/EspBlufi) and [iOS version](https://itunes.apple.com/cn/app/espblufi/id1450614082?mt=8). +Blufi is completely open source, here is the download link: + +* [blufi source code](https://github.com/espressif/esp-idf/tree/master/examples/bluetooth/blufi) + +* [BluFi protocol](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/blufi.html?highlight=blufi#the-frame-formats-defined-in-blufi) + +* [iOS source code](https://github.com/EspressifApp/EspBlufiForiOS) + +* [Android source code](https://github.com/EspressifApp/EspBlufi) + +* [Bluetooth Network User Guide CN](https://www.espressif.com/sites/default/files/documentation/esp32_bluetooth_networking_user_guide_cn.pdf) \ No newline at end of file diff --git a/examples/bluetooth/gatt_server/main/gatts_demo.c b/examples/bluetooth/gatt_server/main/gatts_demo.c index 789f15fdad..6770d8f862 100644 --- a/examples/bluetooth/gatt_server/main/gatts_demo.c +++ b/examples/bluetooth/gatt_server/main/gatts_demo.c @@ -98,7 +98,7 @@ static uint8_t adv_service_uuid128[32] = { static esp_ble_adv_data_t adv_data = { .set_scan_rsp = false, .include_name = true, - .include_txpower = true, + .include_txpower = false, .min_interval = 0x0006, //slave connection min interval, Time = min_interval * 1.25 msec .max_interval = 0x0010, //slave connection max interval, Time = max_interval * 1.25 msec .appearance = 0x00, @@ -115,8 +115,8 @@ static esp_ble_adv_data_t scan_rsp_data = { .set_scan_rsp = true, .include_name = true, .include_txpower = true, - .min_interval = 0x0006, - .max_interval = 0x0010, + //.min_interval = 0x0006, + //.max_interval = 0x0010, .appearance = 0x00, .manufacturer_len = 0, //TEST_MANUFACTURER_DATA_LEN, .p_manufacturer_data = NULL, //&test_manufacturer[0], From 0a040345cc9f45d16a6d4d9fb6dc6af21bee377e Mon Sep 17 00:00:00 2001 From: Konstantin Kondrashov Date: Wed, 12 Jun 2019 19:00:44 +0800 Subject: [PATCH 064/486] mbedtls: Fix Z->s in mbedtls_mpi_exp_mod() Z->s should never be zero, only 1 or -1. Added additional checks for X, Y and M args to correctly set Z->s. Closes: https://github.com/espressif/esp-idf/issues/1681 Closes: https://github.com/espressif/esp-idf/issues/3603 Closes: IDFGH-1313 --- components/mbedtls/port/esp_bignum.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/components/mbedtls/port/esp_bignum.c b/components/mbedtls/port/esp_bignum.c index 843c40d9b6..50e1d9f364 100644 --- a/components/mbedtls/port/esp_bignum.c +++ b/components/mbedtls/port/esp_bignum.c @@ -359,6 +359,18 @@ int mbedtls_mpi_exp_mod( mbedtls_mpi* Z, const mbedtls_mpi* X, const mbedtls_mpi mbedtls_mpi *Rinv; /* points to _Rinv (if not NULL) othwerwise &RR_new */ mbedtls_mpi_uint Mprime; + if (mbedtls_mpi_cmp_int(M, 0) <= 0 || (M->p[0] & 1) == 0) { + return MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + } + + if (mbedtls_mpi_cmp_int(Y, 0) < 0) { + return MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + } + + if (mbedtls_mpi_cmp_int(Y, 0) == 0) { + return mbedtls_mpi_lset(Z, 1); + } + if (hw_words * 32 > 4096) { return MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; } @@ -392,13 +404,24 @@ int mbedtls_mpi_exp_mod( mbedtls_mpi* Z, const mbedtls_mpi* X, const mbedtls_mpi start_op(RSA_START_MODEXP_REG); /* X ^ Y may actually be shorter than M, but unlikely when used for crypto */ - MBEDTLS_MPI_CHK( mbedtls_mpi_grow(Z, m_words) ); + if ((ret = mbedtls_mpi_grow(Z, m_words)) != 0) { + esp_mpi_release_hardware(); + goto cleanup; + } wait_op_complete(RSA_START_MODEXP_REG); mem_block_to_mpi(Z, RSA_MEM_Z_BLOCK_BASE, m_words); esp_mpi_release_hardware(); + // Compensate for negative X + if (X->s == -1 && (Y->p[0] & 1) != 0) { + Z->s = -1; + MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(Z, M, Z)); + } else { + Z->s = 1; + } + cleanup: if (_Rinv == NULL) { mbedtls_mpi_free(&Rinv_new); From bc8c8c68ddc8c2f4eeb1ae66bf763554e7aa388b Mon Sep 17 00:00:00 2001 From: Konstantin Kondrashov Date: Thu, 13 Jun 2019 22:09:50 +0800 Subject: [PATCH 065/486] mbedtls: Add UTs for modexp --- components/mbedtls/test/test_mbedtls_mpi.c | 80 ++++++++++++++++------ 1 file changed, 59 insertions(+), 21 deletions(-) diff --git a/components/mbedtls/test/test_mbedtls_mpi.c b/components/mbedtls/test/test_mbedtls_mpi.c index ade0fdb6b8..084fe34845 100644 --- a/components/mbedtls/test/test_mbedtls_mpi.c +++ b/components/mbedtls/test/test_mbedtls_mpi.c @@ -11,6 +11,7 @@ #include "unity.h" #include "sdkconfig.h" +#define MBEDTLS_OK 0 /* Debugging function to print an MPI number to stdout. Happens to print output that can be copy-pasted directly into a Python shell. @@ -116,11 +117,14 @@ TEST_CASE("test MPI multiplication", "[bignum]") 4096); } -static void test_bignum_modexp(const char *z_str, const char *x_str, const char *y_str, const char *m_str) +static bool test_bignum_modexp(const char *z_str, const char *x_str, const char *y_str, const char *m_str, int ret_error) { mbedtls_mpi Z, X, Y, M; char z_buf[400] = { 0 }; size_t z_buf_len = 0; + bool fail = false; + + printf("%s = (%s ^ %s) mod %s ret=%d ... ", z_str, x_str, y_str, m_str, ret_error); mbedtls_mpi_init(&Z); mbedtls_mpi_init(&X); @@ -136,38 +140,72 @@ static void test_bignum_modexp(const char *z_str, const char *x_str, const char //mbedtls_mpi_printf("M", &M); /* Z = (X ^ Y) mod M */ - TEST_ASSERT_FALSE(mbedtls_mpi_exp_mod(&Z, &X, &Y, &M, NULL)); - - mbedtls_mpi_write_string(&Z, 16, z_buf, sizeof(z_buf)-1, &z_buf_len); - TEST_ASSERT_EQUAL_STRING_MESSAGE(z_str, z_buf, "mbedtls_mpi_exp_mod incorrect"); + if (ret_error != mbedtls_mpi_exp_mod(&Z, &X, &Y, &M, NULL)) { + fail = true; + } + if (ret_error == MBEDTLS_OK) { + mbedtls_mpi_write_string(&Z, 16, z_buf, sizeof(z_buf)-1, &z_buf_len); + if (memcmp(z_str, z_buf, strlen(z_str)) != 0) { + printf("\n Expected '%s' Was '%s' \n", z_str, z_buf); + fail = true; + } + } + mbedtls_mpi_free(&Z); mbedtls_mpi_free(&X); mbedtls_mpi_free(&Y); mbedtls_mpi_free(&M); + + if (fail == true) { + printf(" FAIL\n"); + } else { + printf(" PASS\n"); + } + return fail; } TEST_CASE("test MPI modexp", "[bignum]") { - test_bignum_modexp("01000000", "1000", "2", "FFFFFFFF"); - test_bignum_modexp("014B5A90", "1234", "2", "FFFFFFF"); - test_bignum_modexp("01234321", "1111", "2", "FFFFFFFF"); - test_bignum_modexp("02", "5", "1", "3"); - test_bignum_modexp("22", "55", "1", "33"); - test_bignum_modexp("0222", "555", "1", "333"); - test_bignum_modexp("2222", "5555", "1", "3333"); - test_bignum_modexp("11", "5555", "1", "33"); + bool test_error = false; + printf("Z = (X ^ Y) mod M \n"); + // test_bignum_modexp(Z, X, Y, M, ret_error); + test_error |= test_bignum_modexp("01000000", "1000", "2", "FFFFFFFF", MBEDTLS_OK); + test_error |= test_bignum_modexp("014B5A90", "1234", "2", "FFFFFFF", MBEDTLS_OK); + test_error |= test_bignum_modexp("01234321", "1111", "2", "FFFFFFFF", MBEDTLS_OK); + test_error |= test_bignum_modexp("02", "5", "1", "3", MBEDTLS_OK); + test_error |= test_bignum_modexp("22", "55", "1", "33", MBEDTLS_OK); + test_error |= test_bignum_modexp("0222", "555", "1", "333", MBEDTLS_OK); + test_error |= test_bignum_modexp("2222", "5555", "1", "3333", MBEDTLS_OK); + test_error |= test_bignum_modexp("11", "5555", "1", "33", MBEDTLS_OK); + test_error |= test_bignum_modexp("55", "1111", "1", "77", MBEDTLS_OK); + test_error |= test_bignum_modexp("88", "1111", "2", "BB", MBEDTLS_OK); + test_error |= test_bignum_modexp("01000000", "2", "128", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", MBEDTLS_OK); + test_error |= test_bignum_modexp("0ABCDEF12345", "ABCDEF12345", "1", "FFFFFFFFFFFF", MBEDTLS_OK); + test_error |= test_bignum_modexp("0ABCDE", "ABCDE", "1", "FFFFF", MBEDTLS_OK); - test_bignum_modexp("55", "1111", "1", "77"); - test_bignum_modexp("88", "1111", "2", "BB"); + test_error |= test_bignum_modexp("04", "2", "2", "9", MBEDTLS_OK); + test_error |= test_bignum_modexp("04", "2", "-2", "9", MBEDTLS_ERR_MPI_BAD_INPUT_DATA); + test_error |= test_bignum_modexp("04", "2", "2", "-9", MBEDTLS_ERR_MPI_BAD_INPUT_DATA); + test_error |= test_bignum_modexp("04", "2", "-2", "-9", MBEDTLS_ERR_MPI_BAD_INPUT_DATA); - test_bignum_modexp("01000000", "2", "128", - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"); + test_error |= test_bignum_modexp("01", "2", "0", "9", MBEDTLS_OK); + test_error |= test_bignum_modexp("04", "2", "0", "0", MBEDTLS_ERR_MPI_BAD_INPUT_DATA); + test_error |= test_bignum_modexp("04", "2", "2", "0", MBEDTLS_ERR_MPI_BAD_INPUT_DATA); + test_error |= test_bignum_modexp("00", "0", "2", "9", MBEDTLS_OK); + test_error |= test_bignum_modexp("01", "0", "0", "9", MBEDTLS_OK); - /* failures below here... */ - test_bignum_modexp("0ABCDEF12345", "ABCDEF12345", "1", "FFFFFFFFFFFF"); - test_bignum_modexp("0ABCDE", "ABCDE", "1", "FFFFF"); + test_error |= test_bignum_modexp("04", "-2", "2", "9", MBEDTLS_OK); + test_error |= test_bignum_modexp("01", "-2", "0", "9", MBEDTLS_OK); + test_error |= test_bignum_modexp("07", "-2", "7", "9", MBEDTLS_OK); + test_error |= test_bignum_modexp("07", "-2", "1", "9", MBEDTLS_OK); + test_error |= test_bignum_modexp("02", "2", "1", "9", MBEDTLS_OK); + test_error |= test_bignum_modexp("01", "2", "0", "9", MBEDTLS_OK); - test_bignum_modexp("04", "2", "2", "9"); + test_error |= test_bignum_modexp("05", "5", "7", "7", MBEDTLS_OK); + test_error |= test_bignum_modexp("02", "-5", "7", "7", MBEDTLS_OK); + test_error |= test_bignum_modexp("01", "-5", "7", "3", MBEDTLS_OK); + + TEST_ASSERT_FALSE_MESSAGE(test_error, "mbedtls_mpi_exp_mod incorrect for some tests\n"); } From b107b832caef09f4432e5716bd12bfad0eff04d6 Mon Sep 17 00:00:00 2001 From: Konstantin Kondrashov Date: Mon, 17 Jun 2019 15:44:25 +0800 Subject: [PATCH 066/486] sntp/lwip: Add SNTP_UPDATE_DELAY option in Kconfig Closes: https://github.com/espressif/esp-idf/issues/2277 Closes: IDFGH-337 --- components/lwip/Kconfig | 31 +++++++++++++------ components/lwip/port/esp32/include/lwipopts.h | 10 ++++++ 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/components/lwip/Kconfig b/components/lwip/Kconfig index 6fa8a507e2..2cca72b2fb 100644 --- a/components/lwip/Kconfig +++ b/components/lwip/Kconfig @@ -84,15 +84,6 @@ menu "LWIP" help Enabling this option allows checking for available data on a netconn. - config LWIP_DHCP_MAX_NTP_SERVERS - int "Maximum number of NTP servers" - default 1 - range 1 16 - help - Set maximum number of NTP servers used by LwIP SNTP module. - First argument of sntp_setserver/sntp_setservername functions - is limited to this value. - config LWIP_IP_FRAG bool "Enable fragment outgoing IP packets" default n @@ -572,4 +563,26 @@ menu "LWIP" endmenu # LWIP RAW API + menu "SNTP" + + config LWIP_DHCP_MAX_NTP_SERVERS + int "Maximum number of NTP servers" + default 1 + range 1 16 + help + Set maximum number of NTP servers used by LwIP SNTP module. + First argument of sntp_setserver/sntp_setservername functions + is limited to this value. + + config LWIP_SNTP_UPDATE_DELAY + int "Request interval to update time (ms)" + range 15000 4294967295 + default 3600000 + help + This option allows you to set the time update period via SNTP. + Default is 1 hour. Must not be below 15 seconds by specification. + (SNTPv4 RFC 4330 enforces a minimum update time of 15 seconds). + + endmenu # SNTP + endmenu diff --git a/components/lwip/port/esp32/include/lwipopts.h b/components/lwip/port/esp32/include/lwipopts.h index fd6e1262f8..713d3ff097 100644 --- a/components/lwip/port/esp32/include/lwipopts.h +++ b/components/lwip/port/esp32/include/lwipopts.h @@ -841,6 +841,16 @@ enum { #define LWIP_DHCP_MAX_NTP_SERVERS CONFIG_LWIP_DHCP_MAX_NTP_SERVERS #define LWIP_TIMEVAL_PRIVATE 0 +/* + -------------------------------------- + ------------ SNTP options ------------ + -------------------------------------- +*/ +/* + * SNTP update delay - in milliseconds + */ +#define SNTP_UPDATE_DELAY CONFIG_LWIP_SNTP_UPDATE_DELAY + #define SNTP_SET_SYSTEM_TIME_US(sec, us) \ do { \ struct timeval tv = { .tv_sec = sec, .tv_usec = us }; \ From c5c716e9d7838b07d2c61e06cda4268919b69f46 Mon Sep 17 00:00:00 2001 From: suda-morris <362953310@qq.com> Date: Wed, 12 Jun 2019 11:52:23 +0800 Subject: [PATCH 067/486] esp_wifi: fix wrong path of phy_init_data Closes https://github.com/espressif/esp-idf/issues/3482 --- components/esp32/Makefile.projbuild | 36 +------------------ components/esp_wifi/CMakeLists.txt | 17 +++++---- components/esp_wifi/Makefile.projbuild | 32 +++++++++++++++++ components/esp_wifi/component.mk | 2 +- .../{ => esp32}/include/phy_init_data.h | 0 docs/en/api-guides/RF_calibration.rst | 2 +- tools/ci/test_build_system.sh | 13 +++++++ tools/ci/test_build_system_cmake.sh | 15 +++++++- 8 files changed, 72 insertions(+), 45 deletions(-) create mode 100644 components/esp_wifi/Makefile.projbuild rename components/esp_wifi/{ => esp32}/include/phy_init_data.h (100%) diff --git a/components/esp32/Makefile.projbuild b/components/esp32/Makefile.projbuild index b99415538e..0ae77a69d2 100644 --- a/components/esp32/Makefile.projbuild +++ b/components/esp32/Makefile.projbuild @@ -1,44 +1,10 @@ -ifdef CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION - -PHY_INIT_DATA_OBJ = $(BUILD_DIR_BASE)/phy_init_data.o -PHY_INIT_DATA_BIN = $(BUILD_DIR_BASE)/phy_init_data.bin - -# Command to flash PHY init data partition -PHY_INIT_DATA_FLASH_CMD = $(ESPTOOLPY_SERIAL) write_flash $(PHY_DATA_OFFSET) $(PHY_INIT_DATA_BIN) -ESPTOOL_ALL_FLASH_ARGS += $(PHY_DATA_OFFSET) $(PHY_INIT_DATA_BIN) - -ESP32_COMPONENT_PATH := $(COMPONENT_PATH) - -$(PHY_INIT_DATA_OBJ): $(ESP32_COMPONENT_PATH)/phy_init_data.h $(BUILD_DIR_BASE)/include/sdkconfig.h - $(summary) CC $(notdir $@) - printf "#include \"phy_init_data.h\"\n" | $(CC) -I $(BUILD_DIR_BASE)/include -I $(ESP32_COMPONENT_PATH) -I $(ESP32_COMPONENT_PATH)/include -c -o $@ -xc - - -$(PHY_INIT_DATA_BIN): $(PHY_INIT_DATA_OBJ) - $(summary) BIN $(notdir $@) - $(OBJCOPY) -O binary $< $@ - -phy_init_data: $(PHY_INIT_DATA_BIN) - -phy_init_data-flash: $(BUILD_DIR_BASE)/phy_init_data.bin - @echo "Flashing PHY init data..." - $(PHY_INIT_DATA_FLASH_CMD) - -phy_init_data-clean: - rm -f $(PHY_INIT_DATA_BIN) $(PHY_INIT_DATA_OBJ) - -all: phy_init_data -flash: phy_init_data - -endif # CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION - - # Enable psram cache bug workaround in compiler if selected ifdef CONFIG_SPIRAM_CACHE_WORKAROUND CFLAGS+=-mfix-esp32-psram-cache-issue CXXFLAGS+=-mfix-esp32-psram-cache-issue endif -# Enable dynamic esp_timer overflow value if building unit tests +# Enable dynamic esp_timer overflow value if building unit tests ifneq ("$(filter esp32,$(TEST_COMPONENTS_LIST))","") CPPFLAGS += -DESP_TIMER_DYNAMIC_OVERFLOW_VAL endif diff --git a/components/esp_wifi/CMakeLists.txt b/components/esp_wifi/CMakeLists.txt index c8f31cd53c..e4ba2db060 100644 --- a/components/esp_wifi/CMakeLists.txt +++ b/components/esp_wifi/CMakeLists.txt @@ -1,3 +1,6 @@ +idf_build_get_property(idf_target IDF_TARGET) +idf_build_get_property(build_dir BUILD_DIR) + set(COMPONENT_SRCS "src/coexist.c" "src/fast_crypto_ops.c" @@ -6,7 +9,7 @@ set(COMPONENT_SRCS "src/phy_init.c" "src/restore.c" "src/wifi_init.c") -set(COMPONENT_ADD_INCLUDEDIRS "include") +set(COMPONENT_ADD_INCLUDEDIRS "include" "${idf_target}/include") set(COMPONENT_PRIV_INCLUDEDIRS) set(COMPONENT_PRIV_REQUIRES wpa_supplicant nvs_flash) @@ -15,13 +18,13 @@ if(NOT CONFIG_ESP32_NO_BLOBS) endif() register_component() -target_link_libraries(${COMPONENT_LIB} PUBLIC "-L ${CMAKE_CURRENT_SOURCE_DIR}/lib_${IDF_TARGET}") +target_link_libraries(${COMPONENT_LIB} PUBLIC "-L ${CMAKE_CURRENT_SOURCE_DIR}/lib_${idf_target}") if(NOT CONFIG_ESP32_NO_BLOBS) set(blobs coexist core espnow mesh net80211 phy pp rtc smartconfig wpa2 wpa wps) foreach(blob ${blobs}) add_library(${blob} STATIC IMPORTED) - set_property(TARGET ${blob} PROPERTY IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/lib_${IDF_TARGET}/lib${blob}.a) + set_property(TARGET ${blob} PROPERTY IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/lib_${idf_target}/lib${blob}.a) target_link_libraries(${COMPONENT_LIB} PUBLIC ${blob}) foreach(_blob ${blobs}) @@ -35,7 +38,7 @@ if(NOT CONFIG_ESP32_NO_BLOBS) endif() if(CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION) - idf_build_get_property(build_dir BUILD_DIR) + idf_component_get_property(esp_common_dir esp_common COMPONENT_DIR) partition_table_get_partition_info(phy_partition_offset "--partition-type data --partition-subtype phy" "offset") set(phy_init_data_bin "${build_dir}/phy_init_data.bin") @@ -43,11 +46,11 @@ if(CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION) # the object file to a raw binary add_custom_command( OUTPUT ${phy_init_data_bin} - DEPENDS ${CMAKE_CURRENT_LIST_DIR}/phy_init_data.h + DEPENDS ${CMAKE_CURRENT_LIST_DIR}/${idf_target}/include/phy_init_data.h COMMAND ${CMAKE_C_COMPILER} -x c -c - -I ${CMAKE_CURRENT_LIST_DIR} -I ${CMAKE_CURRENT_LIST_DIR}/include -I ${build_dir} + -I ${esp_common_dir}/include -I ${CMAKE_CURRENT_LIST_DIR}/include -I ${build_dir}/config -o phy_init_data.obj - ${CMAKE_CURRENT_LIST_DIR}/phy_init_data.h + ${CMAKE_CURRENT_LIST_DIR}/${idf_target}/include/phy_init_data.h COMMAND ${CMAKE_OBJCOPY} -O binary phy_init_data.obj ${phy_init_data_bin} ) add_custom_target(phy_init_data ALL DEPENDS ${phy_init_data_bin}) diff --git a/components/esp_wifi/Makefile.projbuild b/components/esp_wifi/Makefile.projbuild new file mode 100644 index 0000000000..3cc445ce08 --- /dev/null +++ b/components/esp_wifi/Makefile.projbuild @@ -0,0 +1,32 @@ +ifdef CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION + +PHY_INIT_DATA_OBJ = $(BUILD_DIR_BASE)/phy_init_data.o +PHY_INIT_DATA_BIN = $(BUILD_DIR_BASE)/phy_init_data.bin + +# Command to flash PHY init data partition +PHY_INIT_DATA_FLASH_CMD = $(ESPTOOLPY_SERIAL) write_flash $(PHY_DATA_OFFSET) $(PHY_INIT_DATA_BIN) +ESPTOOL_ALL_FLASH_ARGS += $(PHY_DATA_OFFSET) $(PHY_INIT_DATA_BIN) + +ESP_WIFI_COMPONENT_PATH := $(COMPONENT_PATH) + +$(PHY_INIT_DATA_OBJ): $(ESP_WIFI_COMPONENT_PATH)/$(IDF_TARGET)/include/phy_init_data.h $(BUILD_DIR_BASE)/include/sdkconfig.h + $(summary) CC $(notdir $@) + printf "#include \"phy_init_data.h\"\n" | $(CC) -I $(BUILD_DIR_BASE)/include -I $(ESP_WIFI_COMPONENT_PATH)/../esp_common/include -I $(ESP_WIFI_COMPONENT_PATH)/include -I $(ESP_WIFI_COMPONENT_PATH)/$(IDF_TARGET)/include -c -o $@ -xc - + +$(PHY_INIT_DATA_BIN): $(PHY_INIT_DATA_OBJ) + $(summary) BIN $(notdir $@) + $(OBJCOPY) -O binary $< $@ + +phy_init_data: $(PHY_INIT_DATA_BIN) + +phy_init_data-flash: $(BUILD_DIR_BASE)/phy_init_data.bin + @echo "Flashing PHY init data..." + $(PHY_INIT_DATA_FLASH_CMD) + +phy_init_data-clean: + rm -f $(PHY_INIT_DATA_BIN) $(PHY_INIT_DATA_OBJ) + +all: phy_init_data +flash: phy_init_data + +endif # CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION diff --git a/components/esp_wifi/component.mk b/components/esp_wifi/component.mk index a72ad1ccd5..866a1fdd11 100644 --- a/components/esp_wifi/component.mk +++ b/components/esp_wifi/component.mk @@ -2,7 +2,7 @@ # Component Makefile # -COMPONENT_ADD_INCLUDEDIRS := include +COMPONENT_ADD_INCLUDEDIRS := include $(IDF_TARGET)/include COMPONENT_SRCDIRS := src ifndef CONFIG_ESP32_NO_BLOBS diff --git a/components/esp_wifi/include/phy_init_data.h b/components/esp_wifi/esp32/include/phy_init_data.h similarity index 100% rename from components/esp_wifi/include/phy_init_data.h rename to components/esp_wifi/esp32/include/phy_init_data.h diff --git a/docs/en/api-guides/RF_calibration.rst b/docs/en/api-guides/RF_calibration.rst index 6b8aec0bd7..696cc53239 100644 --- a/docs/en/api-guides/RF_calibration.rst +++ b/docs/en/api-guides/RF_calibration.rst @@ -48,7 +48,7 @@ PHY initialization data The PHY initialization data is used for RF calibration. There are two ways to get the PHY initialization data. -One is the default initialization data which is located in the header file :idf_file:`components/esp32/phy_init_data.h`. +One is the default initialization data which is located in the header file :idf_file:`components/esp_wifi/esp32/include/phy_init_data.h`. It is embedded into the application binary after compiling and then stored into read-only memory (DROM). To use the default initialization data, please go to ``menuconfig`` and disable :ref:`CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION`. diff --git a/tools/ci/test_build_system.sh b/tools/ci/test_build_system.sh index 9ffea7042c..667691de06 100755 --- a/tools/ci/test_build_system.sh +++ b/tools/ci/test_build_system.sh @@ -57,6 +57,7 @@ function run_tests() BOOTLOADER_BINS="bootloader/bootloader.elf bootloader/bootloader.bin" APP_BINS="app-template.elf app-template.bin" + PHY_INIT_BIN="phy_init_data.bin" print_status "Initial clean build" # if make fails here, everything fails @@ -283,6 +284,18 @@ function run_tests() rm sdkconfig sdkconfig.defaults make defconfig + print_status "can build with phy_init_data" + make clean > /dev/null + rm -f sdkconfig.defaults + rm -f sdkconfig + echo "CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION=y" >> sdkconfig.defaults + make defconfig > /dev/null + make || failure "Failed to build with PHY_INIT_DATA" + assert_built ${APP_BINS} ${BOOTLOADER_BINS} ${PHY_INIT_BIN} + rm sdkconfig + rm sdkconfig.defaults + make defconfig + print_status "Empty directory not treated as a component" mkdir -p components/esp32 make || failure "Failed to build with empty esp32 directory in components" diff --git a/tools/ci/test_build_system_cmake.sh b/tools/ci/test_build_system_cmake.sh index 3e35eaf9b8..80686322c7 100755 --- a/tools/ci/test_build_system_cmake.sh +++ b/tools/ci/test_build_system_cmake.sh @@ -58,6 +58,7 @@ function run_tests() BOOTLOADER_BINS="bootloader/bootloader.elf bootloader/bootloader.bin" APP_BINS="app-template.elf app-template.bin" PARTITION_BIN="partition_table/partition-table.bin" + PHY_INIT_BIN="phy_init_data.bin" BUILD_ARTIFACTS="project_description.json flasher_args.json config/kconfig_menus.json config/sdkconfig.json" IDF_COMPONENT_PREFIX="__idf" @@ -342,6 +343,18 @@ function run_tests() rm sdkconfig; rm sdkconfig.defaults; + print_status "can build with phy_init_data" + idf.py clean > /dev/null; + idf.py fullclean > /dev/null; + rm -f sdkconfig.defaults; + rm -f sdkconfig; + echo "CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION=y" >> sdkconfig.defaults; + idf.py reconfigure > /dev/null; + idf.py build || failure "Failed to build with PHY_INIT_DATA" + assert_built ${APP_BINS} ${BOOTLOADER_BINS} ${PARTITION_BIN} ${PHY_INIT_BIN} + rm sdkconfig; + rm sdkconfig.defaults; + print_status "Building a project with CMake library imported and PSRAM workaround, all files compile with workaround" # Test for libraries compiled within ESP-IDF rm -rf build @@ -440,7 +453,7 @@ endmenu\n" >> ${IDF_PATH}/Kconfig; print_status "If a component directory is added to COMPONENT_DIRS, its sibling directories are not added" clean_build_dir - mkdir -p mycomponents/mycomponent + mkdir -p mycomponents/mycomponent echo "idf_component_register()" > mycomponents/mycomponent/CMakeLists.txt # first test by adding single component directory to EXTRA_COMPONENT_DIRS mkdir -p mycomponents/esp32 From 304e7119ceb9a505fca98d975e982d1cb287528f Mon Sep 17 00:00:00 2001 From: He Yin Ling Date: Mon, 17 Jun 2019 09:48:04 +0800 Subject: [PATCH 068/486] ci: fix random unit test CI failure: we use `-` command to check if DUT reset pass. If we input `-` command during DUT bootup, DUT could only receive `\n` and print test cases. Print test cases could take long time and lead to reset check timeout. Now we will add delay after reset, and enlarge reset check timeout to solve this problem. --- tools/unit-test-app/unit_test.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tools/unit-test-app/unit_test.py b/tools/unit-test-app/unit_test.py index 330b32ff31..2f80db6f5e 100755 --- a/tools/unit-test-app/unit_test.py +++ b/tools/unit-test-app/unit_test.py @@ -63,8 +63,9 @@ MULTI_DEVICE_ID = 2 DEFAULT_TIMEOUT = 20 +DUT_DELAY_AFTER_RESET = 2 DUT_STARTUP_CHECK_RETRY_COUNT = 5 -TEST_HISTORY_CHECK_TIMEOUT = 1 +TEST_HISTORY_CHECK_TIMEOUT = 2 class TestCaseFailed(AssertionError): @@ -164,6 +165,11 @@ def reset_dut(dut): # now use input cmd `-` and check test history to check if DUT is bootup. # we'll retry this step for a few times, # in case `dut.reset` returns during DUT bootup (when DUT can't process any command). + # + # during bootup, DUT might only receive part of the first `-` command. + # If it only receive `\n`, then it will print all cases. It could take more than 5 seconds, reset check will fail. + # To solve this problem, we will add a delay between reset and input `-` command. And we'll also enlarge expect timeout. + time.sleep(DUT_DELAY_AFTER_RESET) for _ in range(DUT_STARTUP_CHECK_RETRY_COUNT): dut.write("-") try: From ce4de867d61c042fd08137088218ff54dcc4809d Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 24 May 2017 12:35:12 +1000 Subject: [PATCH 069/486] spi_flash: New low-level flash API --- components/esp32/cpu_start.c | 5 +- .../spi_flash/include/spi_flash_lowlevel.h | 265 +++++++++ .../include/spi_flash_lowlevel_driver.h | 176 ++++++ .../include/spi_flash_lowlevel_generic.h | 151 +++++ components/spi_flash/linker.lf | 7 +- components/spi_flash/spi_flash_lowlevel_api.c | 552 ++++++++++++++++++ .../spi_flash/spi_flash_lowlevel_driver.c | 34 ++ .../spi_flash/spi_flash_lowlevel_generic.c | 498 ++++++++++++++++ .../spi_flash/spi_flash_lowlevel_idf_app.c | 60 ++ .../spi_flash/spi_flash_lowlevel_issi.c | 74 +++ .../spi_flash/spi_flash_lowlevel_noos.c | 51 ++ components/spi_flash/test/test_low_level.c | 181 ++++++ 12 files changed, 2052 insertions(+), 2 deletions(-) create mode 100644 components/spi_flash/include/spi_flash_lowlevel.h create mode 100644 components/spi_flash/include/spi_flash_lowlevel_driver.h create mode 100644 components/spi_flash/include/spi_flash_lowlevel_generic.h create mode 100644 components/spi_flash/spi_flash_lowlevel_api.c create mode 100644 components/spi_flash/spi_flash_lowlevel_driver.c create mode 100644 components/spi_flash/spi_flash_lowlevel_generic.c create mode 100644 components/spi_flash/spi_flash_lowlevel_idf_app.c create mode 100644 components/spi_flash/spi_flash_lowlevel_issi.c create mode 100644 components/spi_flash/spi_flash_lowlevel_noos.c create mode 100644 components/spi_flash/test/test_low_level.c diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index e8e1100993..266c7f5e30 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -43,6 +43,7 @@ #include "sdkconfig.h" #include "esp_system.h" #include "esp_spi_flash.h" +#include "spi_flash_lowlevel.h" #include "nvs_flash.h" #include "esp_event.h" #include "esp_spi_flash.h" @@ -388,6 +389,9 @@ void start_cpu0_default(void) spi_flash_init(); /* init default OS-aware flash access critical section */ spi_flash_guard_set(&g_flash_guard_default_ops); + /* Todo the following needs to be properly integrated */ + esp_flash_low_level_app_init(); + esp_flash_init_default_chip(); #ifdef CONFIG_PM_ENABLE esp_pm_impl_init(); #ifdef CONFIG_PM_DFS_INIT_AUTO @@ -399,7 +403,6 @@ void start_cpu0_default(void) esp_pm_configure(&cfg); #endif //CONFIG_PM_DFS_INIT_AUTO #endif //CONFIG_PM_ENABLE - #if CONFIG_ESP32_ENABLE_COREDUMP esp_core_dump_init(); size_t core_data_sz = 0; diff --git a/components/spi_flash/include/spi_flash_lowlevel.h b/components/spi_flash/include/spi_flash_lowlevel.h new file mode 100644 index 0000000000..0b4ebf9a31 --- /dev/null +++ b/components/spi_flash/include/spi_flash_lowlevel.h @@ -0,0 +1,265 @@ +#pragma once +#include +#include + +#include "soc/spi_struct.h" + +struct esp_flash_driver; + +/** @brief Mode used for reading from SPI flash */ +typedef enum { + ESP_FLASH_QIO, ///< Both address & data transferred using quad I/O + ESP_FLASH_QOUT, ///< Data read using quad I/O + ESP_FLASH_DIO, ///< Both address & data transferred using dual I/O + ESP_FLASH_DOUT, ///< Data read using dual I/O + ESP_FLASH_FASTRD, ///< Data read using single I/O, no limit on speed + ESP_FLASH_SLOWRD, ///< Data read using single I/O, some limits on speed + + ESP_FLASH_READ_MODE_MAX, +} esp_flash_read_mode_t; + +/** @brief Configured SPI flash clock speed */ +typedef enum { + ESP_FLASH_80MHZ, + ESP_FLASH_40MHZ, + ESP_FLASH_26MHZ, + ESP_FLASH_20MHZ, + ESP_FLASH_SPEED_MAX, +} esp_flash_speed_t; + +/** @brief Structure for describing a region of flash */ +typedef struct { + uint32_t offset; + uint32_t size; +} esp_flash_region_t; + +// TODO this is copied from SPI driver, should be unified somehow +typedef struct { + int mosi_io_num; ///< GPIO pin for Master Out Slave In (=spi_d) signal + int miso_io_num; ///< GPIO pin for Master In Slave Out (=spi_q) signal + int sclk_io_num; ///< GPIO pin for Spi CLocK signal + int quadwp_io_num; ///< GPIO pin for WP (Write Protect) signal which is used as D2 in 4-bit communication modes, or -1 if not used. + int quadhd_io_num; ///< GPIO pin for HD (HolD) signal which is used as D3 in 4-bit communication modes, or -1 if not used. +} esp_flash_pin_cfg_t; + +/** @brief Structure to describe a SPI flash chip connected to the system. + + Structure must be passed to esp_flash_init() before use. +*/ +typedef struct { + spi_dev_t *spi; ///< Pointer to hardware SPI bus registers used for connection (SP1, SPI2 or SPI3). Set before initialisation. + esp_flash_speed_t speed; ///< Configured SPI flash clock speed. Set before initialisation. + esp_flash_read_mode_t read_mode; ///< Configured SPI flash read mode. Set before initialisation. + uint32_t size; ///< Size of SPI flash in bytes. If 0, size will be detected during initialisation. + const struct esp_flash_driver *drv; ///< Pointer to chip-model-specific "driver" structure. If NULL, will be detected during initialisatiopn. + const esp_flash_pin_cfg_t *pins; ///< Pin configuration for the chip + + void *driver_data; ///< Currently unused, allows drivers to store driver-implementation-specific data on initialisation +} esp_flash_chip_t; + +/** @brief Possible errors returned from SPI flash low-level API */ +typedef enum { + FLASH_OK = 0, ///< Success + FLASH_ERR_NOT_INITIALISED, ///< esp_flash_chip_t structure not correctly initialised by esp_flash_init(). + FLASH_ERR_INVALID_ARG, ///< A supplied argument was invalid. + FLASH_ERR_NOT_FOUND, ///< A requested value is not found. + FLASH_ERR_NO_RESPONSE, ///< Chip did not respond to the command, or timed out. + FLASH_ERR_UNSUPPORTED_HOST, ///< Requested operation isn't supported via this host SPI bus (chip->spi field). + FLASH_ERR_UNSUPPORTED_CHIP, ///< Requested operation isn't supported by this model of SPI flash chip. + FLASH_ERR_PROTECTED, ///< Write operation failed due to chip's write protection being enabled. +} esp_flash_err_t; + +/** @brief Initialise SPI flash chip interface. + * + * This function must be called before any other API functions are called for this chip. + * + * @note Only the spi, speed & read_mode fields of the chip structure need to be initialised. Other fields will be auto-detected + * if left set to zero or NULL. + * + * @note If the chip->drv pointer is NULL, chip driver will be autodetected based on its manufacturer & product IDs. See + * esp_flash_registered_flash_drivers pointer for details of this process. + * + * @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted. + * @return FLASH_OK on success, or a flash error code if initialisation fails. + */ +esp_flash_err_t esp_flash_init(esp_flash_chip_t *chip); + +/** @brief Read flash ID via the common "RDID" SPI flash command. + * + * @param chip Pointer to identify flash chip. Must have been successfully initialised via esp_flash_init() + * @param[out] Pointer to receive ID value. + * + * ID is a 24-bit value. Lower 16 bits of 'id' are the chip ID, upper 8 bits are the manufacturer ID. + * + * @return FLASH_OK on success, or a flash error code if operation failed. + */ +esp_flash_err_t esp_flash_read_id(const esp_flash_chip_t *chip, uint32_t *id); + +/** @brief Detect flash size based on flash ID. + * + * @param chip Pointer to identify flash chip. Must have been successfully initialised via esp_flash_init() + * @param[out] Detected size in bytes. + * + * @note Most flash chips use a common format for flash ID, where the lower 4 bits specify the size as a power of 2. If + * the manufacturer doesn't follow this convention, the size may be incorrectly detected. + * + * @return FLASH_OK on success, or a flash error code if operation failed. + */ +esp_flash_err_t esp_flash_detect_size(const esp_flash_chip_t *chip, uint32_t *size); + +/** @brief Erase flash chip contents + * + * @param chip Pointer to identify flash chip. Must have been successfully initialised via esp_flash_init() + * + * + * @return FLASH_OK on success, or a flash error code if operation failed. + */ +esp_flash_err_t esp_flash_erase_chip(const esp_flash_chip_t *chip); + +/** @brief Erase a region of the flash chip + * + * @param chip Pointer to identify flash chip. Must have been successfully initialised via esp_flash_init() + * @param start Address to start erasing flash. Must be sector aligned. + * @param len Length of region to erase. Must also be sector aligned. + * + * Sector size is specifyed in chip->drv->sector_size field (typically 4096 bytes.) FLASH_ERR_INVALID_ARG will be + * returned if the start & length are not a multiple of this size. + * + * Erase is performed using block (multi-sector) erases where possible (block size is specified in + * chip->drv->block_erase_size field, typically 65536 bytes). Remaining sectors are erased using individual sector erase + * commands. + * + * @return FLASH_OK on success, or a flash error code if operation failed. + */ +esp_flash_err_t esp_flash_erase_region(const esp_flash_chip_t *chip, uint32_t start, uint32_t len); + +/** @brief Read if the entire chip is write protected + * + * @param chip Pointer to identify flash chip. Must have been successfully initialised via esp_flash_init() + * @param[out] write_protected Pointer to boolean, set to the value of the write protect flag. + * + * @note A correct result for this flag depends on the SPI flash chip model and driver in use (via the 'chip->drv' + * field). + * + * @return FLASH_OK on success, or a flash error code if operation failed. + */ +esp_flash_err_t esp_flash_get_chip_write_protect(const esp_flash_chip_t *chip, bool *write_protected); + +/** @brief Set write protection for the SPI flash chip + * + * @param chip Pointer to identify flash chip. Must have been successfully initialised via esp_flash_init() + * @param write_protected Boolean value for the write protect flag + * + * @note Correct behaviour of this function depends on the SPI flash chip model and driver in use (via the 'chip->drv' + * field). + * + * If write protection is enabled, destructive operations will fail with FLASH_ERR_PROTECTED. + * + * Some SPI flash chips may require a power cycle before write protect status can be cleared. Otherwise, + * write protection can be removed via a follow-up call to this function. + * + * @return FLASH_OK on success, or a flash error code if operation failed. + */ +esp_flash_err_t esp_flash_set_chip_write_protect(const esp_flash_chip_t *chip, bool write_protect_chip); + + +/** @brief Read the list of individually protectable regions of this SPI flash chip. + * + * @param chip Pointer to identify flash chip. Must have been successfully initialised via esp_flash_init() + * @param regions[out] Pointer to receive a pointer to the array of protectable regions of the chip. + * @param[out] Pointer to an integer receiving the count of protectable regions in the array returned in 'regions'. + * + * @note Correct behaviour of this function depends on the SPI flash chip model and driver in use (via the 'chip->drv' + * field). + * + * @return FLASH_OK on success, or a flash error code if operation failed. + */ +esp_flash_err_t esp_flash_get_protectable_regions(const esp_flash_chip_t *chip, const esp_flash_region_t **regions, uint32_t *num_regions); + + +/** @brief Detect if a region of the SPI flash chip is protected + * + * @param chip Pointer to identify flash chip. Must have been successfully initialised via esp_flash_init() + * @param region Pointer to a struct describing a protected region. This must match one of the regions returned from esp_flash_get_protectable_regions(...). + * @param[out] protected Pointer to a flag which is set based on the protected status for this region. + * + * @note It is possible for this result to be false and write operations to still fail, if protection is enabled for the entire chip. + * + * @note Correct behaviour of this function depends on the SPI flash chip model and driver in use (via the 'chip->drv' + * field). + * + * @return FLASH_OK on success, or a flash error code if operation failed. + */ +esp_flash_err_t esp_flash_get_protected_region(const esp_flash_chip_t *chip, const esp_flash_region_t *region, bool *protected); + +/** @brief Update the protected status for a region of the SPI flash chip + * + * @param chip Pointer to identify flash chip. Must have been successfully initialised via esp_flash_init() + * @param region Pointer to a struct describing a protected region. This must match one of the regions returned from esp_flash_get_protectable_regions(...). + * @param protected Write protection flag to set. + * + * @note It is possible for the region protection flag to be cleared and write operations to still fail, if protection is enabled for the entire chip. + * + * @note Correct behaviour of this function depends on the SPI flash chip model and driver in use (via the 'chip->drv' + * field). + * + * @return FLASH_OK on success, or a flash error code if operation failed. + */ +esp_flash_err_t esp_flash_set_protected_region(const esp_flash_chip_t *chip, const esp_flash_region_t *region, bool protected); + +/** @brief Read data from the SPI flash chip + * + * @param chip Pointer to identify flash chip. Must have been successfully initialised via esp_flash_init() + * @param buffer Pointer to a buffer where the data will be read. + * @param address Address on flash to read from. Must be less than chip->size field. + * @param length Length (in bytes) of data to read. + * + * There are no alignment constraints on buffer, address or length. + * + * @note If on-chip flash encryption is used, this function returns raw (ie encrypted) data. Use the flash cache + * to transparently decrypt data. + * + * @return FLASH_OK on success, or a flash error code if operation failed. + */ +esp_flash_err_t esp_flash_read(const esp_flash_chip_t *chip, void *buffer, uint32_t address, uint32_t length); + +/** @brief Write data to the SPI flash chip + * + * @param chip Pointer to identify flash chip. Must have been successfully initialised via esp_flash_init() + * @param address Address on flash to write to. Must be previously erased (SPI NOR flash can only write bits 1->0). + * @param buffer Pointer to a buffer with the data to write. + * @param length Length (in bytes) of data to write. + * + * There are no alignment constraints on buffer, address or length. + * + * @return FLASH_OK on success, or a flash error code if operation failed. + */ +esp_flash_err_t esp_flash_write(const esp_flash_chip_t *chip, uint32_t address, const void *buffer, uint32_t length); + +/** @brief Encrypted and write data to the SPI flash chip using on-chip hardware flash encryption + * + * @param chip Pointer to identify flash chip. Must have been successfully initialised via esp_flash_init() + * @param address Address on flash to write to. 16 byte aligned. Must be previously erased (SPI NOR flash can only write bits 1->0). + * @param buffer Pointer to a buffer with the data to write. + * @param length Length (in bytes) of data to write. 16 byte aligned. + * + * @note Both address & length must be 16 byte aligned, as this is the encryption block size + * + * @return FLASH_OK on success, or a flash error code if operation failed. + */ +esp_flash_err_t esp_flash_write_encrypted(const esp_flash_chip_t *chip, uint32_t address, const void *buffer, uint32_t length); + +/** @brief Pointer to the "default" SPI flash chip, ie the main chip attached to the MCU. + + This chip is used if the 'chip' argument pass to esp_flash_xxx API functions is ever NULL. +*/ +extern const esp_flash_chip_t *esp_flash_default_chip; + +/** @brief Initialise the default SPI flash chip + * + * Called by OS startup code. You do not need to call this in your own applications. + */ +esp_flash_err_t esp_flash_init_default_chip(); + +/** Enable OS-level SPI flash protections in IDF */ +void esp_flash_low_level_app_init(); /* ROM TODO move this to IDF */ diff --git a/components/spi_flash/include/spi_flash_lowlevel_driver.h b/components/spi_flash/include/spi_flash_lowlevel_driver.h new file mode 100644 index 0000000000..0343cc7cb4 --- /dev/null +++ b/components/spi_flash/include/spi_flash_lowlevel_driver.h @@ -0,0 +1,176 @@ +#pragma once +#include "spi_flash_lowlevel.h" + +/** @brief SPI flash driver definition structure. + * + * The driver structure contains chip-specific pointers to functions to perform SPI flash operations, and some + * chip-specific numeric values. + * + * @note This is not a public API. Driver-specific functions are called from the public API (declared in + * spi_flash_lowlevel.h). They assume the caller has already validated arguments and enabled relevant protections + * (disabling flash cache, prevent concurrent SPI access, etc.) + * + * Do not call driver functions directly in other contexts. + * + * A generic driver and it's related operations are defined in spi_flash_lowlevel_generic.h which can be used as + * building blocks for written new/specific SPI flash chip drivers. + * + * @note All of these functions may be called with SPI flash cache disabled, so must only ever access IRAM/DRAM/ROM. + */ +typedef struct esp_flash_driver { + /* Probe to detect if a supported SPI flash chip is found. + * + * Attempts to configure 'chip' with these operations and probes for a matching SPI flash chip. + * + * Auto-detection of a SPI flash chip calls this function in turn on each registered driver (see esp_flash_registered_flash_drivers). + * + * ID - as read by spi_flash_generic_read_id() - is supplied so each probe + * function doesn't need to unnecessarily read ID, but probe is permitted + * to interrogate flash in any non-destructive way. + * + * It is permissible for the driver to modify the 'chip' structure if probing succeeds (specifically, to assign something to the + * driver_data pointer if that is useful for the driver.) + * + * @return FLASH_OK if probing was successful, an error otherwise. Driver may + * assume that returning FLASH_OK means it has claimed this chip. + */ + esp_flash_err_t (*probe)(esp_flash_chip_t *chip, uint32_t flash_id); + + /* Read SPI flash ID + * + * Sends RDID (or an equivalent command) to the device. + */ + esp_flash_err_t (*read_id)(const esp_flash_chip_t *chip, uint32_t *id); + + /* Detect SPI flash size + * + * Interrogate the chip to detect it's size. + */ + esp_flash_err_t (*detect_size)(const esp_flash_chip_t *chip, uint32_t *size); + + /* Erase the entire chip + + Caller has verified the chip is not write protected. + */ + esp_flash_err_t (*erase_chip)(const esp_flash_chip_t *chip); + + /* Erase a sector of the chip. Sector size is specified in the 'sector_size' field. + + sector_address is an offset in bytes. + + Caller has verified that this sector should be non-write-protected. + */ + esp_flash_err_t (*erase_sector)(const esp_flash_chip_t *chip, uint32_t sector_address); + + /* Erase a multi-sector block of the chip. Block size is specified in the 'block_erase_size' field. + sector_address is an offset in bytes. + + Caller has verified that this block should be non-write-protected. + */ + esp_flash_err_t (*erase_block)(const esp_flash_chip_t *chip, uint32_t block_address); + + uint32_t sector_size; /* Sector is minimum erase size */ + uint32_t block_erase_size; /* Optimal (fastest) block size for multi-sector erases on this chip */ + + /* Read the write protect status of the entire chip. */ + esp_flash_err_t (*get_chip_write_protect)(const esp_flash_chip_t *chip, bool *write_protected); + + /* Set the write protect status of the entire chip. */ + esp_flash_err_t (*set_chip_write_protect)(const esp_flash_chip_t *chip, bool write_protect_chip); + + /* Number of individually write protectable regions on this chip. Range 0-63. */ + uint8_t num_protectable_regions; + /* Pointer to an array describing each protectable region. Should have num_protectable_regions elements. */ + const esp_flash_region_t *protectable_regions; + /* Get a bitmask describing all protectable regions on the chip. Each bit represents one entry in the + protectable_regions array, ie bit (1<drv->set_read_mode(chip) in order to configure the chip's read mode correctly. + */ + esp_flash_err_t (*read)(const esp_flash_chip_t *chip, void *buffer, uint32_t address, uint32_t length); + + /* Write any amount of data to the chip. + */ + esp_flash_err_t (*write)(const esp_flash_chip_t *chip, uint32_t address, const void *buffer, uint32_t length); + + + /* Use the page program command to write data to the chip. + * + * This function is expected to be called by chip->drv->write (if the + * chip->drv->write implementation doesn't call it then it can be left as NULL.) + * + * - The length argument supplied to this function is at most 'page_size' bytes. + * + * - The region between 'address' and 'address + length' will not cross a page_size aligned boundary (the write + * implementation is expected to split such a write into two before calling page_program.) + */ + esp_flash_err_t (*page_program)(const esp_flash_chip_t *chip, uint32_t address, const void *buffer, uint32_t length); + + /* Page size as written by the page_program function. Usually 256 bytes. */ + uint32_t page_size; + + /* Perform an encrypted write to the chip, using internal flash encryption hardware. */ + esp_flash_err_t (*write_encrypted)(const esp_flash_chip_t *chip, uint32_t address, const void *buffer, uint32_t length); + + /* Set the write enable flag. This function is called internally by other functions in this structure, before a destructive + operation takes place. */ + esp_flash_err_t (*write_enable)(const esp_flash_chip_t *chip); + + /* Wait for the SPI flash chip to be idle (any write operation to be complete.) This function is both called from the higher-level API functions, and from other functions in this structure. + + timeout_ms should be a timeout (in milliseconds) before the function returns FLASH_ERR_NO_RESPONSE. This is useful to avoid hanging + if the chip is otherwise unresponsive (ie returns all 0xFF or similar.) + */ + esp_flash_err_t (*wait_idle)(const esp_flash_chip_t *chip, unsigned timeout_ms); + + /* Configure both the SPI host and the chip for the read mode specified in chip->read_mode. + * + * This function is called by the higher-level API before the 'read' function is called. + * + * Can return FLASH_ERR_UNSUPPORTED_HOST or FLASH_ERR_UNSUPPORTED_CHIP if the specified mode is unsupported. + */ + esp_flash_err_t (*set_read_mode)(const esp_flash_chip_t *chip); +} esp_flash_driver_t; + +/* Pointer to an array of pointers to all known drivers for flash chips. This array is used + by esp_flash_init() to detect the flash chip driver, if none is supplied by the caller. + + Array is terminated with a NULL pointer. + + This pointer can be overwritten with a pointer to a new array, to update the list of known flash chips. + */ +extern const esp_flash_driver_t **esp_flash_registered_flash_drivers; + +/* Provide OS-level integration hooks for accessing flash chips + inside a running OS */ +typedef struct +{ + /* Call this function before commencing any flash operation. + + Does not need to be recursive (ie is called at most once for each call to 'end'. + */ + esp_flash_err_t (*start)(const esp_flash_chip_t *chip); + + /* Call this function after completing any flash operation. */ + esp_flash_err_t (*end)(const esp_flash_chip_t *chip); + + /* Delay for at least 'ms' milliseconds. + + This function will be called in between 'start' and 'end'. + */ + esp_flash_err_t (*delay_ms)(unsigned ms); +} esp_flash_os_functions_t; + +/* The default (ie initial boot) no-OS ROM esp_flash_os_functions_t */ +const esp_flash_os_functions_t esp_flash_noos_functions; + +/* Pointer to the current esp_flash_os_functions_t structure in use. + Can be changed at runtime to reflect different running conditions. + */ +extern const esp_flash_os_functions_t *esp_flash_os_functions; diff --git a/components/spi_flash/include/spi_flash_lowlevel_generic.h b/components/spi_flash/include/spi_flash_lowlevel_generic.h new file mode 100644 index 0000000000..75866c721e --- /dev/null +++ b/components/spi_flash/include/spi_flash_lowlevel_generic.h @@ -0,0 +1,151 @@ +#pragma once +#include "spi_flash_lowlevel_driver.h" +/* The 'generic' SPI flash operations are a lowest common subset of SPI flash commands, that work across most chips. + * + * These can be used as-is vai the esp_flash_common_chip_driver driver, or they can be used as "base driver" functions when + * creating a new esp_flash_driver_t driver structure. + * + * + * All of the functions in this header are internal functions, not part of a public API. See spi_flash_lowlevel.h for + * the public API. + */ + +/* SPI commands (actual on-wire commands not SPI controller bitmasks) + Suitable for use with spi_flash_common_command static function. +*/ +#define CMD_RDID 0x9F +#define CMD_WRSR 0x01 +#define CMD_WRSR2 0x31 /* Not all SPI flash uses this command */ +#define CMD_WREN 0x06 +#define CMD_WRDI 0x04 +#define CMD_RDSR 0x05 +#define CMD_RDSR2 0x35 /* Not all SPI flash uses this command */ + +#define CMD_FASTRD_QIO 0xEB +#define CMD_FASTRD_QUAD 0x6B +#define CMD_FASTRD_DIO 0xBB +#define CMD_FASTRD_DUAL 0x3B +#define CMD_FASTRD 0x0B +#define CMD_READ 0x03 /* Speed limited */ + +#define CMD_CHIP_ERASE 0xC7 +#define CMD_SECTOR_ERASE 0x20 +#define CMD_LARGE_BLOCK_ERASE 0xD8 /* 64KB block erase command */ + +#define SR_WIP (1<<0) /* Status register write-in-progress bit */ +#define SR_WREN (1<<1) /* Status register write enable bit */ + + +/** @brief Execute a simple SPI flash command against the chip. + * + * @param chip Pointer to the chip to use. + * @param command Command to execute (an on-wire hex command.) + * @param mosi_data Up to 32 bits of MOSI data to write after the command. + * @param mosi_len Length of MOSI data (in bits.) + * @param miso_len Length of MISO data (in bits.) + * @return MISO value read back, if any (depending on miso_len value.) + */ +uint32_t spi_flash_common_command(const esp_flash_chip_t *chip, uint8_t command, uint32_t mosi_data, uint8_t mosi_len, uint8_t miso_len); + +/** @brief Returns true if the pin configuration for this chip uses the GPIO matrix for any signals. */ +bool spi_flash_uses_gpio_matrix(const esp_flash_chip_t *chip); + +/** @brief Generic probe function + * + * If chip->drv_read_id succeeds, the probe succeeds. + */ +esp_flash_err_t spi_flash_generic_probe(esp_flash_chip_t *chip, uint32_t flash_id); + +/** @brief Generic implementation of esp_flash_driver_t->read_id + * + * Uses the RDID command (9Fh) supported by most SPI flash chips. + * + * Results of all-zeroes or all-ones are considered failures (probably no chip attached.) + */ +esp_flash_err_t spi_flash_generic_read_id(const esp_flash_chip_t *chip, uint32_t *id); + +/** @brief Generic size detection function + * + * Tries to detect the size of chip by using the lower 4 bits of the chip->drv->read_id result = N, and assuming size is 2 ^ N. + */ +esp_flash_err_t spi_flash_generic_detect_size(const esp_flash_chip_t *chip, uint32_t *size); + +/** @brief Erase chip by using the generic erase chip (C7h) command. */ +esp_flash_err_t spi_flash_generic_erase_chip(const esp_flash_chip_t *chip); + +/** @brief Erase sector by using the generic sector erase (20h) command. */ +esp_flash_err_t spi_flash_generic_erase_sector(const esp_flash_chip_t *chip, uint32_t start_address); + +/** @brief Erase block by using the generic 64KB block erase (D8h) command */ +esp_flash_err_t spi_flash_generic_erase_block(const esp_flash_chip_t *chip, uint32_t start_address); + +/** @brief Read from flash by using a read command that matches the programmed read mode. */ +esp_flash_err_t spi_flash_generic_read(const esp_flash_chip_t *chip, void *buffer, uint32_t address, uint32_t length); + +/** @brief Perform a page program using the page program (02h) command. */ +esp_flash_err_t spi_flash_generic_page_program(const esp_flash_chip_t *chip, uint32_t address, const void *buffer, uint32_t length); + +/** @brief Perform a generic write. Split the write buffer into + one page operations, and call chip->drv->page-program() for each. +*/ +esp_flash_err_t spi_flash_generic_write(const esp_flash_chip_t *chip, uint32_t address, const void *buffer, uint32_t length); + +/** @brief Perform a write using on-chip flash encryption */ +esp_flash_err_t spi_flash_generic_write_encrypted(const esp_flash_chip_t *chip, uint32_t address, const void *buffer, uint32_t length); + +/** @brief Send the write enable (06h) command and verify the expected bit (1) in the status register is set. */ +esp_flash_err_t spi_flash_generic_write_enable(const esp_flash_chip_t *chip); + +/** @brief Wait for the SPI host hardware state machine to be idle. + + This isn't a flash driver operation, but it's called by spi_flash_generic_wait_idle() and may be useful when implementing alternative drivers. + + timeout_ms will be decremented if the function needs to wait until the host hardware is idle. +*/ +esp_flash_err_t spi_flash_generic_wait_host_idle(const esp_flash_chip_t *chip, uint32_t *timeout_ms); + +/** @brief Read flash status via the RDSR command (05h) and wait for bit 0 (write in progress bit) to be cleared. */ +esp_flash_err_t spi_flash_generic_wait_idle(const esp_flash_chip_t *chip, uint32_t timeout_ms); + +/** @brief Utility function to configure the SPI host hardware registers for the specified read mode. + + Called by spi_flash_generic_set_read_mode() but may also be useful + when implementing other SPI flash drivers. + + Note that calling this configures SPI host registers, so if running any other commands as part of set_read_mode() then these must be run before calling this function. + */ +esp_flash_err_t spi_flash_common_configure_host_read_mode(const esp_flash_chip_t *chip); + +/** @brief Utility function for set_read_mode driver function + * + * Most setting of read mode follows a common pattern, except for how to enable Quad I/O modes (QIO/QOUT). + * These use different commands to read/write the status register, and a different bit is set/cleared. + * + * This is a generic utility function to implement set_read_mode() for this pattern. Also configures host + * registers via spi_flash_common_configure_host_read_mode(). + * + * @param qe_rdsr_command SPI flash command to read status register + * @param qe_wrsr_command SPI flash command to write status register + * @param qe_sr_bitwidth Width of the status register these commands operate on, in bits. + * @param qe_sr_bit Bit mask for enabling Quad Enable functions on this chio. + */ +esp_flash_err_t spi_flash_common_set_read_mode(const esp_flash_chip_t *chip, uint8_t qe_rdsr_command, uint8_t qe_wrsr_command, uint8_t qe_sr_bitwidth, unsigned qe_sr_bit); + +/** @brief Set the specified SPI read mode. + * + * Includes setting SPI host hardware registers, but also setting quad enable status register bit if needed. + */ +esp_flash_err_t spi_flash_generic_set_read_mode(const esp_flash_chip_t *chip); + +/** @brief Returns true if chip is configured for Quad I/O or + Quad Fast Read */ +inline static bool spi_flash_is_quad_mode(const esp_flash_chip_t *chip) +{ + return chip->read_mode == ESP_FLASH_QIO || chip->read_mode == ESP_FLASH_QOUT; +} + +/* Generic SPI flash driver, uses all the above functions for its operations. In default autodetection, this is used as + a catchall if a more specific driver is not found. +*/ +extern const esp_flash_driver_t esp_flash_generic_chip_driver; + diff --git a/components/spi_flash/linker.lf b/components/spi_flash/linker.lf index fbbb64ea74..e466c85a2b 100644 --- a/components/spi_flash/linker.lf +++ b/components/spi_flash/linker.lf @@ -1,5 +1,10 @@ [mapping:spi_flash] archive: libspi_flash.a -entries: +entries: spi_flash_rom_patch (noflash_text) + spi_flash_lowlevel_api (noflash) + spi_flash_lowlevel_generic (noflash) + spi_flash_lowlevel_issi (noflash) + spi_flash_lowlevel_idf_app (noflash) + spi_flash_driver_hs (noflash) diff --git a/components/spi_flash/spi_flash_lowlevel_api.c b/components/spi_flash/spi_flash_lowlevel_api.c new file mode 100644 index 0000000000..0a3d6f29a5 --- /dev/null +++ b/components/spi_flash/spi_flash_lowlevel_api.c @@ -0,0 +1,552 @@ +// Copyright 2017 Espressif Systems (Shanghai) PTE LTD +// +// 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 +#include +#include + +#include "spi_flash_lowlevel_driver.h" +#include "spi_flash_lowlevel_generic.h" +#include "soc/spi_reg.h" + +#define MAX_WRITE_CHUNK 8192 /* write in chunks */ + +/* Static function to notify OS of a new SPI flash operation. + + If returns an error result, caller must abort. If returns FLASH_OK, caller must + call spiflash_end() before returning. +*/ +static esp_flash_err_t spiflash_start(const esp_flash_chip_t *chip) +{ + if (esp_flash_os_functions != NULL + && esp_flash_os_functions->start != NULL) { + esp_flash_err_t err = esp_flash_os_functions->start(chip); + if (err != FLASH_OK) { + return err; + } + } + return FLASH_OK; +} + +/* Static function to notify OS that SPI flash operation is complete. + */ +static esp_flash_err_t spiflash_end(const esp_flash_chip_t *chip, esp_flash_err_t err) +{ + if (esp_flash_os_functions != NULL + && esp_flash_os_functions->end != NULL) { + esp_flash_err_t end_err = esp_flash_os_functions->end(chip); + if (err == FLASH_OK) { + err = end_err; // Only return the 'end' error if we haven't already failed + } + } + return err; +} + +/* Return true if regions 'a' and 'b' overlap at all, based on their start offsets and lengths. */ +inline static bool regions_overlap(uint32_t a_start, uint32_t a_len,uint32_t b_start, uint32_t b_len); + +/* Top-level API functions, calling into driver functions via chip->drv */ + +static esp_flash_err_t detect_spi_flash_chip(esp_flash_chip_t *chip); + +esp_flash_err_t esp_flash_init(esp_flash_chip_t *chip) +{ + if (chip->spi == NULL) { + return FLASH_ERR_INVALID_ARG; + } + + // TODO: configure SPI host clock speed, pin configuration + + if (chip->drv == NULL) { + // Detect driver + esp_flash_err_t err = detect_spi_flash_chip(chip); + if (err != FLASH_OK) { + return err; + } + } + + esp_flash_err_t err = spiflash_start(chip); + if (err != FLASH_OK) { + return err; + } + + if (chip->size == 0) { + // Detect flash size + err = chip->drv->detect_size(chip, &chip->size); + } + + if (err == FLASH_OK) { + // Try to set the flash mode to whatever default mode was chosen + // (this isn't necessary at this point for functionality, but init will fail + // if this mode can't be set on this chip.) + err = chip->drv->set_read_mode(chip); + } + + // Done: all fields on 'chip' are initialised + return spiflash_end(chip, err); +} + +static esp_flash_err_t detect_spi_flash_chip(esp_flash_chip_t *chip) +{ + esp_flash_err_t err; + uint32_t flash_id; + int retries = 10; + do { + err = spiflash_start(chip); + if (err != FLASH_OK) { + return err; + } + + // Send generic RDID command twice, check for a matching result and retry in case we just powered on (inner + // function fails if it sees all-ones or all-zeroes.) + err = spi_flash_generic_read_id(chip, &flash_id); + + if (err == FLASH_OK) { // check we see the same ID twice, in case of transient power-on errors + uint32_t new_id; + err = spi_flash_generic_read_id(chip, &new_id); + if (err == FLASH_OK && (new_id != flash_id)) { + err = FLASH_ERR_NOT_INITIALISED; + } + } + + err = spiflash_end(chip, err); + } while (err != FLASH_OK && retries-- > 0); + + // Detect the chip and set the driver structure for it + const esp_flash_driver_t **drivers = esp_flash_registered_flash_drivers; + while (*drivers != NULL && chip->drv == NULL) { + chip->drv = *drivers; + + // start/end SPI operation each time, for multitasking + // and also so esp_flash_registered_flash_drivers can live in flash + err = spiflash_start(chip); + if (err != FLASH_OK) { + return err; + } + + if (chip->drv->probe(chip, flash_id) != FLASH_OK) { + chip->drv = NULL; + } + // if probe succeeded, chip->drv stays set + drivers++; + + err = spiflash_end(chip, err); + if (err != FLASH_OK) { + return err; + } + } + + return (chip->drv == NULL) ? FLASH_ERR_NOT_FOUND : FLASH_OK; +} + +// Convenience macro for beginning of all API functions, +// check that the 'chip' parameter is properly initialised +// and supports the operation in question +#define VERIFY_OP(OP) do { \ + if (chip == NULL) { \ + chip = esp_flash_default_chip; \ + } \ + if (chip == NULL || chip->drv == NULL) { \ + return FLASH_ERR_NOT_INITIALISED; \ + } \ + if (chip->drv->OP == NULL) { \ + return FLASH_ERR_UNSUPPORTED_CHIP; \ + } \ + } while (0) + +esp_flash_err_t esp_flash_read_id(const esp_flash_chip_t *chip, uint32_t *id) +{ + printf("chip %p esp_flash_default_chip %p\n", + chip, esp_flash_default_chip); + VERIFY_OP(read_id); + if (id == NULL) { + return FLASH_ERR_INVALID_ARG; + } + esp_flash_err_t err = spiflash_start(chip); + if (err != FLASH_OK) { + return err; + } + + err = chip->drv->read_id(chip, id); + + return spiflash_end(chip, err); +} + +esp_flash_err_t esp_flash_detect_size(const esp_flash_chip_t *chip, uint32_t *size) +{ + VERIFY_OP(detect_size); + if (size == NULL) { + return FLASH_ERR_INVALID_ARG; + } + *size = 0; + + esp_flash_err_t err = spiflash_start(chip); + if (err != FLASH_OK) { + return err; + } + + err = chip->drv->detect_size(chip, size); + + return spiflash_end(chip, err); +} + +esp_flash_err_t esp_flash_erase_chip(const esp_flash_chip_t *chip) +{ + VERIFY_OP(erase_chip); + bool write_protect = false; + + esp_flash_err_t err = spiflash_start(chip); + if (err != FLASH_OK) { + return err; + } + + err = esp_flash_get_chip_write_protect(chip, &write_protect); + + if (err == FLASH_OK && write_protect) { + err = FLASH_ERR_PROTECTED; + } + + if (err == FLASH_OK) { + err = chip->drv->erase_chip(chip); + } + + return spiflash_end(chip, err); +} + +esp_flash_err_t esp_flash_erase_region(const esp_flash_chip_t *chip, uint32_t start, uint32_t len) +{ + VERIFY_OP(erase_sector); + uint32_t block_erase_size = chip->drv->erase_block == NULL ? 0 : chip->drv->block_erase_size; + uint32_t sector_size = chip->drv->sector_size; + bool write_protect = false; + + if (sector_size == 0 || (block_erase_size % sector_size) != 0) { + return FLASH_ERR_NOT_INITIALISED; + } + if (start > chip->size || start + len > chip->size) { + return FLASH_ERR_INVALID_ARG; + } + if ((start % chip->drv->sector_size) != 0 || (len % chip->drv->sector_size) != 0) { + // Can only erase multiples of the sector size, starting at sector boundary + return FLASH_ERR_INVALID_ARG; + } + + esp_flash_err_t err = spiflash_start(chip); + if (err != FLASH_OK) { + return err; + } + + // Check for write protection on whole chip + if (chip->drv->get_chip_write_protect != NULL) { + err = chip->drv->get_chip_write_protect(chip, &write_protect); + if (err == FLASH_OK && write_protect) { + err = FLASH_ERR_PROTECTED; + } + } + + // Check for write protected regions overlapping the erase region + if (err == FLASH_OK && chip->drv->get_protected_regions != NULL && chip->drv->num_protectable_regions > 0) { + uint64_t protected = 0; + err = chip->drv->get_protected_regions(chip, &protected); + if (protected != 0) { + for (int i = 0; i < chip->drv->num_protectable_regions && err == FLASH_OK; i++) { + const esp_flash_region_t *region = &chip->drv->protectable_regions[i]; + if ((protected & (1LL << i)) + && regions_overlap(start, len, region->offset, region->size)) { + err = FLASH_ERR_PROTECTED; + } + } + } + } + + // Don't lock the SPI flash for the entire erase, as this may be very long + err = spiflash_end(chip, err); + + while (err == FLASH_OK && len >= sector_size) { + esp_flash_err_t err = spiflash_start(chip); + if (err != FLASH_OK) { + return err; + } + + // If possible erase an entire multi-sector block + if (block_erase_size > 0 && len >= block_erase_size && (start % block_erase_size) == 0) { + err = chip->drv->erase_block(chip, start); + start += block_erase_size; + len -= block_erase_size; + } + else { + // Otherwise erase individual sector only + err = chip->drv->erase_sector(chip, start); + start += sector_size; + len -= sector_size; + } + + err = spiflash_end(chip, err); + } + + return err; +} + +esp_flash_err_t esp_flash_get_chip_write_protect(const esp_flash_chip_t *chip, bool *write_protected) +{ + VERIFY_OP(get_chip_write_protect); + if (write_protected == NULL) { + return FLASH_ERR_INVALID_ARG; + } + + esp_flash_err_t err = spiflash_start(chip); + if (err != FLASH_OK) { + return err; + } + + err = chip->drv->get_chip_write_protect(chip, write_protected); + + return spiflash_end(chip, err); +} + +esp_flash_err_t esp_flash_set_chip_write_protect(const esp_flash_chip_t *chip, bool write_protect_chip) +{ + VERIFY_OP(set_chip_write_protect); + + esp_flash_err_t err = spiflash_start(chip); + if (err != FLASH_OK) { + return err; + } + + err = chip->drv->set_chip_write_protect(chip, write_protect_chip); + + return spiflash_end(chip, err); +} + +esp_flash_err_t esp_flash_get_protectable_regions(const esp_flash_chip_t *chip, const esp_flash_region_t **regions, uint32_t *num_regions) +{ + if(num_regions != NULL) { + *num_regions = 0; // In case caller doesn't check result + } + VERIFY_OP(get_protected_regions); + + if(regions == NULL || num_regions == NULL) { + return FLASH_ERR_INVALID_ARG; + } + + *num_regions = chip->drv->num_protectable_regions; + *regions = chip->drv->protectable_regions; + return FLASH_OK; +} + +static esp_flash_err_t find_region(const esp_flash_chip_t *chip, const esp_flash_region_t *region, uint8_t *index) +{ + if (region == NULL) { + return FLASH_ERR_INVALID_ARG; + } + + for(*index = 0; *index < chip->drv->num_protectable_regions; (*index)++) { + if (memcmp(&chip->drv->protectable_regions[*index], + region, sizeof(esp_flash_region_t)) == 0) { + return FLASH_OK; + } + } + + return FLASH_ERR_NOT_FOUND; +} + +esp_flash_err_t esp_flash_get_protected_region(const esp_flash_chip_t *chip, const esp_flash_region_t *region, bool *protected) +{ + VERIFY_OP(get_protected_regions); + + if (protected == NULL) { + return FLASH_ERR_INVALID_ARG; + } + + uint8_t index; + esp_flash_err_t err = find_region(chip, region, &index); + if (err != FLASH_OK) { + return err; + } + + uint64_t protection_mask = 0; + err = spiflash_start(chip); + if (err != FLASH_OK) { + return err; + } + + err = chip->drv->get_protected_regions(chip, &protection_mask); + if (err == FLASH_OK) { + *protected = protection_mask & (1LL << index); + } + + return spiflash_end(chip, err); +} + +esp_flash_err_t esp_flash_set_protected_region(const esp_flash_chip_t *chip, const esp_flash_region_t *region, bool protected) +{ + VERIFY_OP(set_protected_regions); + + uint8_t index; + esp_flash_err_t err = find_region(chip, region, &index); + if (err != FLASH_OK) { + return err; + } + + uint64_t protection_mask = 0; + err = spiflash_start(chip); + if (err != FLASH_OK) { + return err; + } + + err = chip->drv->get_protected_regions(chip, &protection_mask); + if (err == FLASH_OK) { + if (protected) { + protection_mask |= (1LL << index); + } else { + protection_mask &= ~(1LL << index); + } + err = chip->drv->set_protected_regions(chip, protection_mask); + } + + return spiflash_end(chip, err); +} + +esp_flash_err_t esp_flash_read(const esp_flash_chip_t *chip, void *buffer, uint32_t address, uint32_t length) +{ + VERIFY_OP(read); + if (buffer == NULL || address > chip->size || address+length > chip->size) { + return FLASH_ERR_INVALID_ARG; + } + + esp_flash_err_t err = spiflash_start(chip); + if (err != FLASH_OK) { + return err; + } + + if (err == FLASH_OK) { + err = chip->drv->set_read_mode(chip); + } + + if (err == FLASH_OK) { + err = chip->drv->read(chip, buffer, address, length); + } + + return spiflash_end(chip, err); +} + +esp_flash_err_t esp_flash_write(const esp_flash_chip_t *chip, uint32_t address, const void *buffer, uint32_t length) +{ + VERIFY_OP(write); + if (buffer == NULL || address > chip->size || address+length > chip->size) { + return FLASH_ERR_INVALID_ARG; + } + + /* If 'chip' is connected to the main SPI bus, we can only write directly from regions that are accessible + with cache disabled. */ +#ifdef ESP_PLATFORM + bool direct_write = ( chip->spi != &SPI1 + || ( (uintptr_t) address >= 0x3FFAE000 + && (uintptr_t) address < 0x40000000 ) ); +#else + bool direct_write = true; +#endif + + esp_flash_err_t err = FLASH_OK; + + /* Write output in chunks, either by buffering on stack or + by artificially cutting into MAX_WRITE_CHUNK parts (in an OS + environment, this prevents writing from causing interrupt or higher priority task + starvation.) */ + while(err == FLASH_OK && length > 0) { + uint32_t write_len; + const void *write_buf; + if (direct_write) { + write_len = MIN(length, MAX_WRITE_CHUNK); + write_buf = buffer; + } else { + uint32_t buf[8]; + write_len = MIN(length, sizeof(buf)); + memcpy(buf, buffer, write_len); + write_buf = buf; + } + + err = spiflash_start(chip); + if (err != FLASH_OK) { + return err; + } + + err = chip->drv->write(chip, address, write_buf, write_len); + + address += write_len; + buffer = (void *)((intptr_t)buffer + write_len); + length -= write_len; + + err = spiflash_end(chip, err); + } + return err; +} + +esp_flash_err_t esp_flash_write_encrypted(const esp_flash_chip_t *chip, uint32_t address, const void *buffer, uint32_t length) +{ + VERIFY_OP(write_encrypted); + if (chip->spi != 0) { + // Encrypted operations have to use SPI0 + return FLASH_ERR_UNSUPPORTED_HOST; + } + if (buffer == NULL || address > chip->size || address+length > chip->size) { + return FLASH_ERR_INVALID_ARG; + } + + esp_flash_err_t err = spiflash_start(chip); + if (err != FLASH_OK) { + return err; + } + + err = chip->drv->write_encrypted(chip, address, buffer, length); + + return spiflash_end(chip, err); +} + + +inline static bool regions_overlap(uint32_t a_start, uint32_t a_len,uint32_t b_start, uint32_t b_len) +{ + uint32_t a_end = a_start + a_len; + uint32_t b_end = b_start + b_len; + + return ((a_start >= b_start && a_start <= b_end) + || (a_end >= b_start && a_end <= b_end) + || (b_start >= a_start && b_start <= a_end) + || (b_end >= a_start && b_end <= a_end)); +} + +const esp_flash_chip_t *esp_flash_default_chip; + +static esp_flash_chip_t default_chip; + +esp_flash_err_t esp_flash_init_default_chip() +{ + default_chip.spi = &SPI1; + default_chip.read_mode = ESP_FLASH_FASTRD; // TODO: initialise properly + default_chip.speed = ESP_FLASH_20MHZ; // TODO: initialise properly + + // ROM TODO: account for non-standard default pins in efuse + + // ROM TODO: to account for chips which are slow to power on, maybe keep probing in a loop here + + esp_flash_err_t err = esp_flash_init(&default_chip); + if (err != FLASH_OK) { + return err; + } + + esp_flash_default_chip = &default_chip; + return FLASH_OK; +} + +const esp_flash_os_functions_t *esp_flash_os_functions = &esp_flash_noos_functions; diff --git a/components/spi_flash/spi_flash_lowlevel_driver.c b/components/spi_flash/spi_flash_lowlevel_driver.c new file mode 100644 index 0000000000..e901ef1e21 --- /dev/null +++ b/components/spi_flash/spi_flash_lowlevel_driver.c @@ -0,0 +1,34 @@ +// Copyright 2017 Espressif Systems (Shanghai) PTE LTD +// +// 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 "spi_flash_lowlevel_driver.h" + +extern const esp_flash_driver_t esp_flash_generic_chip_driver; +extern const esp_flash_driver_t esp_flash_issi_chip_driver; + +/* Default registered chip drivers. + + Note these are tested in order and first match is taken, so generic/catchall entries + should go last. + + Note that the esp_flash_registered_flash_ops pointer can be + changed to point to a different array of registered ops, if desired. +*/ +static const esp_flash_driver_t *default_registered_flash_drivers[] = { + &esp_flash_issi_chip_driver, + &esp_flash_generic_chip_driver, + NULL, +}; + +const esp_flash_driver_t **esp_flash_registered_flash_drivers = default_registered_flash_drivers; diff --git a/components/spi_flash/spi_flash_lowlevel_generic.c b/components/spi_flash/spi_flash_lowlevel_generic.c new file mode 100644 index 0000000000..aef068e30f --- /dev/null +++ b/components/spi_flash/spi_flash_lowlevel_generic.c @@ -0,0 +1,498 @@ +// Copyright 2017 Espressif Systems (Shanghai) PTE LTD +// +// 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 // For MIN/MAX +#include + +#include "spi_flash_lowlevel_driver.h" +#include "spi_flash_lowlevel_generic.h" + +#define SPI_FLASH_GENERIC_CHIP_ERASE_TIMEOUT 4000 +#define SPI_FLASH_GENERIC_SECTOR_ERASE_TIMEOUT 500 +#define SPI_FLASH_GENERIC_BLOCK_ERASE_TIMEOUT 1000 + +#define DEFAULT_IDLE_TIMEOUT 200 +#define DEFAULT_PAGE_PROGRAM_TIMEOUT 500 + +/* Hardware host-specific constants */ +#define MAX_WRITE_BYTES 32 +#define MAX_READ_BYTES 64 + +#define ADDRESS_MASK_24BIT 0xFFFFFF + +uint32_t spi_flash_common_command(const esp_flash_chip_t *chip, uint8_t command, uint32_t mosi_data, uint8_t mosi_len, uint8_t miso_len) +{ + typeof(chip->spi->user2) user2 = { + .usr_command_value = command, + .usr_command_bitlen = (8 -1), + }; + chip->spi->user2 = user2; + + typeof(chip->spi->user) user = { + .usr_miso = miso_len > 0, + .usr_mosi = mosi_len > 0, + .usr_dummy = 0, + .usr_command = 1, + }; + chip->spi->user = user; + + chip->spi->ctrl.val = 0; + chip->spi->miso_dlen.usr_miso_dbitlen = miso_len ? (miso_len - 1) : 0; + chip->spi->mosi_dlen.usr_mosi_dbitlen = mosi_len ? (mosi_len - 1) : 0; + + // TODO: there's a bug(?) here where if multiple bytes are written + // with each byte MSB-first (correct), but the bytes are + // written out LSB first... + // + // May be easier to just document this in the function interface... + chip->spi->data_buf[0] = mosi_data; + + if (spi_flash_uses_gpio_matrix(chip)) { + /* When flash pins are mapped via GPIO matrix, need a dummy cycle before reading via MISO */ + if (chip->speed == ESP_FLASH_80MHZ) { + chip->spi->user.usr_dummy = 1; + chip->spi->user1.usr_dummy_cyclelen = 1; + } else { + chip->spi->user.usr_dummy = 1; + chip->spi->user1.usr_dummy_cyclelen = 0; + } + } + + chip->spi->cmd.usr = 1; + while(chip->spi->cmd.usr != 0) + { } + + uint32_t miso = chip->spi->data_buf[0]; + + return miso; +} + +esp_flash_err_t spi_flash_generic_probe(esp_flash_chip_t *chip, uint32_t flash_id) +{ + // This is the catch-all probe function, claim the chip always if nothing + // else has claimed it yet. + return FLASH_OK; +} + +esp_flash_err_t spi_flash_generic_read_id(const esp_flash_chip_t *chip, uint32_t *id) +{ + uint32_t raw_flash_id = spi_flash_common_command(chip, CMD_RDID, 0, 0, 24); + if (raw_flash_id == 0xFFFFFF || raw_flash_id == 0) { + return FLASH_ERR_NO_RESPONSE; + } + + // Byte swap the flash id as it's usually written the other way around + uint8_t mfg_id = raw_flash_id & 0xFF; + uint16_t flash_id = (raw_flash_id >> 16) | (raw_flash_id & 0xFF00); + + *id = ((uint32_t)mfg_id << 16) | flash_id; + return FLASH_OK; +} + + +esp_flash_err_t spi_flash_generic_detect_size(const esp_flash_chip_t *chip, uint32_t *size) +{ + uint32_t id = 0; + *size = 0; + esp_flash_err_t err = chip->drv->read_id(chip, &id); + if (err != FLASH_OK) { + return err; + } + + /* Can't detect size unless the high byte of the product ID matches the same convention, which is usually 0x40 or + * 0xC0 or similar. */ + if ((id & 0x0F00) != 0) { + return FLASH_ERR_UNSUPPORTED_CHIP; + } + + *size = 1 << (id & 0xFF); + + return FLASH_OK; +} + + +esp_flash_err_t spi_flash_generic_erase_chip(const esp_flash_chip_t *chip) +{ + esp_flash_err_t err; + + err = chip->drv->write_enable(chip); + if (err == FLASH_OK) { + err = chip->drv->wait_idle(chip, DEFAULT_IDLE_TIMEOUT); + } + if (err == FLASH_OK) { + chip->spi->ctrl.val = 0; + chip->spi->cmd.flash_ce = 1; + while(chip->spi->cmd.val != 0) { } + err = chip->drv->wait_idle(chip, SPI_FLASH_GENERIC_CHIP_ERASE_TIMEOUT); + } + + return err; +} + +esp_flash_err_t spi_flash_generic_erase_sector(const esp_flash_chip_t *chip, uint32_t start_address) +{ + esp_flash_err_t err = chip->drv->write_enable(chip); + if (err == FLASH_OK) { + err = chip->drv->wait_idle(chip, DEFAULT_IDLE_TIMEOUT); + } + if (err == FLASH_OK) { + chip->spi->user1.usr_addr_bitlen = (24 - 1); + chip->spi->addr = start_address & ADDRESS_MASK_24BIT; + chip->spi->ctrl.val = 0; + chip->spi->cmd.flash_se = 1; + while(chip->spi->cmd.val != 0) { } + err = chip->drv->wait_idle(chip, SPI_FLASH_GENERIC_SECTOR_ERASE_TIMEOUT); + } + return err; +} + +esp_flash_err_t spi_flash_generic_erase_block(const esp_flash_chip_t *chip, uint32_t start_address) +{ + esp_flash_err_t err = chip->drv->write_enable(chip); + if (err == FLASH_OK) { + err = chip->drv->wait_idle(chip, DEFAULT_IDLE_TIMEOUT); + } + if (err == FLASH_OK) { + chip->spi->user1.usr_addr_bitlen = (24 - 1); + chip->spi->addr = start_address & ADDRESS_MASK_24BIT; + chip->spi->cmd.flash_be = 1; + while(chip->spi->cmd.val != 0) { } + err = chip->drv->wait_idle(chip, SPI_FLASH_GENERIC_BLOCK_ERASE_TIMEOUT); + } + return err; +} + +esp_flash_err_t spi_flash_generic_read(const esp_flash_chip_t *chip, void *buffer, uint32_t address, uint32_t length) +{ + esp_flash_err_t err = FLASH_OK; + + while (err == FLASH_OK && length > 0) { + uint32_t read_len = MIN(length, MAX_READ_BYTES); + chip->spi->miso_dlen.usr_miso_dbitlen = (read_len * 8) - 1; + chip->spi->addr = address << 8; + + chip->spi->cmd.usr = 1; + while(chip->spi->cmd.val != 0) {} + + if(((intptr_t)buffer % 4 == 0) && (read_len % 4 == 0)) { + // If everything is word-aligned, do a faster memcpy + xthal_memcpy(buffer, (void *)chip->spi->data_buf, read_len); + length -= read_len; + buffer = (void *)((intptr_t)buffer + read_len); + address += read_len; + } else { + // Otherwise, slow(er) path copies word by word + for (int i = 0; i < (read_len+3)/4; i++) { + int word_len = MIN(sizeof(uint32_t), length); + uint32_t word = chip->spi->data_buf[i]; + xthal_memcpy(buffer, &word, word_len); + length -= word_len; + buffer = (void *)((intptr_t)buffer + word_len); + address += word_len; + } + } + } + + + return err; +} + +esp_flash_err_t spi_flash_generic_page_program(const esp_flash_chip_t *chip, uint32_t address, const void *buffer, uint32_t length) +{ + esp_flash_err_t err; + + err = chip->drv->wait_idle(chip, DEFAULT_IDLE_TIMEOUT); + + if (err == FLASH_OK) { + err = chip->drv->write_enable(chip); + } + + if (err == FLASH_OK) { + // Perform the actual Page Program command + chip->spi->user.usr_dummy = 0; + chip->spi->user1.usr_addr_bitlen = (24 - 1); + chip->spi->addr = (address & ADDRESS_MASK_24BIT) | (length << 24); + + // Load data registers, word at a time + int num_words = (length+3) / 4; + for (int i = 0; i < num_words; i++) { + uint32_t word = 0; + uint32_t word_len = MIN(length, sizeof(word)); + xthal_memcpy(&word, buffer, word_len); + chip->spi->data_buf[i] = word; + length -= word_len; + buffer = (void *)((intptr_t)buffer + word_len); + } + + chip->spi->cmd.flash_pp = 1; + while (chip->spi->cmd.val != 0) { } + + err = chip->drv->wait_idle(chip, DEFAULT_PAGE_PROGRAM_TIMEOUT); + } + + + return err; +} + +esp_flash_err_t spi_flash_generic_write(const esp_flash_chip_t *chip, uint32_t address, const void *buffer, uint32_t length) +{ + esp_flash_err_t err = FLASH_OK; + const uint32_t page_size = chip->drv->page_size; + + while (err == FLASH_OK && length > 0) { + uint32_t page_len = MIN(MAX_WRITE_BYTES, MIN(page_size, length)); + if ((address + page_len) / page_size != address / page_size) { + // Most flash chips can't page write across a page boundary + page_len = page_size - (address % page_size); + } + err = chip->drv->page_program(chip, address, buffer, page_len); + address += page_len; + buffer = (void *)((intptr_t)buffer + page_len); + length -= page_len; + } + + + return err; +} + +esp_flash_err_t spi_flash_generic_write_encrypted(const esp_flash_chip_t *chip, uint32_t address, const void *buffer, uint32_t length) +{ + return FLASH_ERR_UNSUPPORTED_HOST; // TODO +} + +esp_flash_err_t spi_flash_generic_write_enable(const esp_flash_chip_t *chip) +{ + esp_flash_err_t err = FLASH_OK; + + err = chip->drv->wait_idle(chip, DEFAULT_IDLE_TIMEOUT); + + if (err == FLASH_OK) { + chip->spi->cmd.flash_wren = 1; + while(chip->spi->cmd.val != 0) { } + } + + uint8_t status = spi_flash_common_command(chip, CMD_RDSR, 0, 0, 8); + if ((status & SR_WREN) == 0) { + // WREN flag has not been set! + err = FLASH_ERR_NOT_FOUND; + } + + + return err; +} + +esp_flash_err_t spi_flash_generic_wait_host_idle(const esp_flash_chip_t *chip, uint32_t *timeout_ms) +{ + while(chip->spi->ext2.st != 0 && *timeout_ms > 0) { + if (*timeout_ms > 1) { + esp_flash_os_functions->delay_ms(1); + } + (*timeout_ms)--; + } + + // Not clear if this is necessary, or only necessary if + // chip->spi == SPI1. But probably doesn't hurt... + while(SPI0.ext2.st != 0 && *timeout_ms > 0) { + if (*timeout_ms > 1) { + esp_flash_os_functions->delay_ms(1); + } + (*timeout_ms)--; + } + + return (*timeout_ms > 0) ? FLASH_OK : FLASH_ERR_NO_RESPONSE; +} + +esp_flash_err_t spi_flash_generic_wait_idle(const esp_flash_chip_t *chip, uint32_t timeout_ms) +{ + timeout_ms++; // allow at least one pass before timeout, last one has no sleep cycle + + uint8_t status = 0; + while(timeout_ms > 0) { + + esp_flash_err_t err = spi_flash_generic_wait_host_idle(chip, &timeout_ms); + if (err != FLASH_OK) { + return err; + } + + status = spi_flash_common_command(chip, CMD_RDSR, 0, 0, 8); + if ((status & SR_WIP) == 0) { + break; // Write in progress is complete + } + if (timeout_ms > 1) { + esp_flash_os_functions->delay_ms(1); + } + timeout_ms--; + } + + return (timeout_ms > 0) ? FLASH_OK : FLASH_ERR_NO_RESPONSE; +} + +esp_flash_err_t spi_flash_common_configure_host_read_mode(const esp_flash_chip_t *chip) +{ + int dummy_cyclelen, addr_bitlen, read_command; + switch(chip->read_mode) { + case ESP_FLASH_QIO: + addr_bitlen = 32; + dummy_cyclelen = 4; // TODO check this works + read_command = CMD_FASTRD_QIO; + break; + case ESP_FLASH_QOUT: + addr_bitlen = 24; + dummy_cyclelen = 8; // TODO check this works + read_command = CMD_FASTRD_QUAD; + break; + case ESP_FLASH_DIO: + addr_bitlen = 32; + dummy_cyclelen = 0; + read_command = CMD_FASTRD_DIO; + break; + case ESP_FLASH_DOUT: + addr_bitlen = 24; + dummy_cyclelen = 8; + read_command = CMD_FASTRD_DUAL; + break; + case ESP_FLASH_FASTRD: + addr_bitlen = 24; + dummy_cyclelen = 8; + read_command = CMD_FASTRD; + break; + case ESP_FLASH_SLOWRD: + addr_bitlen = 24; + dummy_cyclelen = 0; + read_command = CMD_READ; + break; + default: + return FLASH_ERR_NOT_INITIALISED; + } + + // Add dummy cycles to compensate for GPIO matrix + // latency, if necessary... + if (spi_flash_uses_gpio_matrix(chip)) { + if (chip->speed == ESP_FLASH_80MHZ) { + dummy_cyclelen += 2; + } else if (chip->speed == ESP_FLASH_40MHZ) { + dummy_cyclelen += 1; + } + } + + chip->spi->user1.usr_dummy_cyclelen = (dummy_cyclelen - 1); + chip->spi->user1.usr_addr_bitlen = (addr_bitlen - 1); + chip->spi->user2.usr_command_value = read_command; + chip->spi->user2.usr_command_bitlen = (8 - 1); + + typeof (chip->spi->user) user = { + .usr_command = 1, + .usr_mosi = 0, + .usr_miso = 1, + .usr_dummy = (dummy_cyclelen > 0) ? 1 : 0, + .usr_addr = 1, + }; + chip->spi->user = user; + + typeof (chip->spi->ctrl) ctrl = { + .fread_qio = (chip->read_mode == ESP_FLASH_QIO), + .fread_quad = (chip->read_mode == ESP_FLASH_QOUT), + .fread_dio = (chip->read_mode == ESP_FLASH_DIO), + .fread_dual = (chip->read_mode == ESP_FLASH_DOUT), + .fastrd_mode = (chip->read_mode != ESP_FLASH_SLOWRD), + }; + chip->spi->ctrl = ctrl; + + return FLASH_OK; +} + +#include + +esp_flash_err_t spi_flash_common_set_read_mode(const esp_flash_chip_t *chip, uint8_t qe_rdsr_command, uint8_t qe_wrsr_command, uint8_t qe_sr_bitwidth, unsigned qe_sr_bit) +{ + if (spi_flash_is_quad_mode(chip)) { + // Ensure quad modes are enabled, using the Quad Enable parameters supplied. + unsigned sr = spi_flash_common_command(chip, qe_rdsr_command, 0, 0, qe_sr_bitwidth); + ets_printf("before 0x%x\n", sr); + if ((sr & qe_sr_bit) == 0) { + sr |= qe_sr_bit; + spi_flash_common_command(chip, qe_wrsr_command, sr, qe_sr_bitwidth, 0); + + /* Check the new QE bit has stayed set */ + sr = spi_flash_common_command(chip, qe_rdsr_command, 0, 0, qe_sr_bitwidth); + ets_printf("after 0x%x\n", sr); + if ((sr & qe_sr_bit) == 0) { + return FLASH_ERR_NO_RESPONSE; + } + } + } + + // Configure the host, and return + return spi_flash_common_configure_host_read_mode(chip); +} + +esp_flash_err_t spi_flash_generic_set_read_mode(const esp_flash_chip_t *chip) +{ + // On "generic" chips, this involves checking + // bit 1 (QE) of RDSR2 (35h) result + // (it works this way on GigaDevice & Fudan Micro chips, probably others...) + const uint8_t BIT_QE = 1<<1; + return spi_flash_common_set_read_mode(chip, CMD_RDSR2, CMD_WRSR2, 8, BIT_QE); +} + +bool spi_flash_uses_gpio_matrix(const esp_flash_chip_t *chip) +{ + if (chip->pins == NULL) { + return false; + } + if (chip->pins->mosi_io_num != -1 + || chip->pins->miso_io_num != -1 + || chip->pins->sclk_io_num != -1) { + return true; + } + if (spi_flash_is_quad_mode(chip)) { + if (chip->pins->quadwp_io_num != -1 + || chip->pins->quadhd_io_num != -1) { + return true; + } + } + return false; +} + +const esp_flash_driver_t esp_flash_generic_chip_driver = { + .probe = spi_flash_generic_probe, + .read_id = spi_flash_generic_read_id, + .detect_size = spi_flash_generic_detect_size, + .erase_chip = spi_flash_generic_erase_chip, + .erase_sector = spi_flash_generic_erase_sector, + .erase_block = spi_flash_generic_erase_block, + .sector_size = 4 * 1024, + .block_erase_size = 64 * 1024, + + // TODO: figure out if generic chip-wide protection bits exist across some manufacturers + .get_chip_write_protect = NULL, + .set_chip_write_protect = NULL, + + // Chip write protection regions do not appear to be standardised + // at all, this is implemented in chip-specific drivers only. + .num_protectable_regions = 0, + .protectable_regions = NULL, + .get_protected_regions = NULL, + .set_protected_regions = NULL, + + .read = spi_flash_generic_read, + .write = spi_flash_generic_write, + .page_program = spi_flash_generic_page_program, + .page_size = 256, + .write_encrypted = spi_flash_generic_write_encrypted, + + .write_enable = spi_flash_generic_write_enable, + .wait_idle = spi_flash_generic_wait_idle, + .set_read_mode = spi_flash_generic_set_read_mode, +}; diff --git a/components/spi_flash/spi_flash_lowlevel_idf_app.c b/components/spi_flash/spi_flash_lowlevel_idf_app.c new file mode 100644 index 0000000000..9b71a1e863 --- /dev/null +++ b/components/spi_flash/spi_flash_lowlevel_idf_app.c @@ -0,0 +1,60 @@ +// Copyright 2017 Espressif Systems (Shanghai) PTE LTD +// +// 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 "spi_flash_lowlevel_driver.h" + +#include "rom/ets_sys.h" +#include "esp_attr.h" +#include "esp_spi_flash.h" + +static esp_flash_err_t start(const esp_flash_chip_t *chip) +{ + if (chip->spi == &SPI1) { + g_flash_guard_default_ops.start(); + } + + // TODO figure out if we can coexist with the SPI master driver here, for other peripherals + + return FLASH_OK; +} +static esp_flash_err_t end(const esp_flash_chip_t *chip) +{ + if (chip->spi == &SPI1) { + g_flash_guard_default_ops.end(); + } + + // TODO figure out if we can coexist with the SPI master driver here, for other peripherals + + return FLASH_OK; +} + +static esp_flash_err_t delay_ms(unsigned ms) +{ + ets_delay_us(1000 * ms); + return FLASH_OK; +} + + +const esp_flash_os_functions_t default_os_functions = { + .start = start, + .end = end, + .delay_ms = delay_ms, +}; + +void esp_flash_low_level_app_init() +{ + esp_flash_os_functions = &default_os_functions; +} + + diff --git a/components/spi_flash/spi_flash_lowlevel_issi.c b/components/spi_flash/spi_flash_lowlevel_issi.c new file mode 100644 index 0000000000..f97ec5a0c0 --- /dev/null +++ b/components/spi_flash/spi_flash_lowlevel_issi.c @@ -0,0 +1,74 @@ +// Copyright 2017 Espressif Systems (Shanghai) PTE LTD +// +// 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 "spi_flash_lowlevel_driver.h" +#include "spi_flash_lowlevel_generic.h" + +/* Driver for ISSI flash chip, as used in ESP32 D2WD */ + +esp_flash_err_t issi_probe(esp_flash_chip_t *chip, uint32_t flash_id) +{ + /* Check manufacturer and product IDs match our desired masks */ + const uint8_t MFG_ID = 0x9D; + if (flash_id >> 16 != MFG_ID) { + return FLASH_ERR_NOT_FOUND; + } + + const uint16_t FLASH_ID_MASK = 0xCF00; + const uint16_t FLASH_ID_VALUE = 0x4000; + if ((flash_id & FLASH_ID_MASK) != FLASH_ID_VALUE) { + return FLASH_ERR_NOT_FOUND; + } + + return FLASH_OK; +} + +esp_flash_err_t issi_set_read_mode(const esp_flash_chip_t *chip) +{ + /* ISSI uses bit 6 of "basic" SR as Quad Enable */ + const uint8_t BIT_QE = 1<<6; + return spi_flash_common_set_read_mode(chip, CMD_RDSR, CMD_WRSR, 8, BIT_QE); +} + + +const esp_flash_driver_t esp_flash_issi_chip_driver = { + .probe = issi_probe, + .read_id = spi_flash_generic_read_id, + .detect_size = spi_flash_generic_detect_size, + .erase_chip = spi_flash_generic_erase_chip, + .erase_sector = spi_flash_generic_erase_sector, + .erase_block = spi_flash_generic_erase_block, + .sector_size = 4 * 1024, + .block_erase_size = 64 * 1024, + + // TODO: support get/set chip write protect for ISSI flash + .get_chip_write_protect = NULL, + .set_chip_write_protect = NULL, + + // TODO support protected regions on ISSI flash + .num_protectable_regions = 0, + .protectable_regions = NULL, + .get_protected_regions = NULL, + .set_protected_regions = NULL, + + .read = spi_flash_generic_read, + .write = spi_flash_generic_write, + .page_program = spi_flash_generic_page_program, + .page_size = 256, + .write_encrypted = spi_flash_generic_write_encrypted, + + .write_enable = spi_flash_generic_write_enable, + .wait_idle = spi_flash_generic_wait_idle, + .set_read_mode = issi_set_read_mode, +}; diff --git a/components/spi_flash/spi_flash_lowlevel_noos.c b/components/spi_flash/spi_flash_lowlevel_noos.c new file mode 100644 index 0000000000..3fb4b8c4bd --- /dev/null +++ b/components/spi_flash/spi_flash_lowlevel_noos.c @@ -0,0 +1,51 @@ +// Copyright 2017 Espressif Systems (Shanghai) PTE LTD +// +// 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 "spi_flash_lowlevel_driver.h" + +#include "rom/ets_sys.h" +#include "rom/cache.h" +#include "esp_attr.h" +#include "esp_spi_flash.h" + +static esp_flash_err_t start(const esp_flash_chip_t *chip) +{ + if (chip->spi == &SPI1) { + Cache_Read_Disable(0); + Cache_Read_Disable(1); + } + return FLASH_OK; +} +static esp_flash_err_t end(const esp_flash_chip_t *chip) +{ + if (chip->spi == &SPI1) { + Cache_Flush(0); + Cache_Flush(1); + Cache_Read_Enable(0); + Cache_Read_Enable(1); + } + return FLASH_OK; +} + +static esp_flash_err_t delay_ms(unsigned ms) +{ + ets_delay_us(1000 * ms); + return FLASH_OK; +} + +const esp_flash_os_functions_t esp_flash_noos_functions = { + .start = start, + .end = end, + .delay_ms = delay_ms, +}; diff --git a/components/spi_flash/test/test_low_level.c b/components/spi_flash/test/test_low_level.c new file mode 100644 index 0000000000..da77f892fb --- /dev/null +++ b/components/spi_flash/test/test_low_level.c @@ -0,0 +1,181 @@ +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +static uint8_t sector_buf[4096]; + +TEST_CASE("SPI flash metadata functions", "[spi_flash_ll]") +{ + uint32_t id, size; + + TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_read_id(NULL, &id) ); + + TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_detect_size(NULL, &size) ); + + printf("Flash ID %08x detected size %d bytes\n", id, size); +} + +static uint32_t erase_test_region(int num_sectors) +{ + const esp_partition_t *part = get_test_data_partition(); + uint32_t offs = part->address; + + /* chip should be initialised */ + TEST_ASSERT(esp_flash_default_chip != NULL + && esp_flash_default_chip->drv != NULL); + + TEST_ASSERT(num_sectors * 4096 <= part->size); + + bzero(sector_buf, sizeof(sector_buf)); + + printf("Erase @ 0x%x...\n", offs); + TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_erase_region(NULL, offs, num_sectors * 4096) ); + + printf("Verify erased...\n"); + TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_read(NULL, sector_buf, offs, sizeof(sector_buf)) ); + + printf("Buffer starts 0x%02x 0x%02x 0x%02x 0x%02x\n", sector_buf[0], sector_buf[1], sector_buf[2], sector_buf[3]); + for (int i = 0; i < sizeof(sector_buf); i++) { + TEST_ASSERT_EQUAL_HEX8(0xFF, sector_buf[i]); + } + + return offs; +} + +TEST_CASE("SPI flash simple read/write", "[spi_flash_ll]") +{ + uint32_t offs = erase_test_region(1); + + for (int i =0 ; i < sizeof(sector_buf); i++) { + sector_buf[i] = i & 0xFF; + } + + printf("Write...\n"); + TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_write(NULL, offs, sector_buf, sizeof(sector_buf)) ); + + bzero(sector_buf, sizeof(sector_buf)); + + printf("Read back...\n"); + TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_read(NULL, sector_buf, offs, sizeof(sector_buf)) ); + + printf("Buffer starts 0x%02x 0x%02x 0x%02x 0x%02x\n", sector_buf[0], sector_buf[1], sector_buf[2], sector_buf[3]); + + for (int i = 0; i < sizeof(sector_buf); i++) { + TEST_ASSERT_EQUAL_HEX8(i & 0xFF, sector_buf[i]); + } +} + +TEST_CASE("SPI flash unaligned read/write", "[spi_flash_ll]") +{ + uint32_t offs = erase_test_region(2); + + const char *msg = "i am a message"; + TEST_ASSERT(strlen(msg)+1 % 4 != 0); + TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_write(NULL, offs+1, msg, strlen(msg)+1) ); + + char buf[strlen(msg) + 1]; + + memset(buf, 0xEE, sizeof(buf)); + + TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_read(NULL, buf, offs+1, strlen(msg)+1) ); + TEST_ASSERT_EQUAL_STRING_LEN(msg, buf, strlen(msg)); + TEST_ASSERT(memcmp(buf, msg, strlen(msg)+1) == 0); +} + + +TEST_CASE("SPI flash single byte reads/writes", "[spi_flash_ll]") +{ + uint32_t offs = erase_test_region(2); + + for (unsigned v = 0; v < 512; v++) { + TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_write(NULL, offs+v, &v, 1) ); + } + + for (unsigned v = 0; v < 512; v++) { + uint8_t readback; + TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_read(NULL, &readback, offs+v, 1) ); + TEST_ASSERT_EQUAL_HEX8(v, readback); + } +} + +/* this test is notable because it generates a lot of unaligned reads/writes, + and also reads/writes across both a sector boundary & many page boundaries. +*/ +TEST_CASE("SPI flash three byte reads/writes", "[spi_flash_ll]") +{ + uint32_t offs = erase_test_region(2); + + for (uint32_t v = 0; v < 2000; v++) { + TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_write(NULL, offs+3*v, &v, 3) ); + } + + for (uint32_t v = 0; v < 2000; v++) { + uint32_t readback; + TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_read(NULL, &readback, offs+3*v, 3) ); + TEST_ASSERT_EQUAL_HEX32(v & 0xFFFFFF, readback & 0xFFFFFF); + } +} + +TEST_CASE("SPI flash erase large region", "[spi_flash_ll]") +{ + const esp_partition_t *part = get_test_data_partition(); + + /* Write some noise at the start and the end of the region */ + const char *ohai = "OHAI"; + uint32_t readback; + TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_write(NULL, part->address, ohai, 5)); + TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_write(NULL, part->address + part->size - 5, ohai, 5)); + + /* sanity check what we just wrote */ + TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_read(NULL, &readback, part->address + part->size - 5, 4)); + TEST_ASSERT_EQUAL_HEX32(*((const uint32_t*)ohai), readback); + + /* Erase whole region */ + TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_erase_region(NULL, part->address, part->size)); + + /* ensure both areas we wrote are now all-FFs */ + TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_read(NULL, &readback, part->address, 4)); + TEST_ASSERT_EQUAL_HEX32(0xFFFFFFFF, readback); + + TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_read(NULL, &readback, part->address + part->size - 5, 4)); + TEST_ASSERT_EQUAL_HEX32(0xFFFFFFFF, readback); +} + +TEST_CASE("SPI flash test reading with all speed/mode permutations", "[spi_flash_ll]") +{ + /* Note: this only works if the SPI flash chip supports all these modes & speeds */ + + uint32_t offs = erase_test_region(1); + + /* Write some test data */ + const char *message = "This is some test data."; + TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_write(NULL, offs, message, strlen(message)+1) ); + + // Start by copying the default chip to a structure we can tweak + esp_flash_chip_t chip = *esp_flash_default_chip; + + for (chip.read_mode = 0; + chip.read_mode != ESP_FLASH_READ_MODE_MAX; + chip.read_mode++) { + for (chip.speed = 0; + chip.speed != ESP_FLASH_SPEED_MAX; + chip.speed++) { + printf("mode %d speed %d\n", chip.read_mode, chip.speed); + + TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_init(&chip) ); + + char *buf[strlen(message)+1]; + memset(buf, 0xFF, sizeof(buf)); + TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_read(&chip, buf, offs, sizeof(buf)) ); + TEST_ASSERT_EQUAL_STRING_LEN(message, buf, strlen(message)); + } + } +} From 1036a091fe6876471ba4efd69cecd19daf974d17 Mon Sep 17 00:00:00 2001 From: "Michael (XIAO Xufeng)" Date: Tue, 8 Jan 2019 18:29:25 +0800 Subject: [PATCH 070/486] spi_flash: support working on differnt buses and frequency --- .gitlab-ci.yml | 6 + .../include/esp_flash_partitions.h | 11 + components/esp32/cpu_start.c | 10 +- components/esp_common/include/esp_err.h | 1 + .../test_fatfs_host/sdkconfig/sdkconfig.h | 2 + components/soc/CMakeLists.txt | 2 + .../soc/esp32/include/hal/spi_flash_ll.h | 370 +++++++++ components/soc/esp32/include/soc/spi_pins.h | 10 +- components/soc/include/hal/esp_flash_err.h | 39 + components/soc/include/hal/spi_flash_hal.h | 243 ++++++ .../soc/include/hal/spi_flash_host_drv.h | 112 +++ components/soc/linker.lf | 4 +- components/soc/src/hal/spi_flash_hal.c | 74 ++ components/soc/src/hal/spi_flash_hal_iram.c | 144 ++++ components/spi_flash/CMakeLists.txt | 13 +- components/spi_flash/Kconfig | 17 + components/spi_flash/README.rst | 100 ++- components/spi_flash/README_legacy.rst | 127 +++ components/spi_flash/component.mk | 1 + components/spi_flash/esp_flash_api.c | 747 ++++++++++++++++++ components/spi_flash/flash_ops.c | 32 +- .../{spi_flash_lowlevel.h => esp_flash.h} | 207 ++--- components/spi_flash/include/esp_spi_flash.h | 3 +- .../spi_flash/include/memspi_host_driver.h | 111 +++ ...level_driver.h => spi_flash_chip_driver.h} | 121 ++- .../include/spi_flash_chip_generic.h | 275 +++++++ .../spi_flash/include/spi_flash_chip_issi.h | 27 + .../include/spi_flash_lowlevel_generic.h | 151 ---- components/spi_flash/linker.lf | 8 +- components/spi_flash/memspi_host_driver.c | 101 +++ components/spi_flash/partition.c | 17 + .../private_include/spi_flash_defs.h | 44 ++ components/spi_flash/spi_flash_chip_drivers.c | 38 + components/spi_flash/spi_flash_chip_generic.c | 405 ++++++++++ ..._lowlevel_issi.c => spi_flash_chip_issi.c} | 53 +- components/spi_flash/spi_flash_lowlevel_api.c | 552 ------------- .../spi_flash/spi_flash_lowlevel_driver.c | 34 - .../spi_flash/spi_flash_lowlevel_generic.c | 498 ------------ .../spi_flash/spi_flash_lowlevel_idf_app.c | 60 -- components/spi_flash/spi_flash_os_func_app.c | 125 +++ ...wlevel_noos.c => spi_flash_os_func_noos.c} | 41 +- .../spi_flash/test/test_cache_disabled.c | 2 +- components/spi_flash/test/test_esp_flash.c | 549 +++++++++++++ .../spi_flash/test/test_large_flash_writes.c | 4 +- components/spi_flash/test/test_low_level.c | 181 ----- components/spi_flash/test/test_mmap.c | 4 +- .../spi_flash/test/test_out_of_bounds_write.c | 4 +- components/spi_flash/test/test_partitions.c | 2 +- components/spi_flash/test/test_read_write.c | 10 +- components/spi_flash/test/test_spi_flash.c | 4 +- .../test_spiffs_host/sdkconfig/sdkconfig.h | 2 + .../test_wl_host/sdkconfig/sdkconfig.h | 3 + 52 files changed, 3974 insertions(+), 1727 deletions(-) create mode 100644 components/soc/esp32/include/hal/spi_flash_ll.h create mode 100644 components/soc/include/hal/esp_flash_err.h create mode 100644 components/soc/include/hal/spi_flash_hal.h create mode 100644 components/soc/include/hal/spi_flash_host_drv.h create mode 100644 components/soc/src/hal/spi_flash_hal.c create mode 100644 components/soc/src/hal/spi_flash_hal_iram.c create mode 100644 components/spi_flash/README_legacy.rst create mode 100644 components/spi_flash/esp_flash_api.c rename components/spi_flash/include/{spi_flash_lowlevel.h => esp_flash.h} (54%) create mode 100644 components/spi_flash/include/memspi_host_driver.h rename components/spi_flash/include/{spi_flash_lowlevel_driver.h => spi_flash_chip_driver.h} (55%) create mode 100644 components/spi_flash/include/spi_flash_chip_generic.h create mode 100644 components/spi_flash/include/spi_flash_chip_issi.h delete mode 100644 components/spi_flash/include/spi_flash_lowlevel_generic.h create mode 100644 components/spi_flash/memspi_host_driver.c create mode 100644 components/spi_flash/private_include/spi_flash_defs.h create mode 100644 components/spi_flash/spi_flash_chip_drivers.c create mode 100644 components/spi_flash/spi_flash_chip_generic.c rename components/spi_flash/{spi_flash_lowlevel_issi.c => spi_flash_chip_issi.c} (53%) delete mode 100644 components/spi_flash/spi_flash_lowlevel_api.c delete mode 100644 components/spi_flash/spi_flash_lowlevel_driver.c delete mode 100644 components/spi_flash/spi_flash_lowlevel_generic.c delete mode 100644 components/spi_flash/spi_flash_lowlevel_idf_app.c create mode 100644 components/spi_flash/spi_flash_os_func_app.c rename components/spi_flash/{spi_flash_lowlevel_noos.c => spi_flash_os_func_noos.c} (54%) create mode 100644 components/spi_flash/test/test_esp_flash.c delete mode 100644 components/spi_flash/test/test_low_level.c diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e8c830caa6..b771bd3011 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1584,6 +1584,12 @@ UT_001_43: - ESP32_IDF - UT_T1_1 +UT_001_44: + <<: *unit_test_template + tags: + - ESP32_IDF + - UT_T1_1 + UT_002_01: <<: *unit_test_template tags: diff --git a/components/bootloader_support/include/esp_flash_partitions.h b/components/bootloader_support/include/esp_flash_partitions.h index e5305d836d..e652cc300c 100644 --- a/components/bootloader_support/include/esp_flash_partitions.h +++ b/components/bootloader_support/include/esp_flash_partitions.h @@ -103,6 +103,17 @@ inline static __attribute__((deprecated)) esp_err_t esp_partition_table_basic_ve { return esp_partition_table_verify(partition_table, log_errors, num_partitions); } + +/** + * Check whether the region on the main flash is safe to write. + * + * @param addr Start address of the region + * @param size Size of the region + * + * @return true if the region is safe to write, otherwise false. + */ +bool esp_partition_main_flash_region_safe(size_t addr, size_t size); + #ifdef __cplusplus } #endif diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index 266c7f5e30..3a782f2985 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -43,7 +43,7 @@ #include "sdkconfig.h" #include "esp_system.h" #include "esp_spi_flash.h" -#include "spi_flash_lowlevel.h" +#include "esp_flash.h" #include "nvs_flash.h" #include "esp_event.h" #include "esp_spi_flash.h" @@ -389,9 +389,10 @@ void start_cpu0_default(void) spi_flash_init(); /* init default OS-aware flash access critical section */ spi_flash_guard_set(&g_flash_guard_default_ops); - /* Todo the following needs to be properly integrated */ - esp_flash_low_level_app_init(); - esp_flash_init_default_chip(); + + esp_flash_app_init(); + esp_err_t flash_ret = esp_flash_init_default_chip(); + assert(flash_ret == ESP_OK); #ifdef CONFIG_PM_ENABLE esp_pm_impl_init(); #ifdef CONFIG_PM_DFS_INIT_AUTO @@ -403,6 +404,7 @@ void start_cpu0_default(void) esp_pm_configure(&cfg); #endif //CONFIG_PM_DFS_INIT_AUTO #endif //CONFIG_PM_ENABLE + #if CONFIG_ESP32_ENABLE_COREDUMP esp_core_dump_init(); size_t core_data_sz = 0; diff --git a/components/esp_common/include/esp_err.h b/components/esp_common/include/esp_err.h index c46ae38372..105723976d 100644 --- a/components/esp_common/include/esp_err.h +++ b/components/esp_common/include/esp_err.h @@ -41,6 +41,7 @@ typedef int32_t esp_err_t; #define ESP_ERR_WIFI_BASE 0x3000 /*!< Starting number of WiFi error codes */ #define ESP_ERR_MESH_BASE 0x4000 /*!< Starting number of MESH error codes */ +#define ESP_ERR_FLASH_BASE 0x6000 /*!< Starting number of flash error codes */ /** * @brief Returns string for esp_err_t error codes diff --git a/components/fatfs/test_fatfs_host/sdkconfig/sdkconfig.h b/components/fatfs/test_fatfs_host/sdkconfig/sdkconfig.h index 45ca4b7061..05ff65a5cc 100644 --- a/components/fatfs/test_fatfs_host/sdkconfig/sdkconfig.h +++ b/components/fatfs/test_fatfs_host/sdkconfig/sdkconfig.h @@ -4,3 +4,5 @@ #define CONFIG_LOG_DEFAULT_LEVEL 3 #define CONFIG_PARTITION_TABLE_OFFSET 0x8000 #define CONFIG_ESPTOOLPY_FLASHSIZE "8MB" +//currently use the legacy implementation, since the stubs for new HAL are not done yet +#define CONFIG_SPI_FLASH_USE_LEGACY_IMPL diff --git a/components/soc/CMakeLists.txt b/components/soc/CMakeLists.txt index 3b5d84ebc6..572fb38f68 100644 --- a/components/soc/CMakeLists.txt +++ b/components/soc/CMakeLists.txt @@ -16,6 +16,8 @@ list(APPEND COMPONENT_SRCS "src/memory_layout_utils.c" "src/hal/spi_slave_hal.c" "src/hal/spi_slave_hal_iram.c" "src/soc_include_legacy_warn.c" + "src/hal/spi_flash_hal.c" + "src/hal/spi_flash_hal_iram.c" ) set(COMPONENT_ADD_LDFRAGMENTS linker.lf) diff --git a/components/soc/esp32/include/hal/spi_flash_ll.h b/components/soc/esp32/include/hal/spi_flash_ll.h new file mode 100644 index 0000000000..f9adbd5f1b --- /dev/null +++ b/components/soc/esp32/include/hal/spi_flash_ll.h @@ -0,0 +1,370 @@ +// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +/******************************************************************************* + * NOTICE + * The ll is not public api, don't use in application code. + * See readme.md in soc/include/hal/readme.md + ******************************************************************************/ + +// The Lowlevel layer for SPI Flash + +#pragma once + +#include +#include "soc/spi_periph.h" +#include // For MIN/MAX +#include +#include + + +//Supported clock register values +#define SPI_FLASH_LL_CLKREG_VAL_5MHZ ((spi_flash_ll_clock_reg_t){.val=0x0000F1CF}) ///< Clock set to 5 MHz +#define SPI_FLASH_LL_CLKREG_VAL_10MHZ ((spi_flash_ll_clock_reg_t){.val=0x000070C7}) ///< Clock set to 10 MHz +#define SPI_FLASH_LL_CLKREG_VAL_20MHZ ((spi_flash_ll_clock_reg_t){.val=0x00003043}) ///< Clock set to 20 MHz +#define SPI_FLASH_LL_CLKREG_VAL_26MHZ ((spi_flash_ll_clock_reg_t){.val=0x00002002}) ///< Clock set to 26 MHz +#define SPI_FLASH_LL_CLKREG_VAL_40MHZ ((spi_flash_ll_clock_reg_t){.val=0x00001001}) ///< Clock set to 40 MHz +#define SPI_FLASH_LL_CLKREG_VAL_80MHZ ((spi_flash_ll_clock_reg_t){.val=0x80000000}) ///< Clock set to 80 MHz + +/// Get the start address of SPI peripheral registers by the host ID +#define spi_flash_ll_get_hw(n) ((n)==0||(n)==1? &SPI1:((n)==2?&SPI2:((n)==3?&SPI3:({abort();(spi_dev_t*)0;})))) + +///Slowest io mode supported by ESP32, currently SlowRd +#define SPI_FLASH_READ_MODE_MIN SPI_FLASH_SLOWRD + +/** @brief Mode used for reading from SPI flash */ +typedef enum { + SPI_FLASH_SLOWRD = 0, ///< Data read using single I/O, some limits on speed + SPI_FLASH_FASTRD, ///< Data read using single I/O, no limit on speed + SPI_FLASH_DOUT, ///< Data read using dual I/O + SPI_FLASH_DIO, ///< Both address & data transferred using dual I/O + SPI_FLASH_QOUT, ///< Data read using quad I/O + SPI_FLASH_QIO, ///< Both address & data transferred using quad I/O + + SPI_FLASH_READ_MODE_MAX, ///< The fastest io mode supported by the host is ``ESP_FLASH_READ_MODE_MAX-1``. +} esp_flash_read_mode_t; + +/// type to store pre-calculated register value in above layers +typedef typeof(SPI1.clock) spi_flash_ll_clock_reg_t; + +/*------------------------------------------------------------------------------ + * Control + *----------------------------------------------------------------------------*/ +/** + * Reset peripheral registers before configuration and starting control + * + * @param dev Beginning address of the peripheral registers. + */ +static inline void spi_flash_ll_reset(spi_dev_t *dev) +{ + dev->user.val = 0; + dev->ctrl.val = 0; +} + +/** + * Check whether the previous operation is done. + * + * @param dev Beginning address of the peripheral registers. + * + * @return true if last command is done, otherwise false. + */ +static inline bool spi_flash_ll_cmd_is_done(const spi_dev_t *dev) +{ + return (dev->cmd.val == 0); +} + +/** + * Erase the flash chip. + * + * @param dev Beginning address of the peripheral registers. + */ +static inline void spi_flash_ll_erase_chip(spi_dev_t *dev) +{ + dev->cmd.flash_ce = 1; +} + +/** + * Erase the sector, the address should be set by spi_flash_ll_set_address. + * + * @param dev Beginning address of the peripheral registers. + */ +static inline void spi_flash_ll_erase_sector(spi_dev_t *dev) +{ + dev->ctrl.val = 0; + dev->cmd.flash_se = 1; +} + +/** + * Erase the block, the address should be set by spi_flash_ll_set_address. + * + * @param dev Beginning address of the peripheral registers. + */ +static inline void spi_flash_ll_erase_block(spi_dev_t *dev) +{ + dev->cmd.flash_be = 1; +} + +/** + * Enable/disable write protection for the flash chip. + * + * @param dev Beginning address of the peripheral registers. + * @param wp true to enable the protection, false to disable (write enable). + */ +static inline void spi_flash_ll_set_write_protect(spi_dev_t *dev, bool wp) +{ + if (wp) { + dev->cmd.flash_wrdi = 1; + } else { + dev->cmd.flash_wren = 1; + } +} + +/** + * Get the read data from the buffer after ``spi_flash_ll_read`` is done. + * + * @param dev Beginning address of the peripheral registers. + * @param buffer Buffer to hold the output data + * @param read_len Length to get out of the buffer + */ +static inline void spi_flash_ll_get_buffer_data(spi_dev_t *dev, void *buffer, uint32_t read_len) +{ + if (((intptr_t)buffer % 4 == 0) && (read_len % 4 == 0)) { + // If everything is word-aligned, do a faster memcpy + memcpy(buffer, (void *)dev->data_buf, read_len); + } else { + // Otherwise, slow(er) path copies word by word + int copy_len = read_len; + for (int i = 0; i < (read_len + 3) / 4; i++) { + int word_len = MIN(sizeof(uint32_t), copy_len); + uint32_t word = dev->data_buf[i]; + memcpy(buffer, &word, word_len); + buffer = (void *)((intptr_t)buffer + word_len); + copy_len -= word_len; + } + } +} + +/** + * Write a word to the data buffer. + * + * @param dev Beginning address of the peripheral registers. + * @param word Data to write at address 0. + */ +static inline void spi_flash_ll_write_word(spi_dev_t *dev, uint32_t word) +{ + dev->data_buf[0] = word; +} + +/** + * Program a page of the flash chip. Call ``spi_flash_ll_set_address`` before + * this to set the address to program. + * + * @param dev Beginning address of the peripheral registers. + * @param buffer Buffer holding the data to program + * @param length Length to program. + */ +static inline void spi_flash_ll_program_page(spi_dev_t *dev, const void *buffer, uint32_t length) +{ + dev->user.usr_dummy = 0; + + // Load data registers, word at a time + int num_words = (length + 3) / 4; + for (int i = 0; i < num_words; i++) { + uint32_t word = 0; + uint32_t word_len = MIN(length, sizeof(word)); + memcpy(&word, buffer, word_len); + dev->data_buf[i] = word; + length -= word_len; + buffer = (void *)((intptr_t)buffer + word_len); + } + + dev->cmd.flash_pp = 1; +} + +/** + * Trigger a user defined transaction. All phases, including command, address, dummy, and the data phases, + * should be configured before this is called. + * + * @param dev Beginning address of the peripheral registers. + */ +static inline void spi_flash_ll_user_start(spi_dev_t *dev) +{ + dev->cmd.usr = 1; +} + +/** + * Check whether the host is idle to perform new commands. + * + * @param dev Beginning address of the peripheral registers. + * + * @return true if the host is idle, otherwise false + */ +static inline bool spi_flash_ll_host_idle(const spi_dev_t *dev) +{ + return dev->ext2.st != 0; +} + +/** + * Set phases for user-defined transaction to read + * + * @param dev Beginning address of the peripheral registers. + */ +static inline void spi_flash_ll_read_phase(spi_dev_t *dev) +{ + typeof (dev->user) user = { + .usr_command = 1, + .usr_mosi = 0, + .usr_miso = 1, + .usr_addr = 1, + }; + dev->user = user; +} +/*------------------------------------------------------------------------------ + * Configs + *----------------------------------------------------------------------------*/ +/** + * Select which pin to use for the flash + * + * @param dev Beginning address of the peripheral registers. + * @param pin Pin ID to use, 0-2. Set to other values to disable all the CS pins. + */ +static inline void spi_flash_ll_set_cs_pin(spi_dev_t *dev, int pin) +{ + dev->pin.cs0_dis = (pin == 0) ? 0 : 1; + dev->pin.cs1_dis = (pin == 1) ? 0 : 1; + dev->pin.cs2_dis = (pin == 2) ? 0 : 1; +} + +/** + * Set the read io mode. + * + * @param dev Beginning address of the peripheral registers. + * @param read_mode I/O mode to use in the following transactions. + */ +static inline void spi_flash_ll_set_read_mode(spi_dev_t *dev, esp_flash_read_mode_t read_mode) +{ + typeof (dev->ctrl) ctrl = dev->ctrl; + ctrl.val &= ~(SPI_FREAD_QIO_M | SPI_FREAD_QUAD_M | SPI_FREAD_DIO_M | SPI_FREAD_DUAL_M); + ctrl.val |= SPI_FASTRD_MODE_M; + switch (read_mode) { + case SPI_FLASH_FASTRD: + //the default option + break; + case SPI_FLASH_QIO: + ctrl.fread_qio = 1; + break; + case SPI_FLASH_QOUT: + ctrl.fread_quad = 1; + break; + case SPI_FLASH_DIO: + ctrl.fread_dio = 1; + break; + case SPI_FLASH_DOUT: + ctrl.fread_dual = 1; + break; + case SPI_FLASH_SLOWRD: + ctrl.fastrd_mode = 0; + break; + default: + abort(); + } + dev->ctrl = ctrl; +} + +/** + * Set clock frequency to work at. + * + * @param dev Beginning address of the peripheral registers. + * @param clock_val pointer to the clock value to set + */ +static inline void spi_flash_ll_set_clock(spi_dev_t *dev, spi_flash_ll_clock_reg_t *clock_val) +{ + dev->clock = *clock_val; +} + +/** + * Set the input length, in bits. + * + * @param dev Beginning address of the peripheral registers. + * @param bitlen Length of input, in bits. + */ +static inline void spi_flash_ll_set_miso_bitlen(spi_dev_t *dev, uint32_t bitlen) +{ + dev->user.usr_miso = bitlen > 0; + dev->miso_dlen.usr_miso_dbitlen = bitlen ? (bitlen - 1) : 0; +} + +/** + * Set the output length, in bits (not including command, address and dummy + * phases) + * + * @param dev Beginning address of the peripheral registers. + * @param bitlen Length of output, in bits. + */ +static inline void spi_flash_ll_set_mosi_bitlen(spi_dev_t *dev, uint32_t bitlen) +{ + dev->user.usr_mosi = bitlen > 0; + dev->mosi_dlen.usr_mosi_dbitlen = bitlen ? (bitlen - 1) : 0; +} + +/** + * Set the command with fixed length (8 bits). + * + * @param dev Beginning address of the peripheral registers. + * @param command Command to send + */ +static inline void spi_flash_ll_set_command8(spi_dev_t *dev, uint8_t command) +{ + dev->user.usr_command = 1; + typeof(dev->user2) user2 = { + .usr_command_value = command, + .usr_command_bitlen = (8 - 1), + }; + dev->user2 = user2; +} + +/** + * Set the address length to send, in bits. Should be called before commands that requires the address e.g. erase sector, read, write... + * + * @param dev Beginning address of the peripheral registers. + * @param bitlen Length of the address, in bits + */ +static inline void spi_flash_ll_set_addr_bitlen(spi_dev_t *dev, uint32_t bitlen) +{ + dev->user1.usr_addr_bitlen = (bitlen - 1); + dev->user.usr_addr = bitlen ? 1 : 0; +} + +/** + * Set the address to send. Should be called before commands that requires the address e.g. erase sector, read, write... + * + * @param dev Beginning address of the peripheral registers. + * @param addr Address to send + */ +static inline void spi_flash_ll_set_address(spi_dev_t *dev, uint32_t addr) +{ + dev->addr = addr; +} + +/** + * Set the length of dummy cycles. + * + * @param dev Beginning address of the peripheral registers. + * @param dummy_n Cycles of dummy phases + */ +static inline void spi_flash_ll_set_dummy(spi_dev_t *dev, uint32_t dummy_n) +{ + dev->user.usr_dummy = dummy_n ? 1 : 0; + dev->user1.usr_dummy_cyclelen = dummy_n - 1; +} diff --git a/components/soc/esp32/include/soc/spi_pins.h b/components/soc/esp32/include/soc/spi_pins.h index eb7af85827..80a17ff090 100644 --- a/components/soc/esp32/include/soc/spi_pins.h +++ b/components/soc/esp32/include/soc/spi_pins.h @@ -3,7 +3,7 @@ // 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 @@ -22,6 +22,14 @@ #define SPI_IOMUX_PIN_NUM_WP 10 #define SPI_IOMUX_PIN_NUM_HD 9 +//For D2WD and PICO-D4 chip +#define SPI_D2WD_PIN_NUM_MISO 17 +#define SPI_D2WD_PIN_NUM_MOSI 8 +#define SPI_D2WD_PIN_NUM_CLK 6 +#define SPI_D2WD_PIN_NUM_CS 16 +#define SPI_D2WD_PIN_NUM_WP 7 +#define SPI_D2WD_PIN_NUM_HD 11 + #define HSPI_IOMUX_PIN_NUM_MISO 12 #define HSPI_IOMUX_PIN_NUM_MOSI 13 #define HSPI_IOMUX_PIN_NUM_CLK 14 diff --git a/components/soc/include/hal/esp_flash_err.h b/components/soc/include/hal/esp_flash_err.h new file mode 100644 index 0000000000..c760f19fe6 --- /dev/null +++ b/components/soc/include/hal/esp_flash_err.h @@ -0,0 +1,39 @@ +// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// +// 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 "esp_err.h" + +/* + * Possible errors returned from esp flash internal functions, these error codes + * should be consistent with esp_err_t codes. But in order to make the source + * files less dependent to esp_err_t, they use the error codes defined in this + * replacable header. This header should ensure the consistency to esp_err_t. + */ + +/* These should be consistent with esp_err_t errors */ +#define ESP_ERR_FLASH_SIZE_NOT_MATCH ESP_ERR_INVALID_SIZE ///< The chip doesn't have enough space for the current partition table +#define ESP_ERR_FLASH_NO_RESPONSE ESP_ERR_INVALID_RESPONSE ///< Chip did not respond to the command, or timed out. + + +#define ESP_ERR_FLASH_ERR_BASE 0x6000 ///< Starting number of Flash error codes */ +//The ROM code has already taken 1 and 2, to avoid possible conflicts, start from 3. +#define ESP_ERR_FLASH_NOT_INITIALISED (ESP_ERR_FLASH_ERR_BASE+3) ///< esp_flash_chip_t structure not correctly initialised by esp_flash_init(). +#define ESP_ERR_FLASH_UNSUPPORTED_HOST (ESP_ERR_FLASH_ERR_BASE+4) ///< Requested operation isn't supported via this host SPI bus (chip->spi field). +#define ESP_ERR_FLASH_UNSUPPORTED_CHIP (ESP_ERR_FLASH_ERR_BASE+5) ///< Requested operation isn't supported by this model of SPI flash chip. +#define ESP_ERR_FLASH_PROTECTED (ESP_ERR_FLASH_ERR_BASE+6) ///< Write operation failed due to chip's write protection being enabled. + + diff --git a/components/soc/include/hal/spi_flash_hal.h b/components/soc/include/hal/spi_flash_hal.h new file mode 100644 index 0000000000..1a5c373614 --- /dev/null +++ b/components/soc/include/hal/spi_flash_hal.h @@ -0,0 +1,243 @@ +// Copyright 2010-2019 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +/******************************************************************************* + * NOTICE + * The HAL is not public api, don't use in application code. + * See readme.md in soc/include/hal/readme.md + ******************************************************************************/ + +// The HAL layer for SPI Flash (common part) + +#pragma once + +#include "hal/spi_flash_ll.h" +#include "hal/spi_flash_host_drv.h" +#include "soc/soc_memory_layout.h" + +#define ESP_FLASH_DEFAULT_FREQ ESP_FLASH_20MHZ + +/* Hardware host-specific constants */ +#define SPI_FLASH_HAL_MAX_WRITE_BYTES 64 +#define SPI_FLASH_HAL_MAX_READ_BYTES 64 + +///Lowest speed supported by the driver, currently 5 MHz +#define ESP_FLASH_SPEED_MIN ESP_FLASH_5MHZ + +/** + * @brief SPI flash clock speed values, always refer to them by the enum rather + * than the actual value (more speed may be appended into the list). + * + * A strategy to select the maximum allowed speed is to enumerate from the + * ``ESP_FLSH_SPEED_MAX-1`` or highest frequency supported by your flash, and + * decrease the speed until the probing success. + */ +typedef enum { + ESP_FLASH_5MHZ = 0, ///< The flash runs under 5MHz + ESP_FLASH_10MHZ, ///< The flash runs under 10MHz + ESP_FLASH_20MHZ, ///< The flash runs under 20MHz + ESP_FLASH_26MHZ, ///< The flash runs under 26MHz + ESP_FLASH_40MHZ, ///< The flash runs under 40MHz + ESP_FLASH_80MHZ, ///< The flash runs under 80MHz + ESP_FLASH_SPEED_MAX, ///< The maximum frequency supported by the host is ``ESP_FLASH_SPEED_MAX-1``. +} esp_flash_speed_t; + +/** + * Generic driver context structure for all chips using the SPI peripheral. + * Include this into the HEAD of the driver data for other driver + * implementations that also use the SPI peripheral. + */ +typedef struct { + spi_dev_t *spi; ///< Pointer to SPI peripheral registers (SP1, SPI2 or SPI3). Set before initialisation. + int cs_num; ///< Which cs pin is used, 0-2. + int extra_dummy; + spi_flash_ll_clock_reg_t clock_conf; +} spi_flash_memspi_data_t; + +/// Configuration structure for the SPI driver. +typedef struct { + int host_id; ///< SPI peripheral ID, 1 for SPI1, 2 for SPI2 (HSPI), 3 for SPI3 (VSPI) + int cs_num; ///< Which cs pin is used, 0-2. + bool iomux; ///< Whether the IOMUX is used, used for timing compensation. + int input_delay_ns; ///< Input delay on the MISO pin after the launch clock, used for timing compensation. + esp_flash_speed_t speed;///< SPI flash clock speed to work at. +} spi_flash_memspi_config_t; + +/** + * Configure SPI flash hal settings. + * + * @param data Buffer to hold configured data, the buffer should be in DRAM to be available when cache disabled + * @param cfg Configurations to set + * + * @return + * - ESP_OK: success + * - ESP_ERR_INVALID_ARG: the data buffer is not in the DRAM. + */ +esp_err_t spi_flash_hal_init(spi_flash_memspi_data_t *data_out, const spi_flash_memspi_config_t *cfg); + +/** + * Configure the device-related register before transactions. + * + * @param driver The driver context. + * + * @return always return ESP_OK. + */ +esp_err_t spi_flash_hal_device_config(spi_flash_host_driver_t *driver); + +/** + * Send an user-defined spi transaction to the device. + * + * @note This is usually used when the memspi interface doesn't support some + * particular commands. Since this function supports timing compensation, it is + * also used to receive some data when the frequency is high. + * + * @param driver The driver context. + * @param trans The transaction to send, also holds the received data. + * + * @return always return ESP_OK. + */ +esp_err_t spi_flash_hal_common_command(spi_flash_host_driver_t *driver, spi_flash_trans_t *trans); + +/** + * Erase whole flash chip. + * + * @param driver The driver context. + */ +void spi_flash_hal_erase_chip(spi_flash_host_driver_t *driver); + +/** + * Erase a specific sector by its start address. + * + * @param driver The driver context. + * @param start_address Start address of the sector to erase. + */ +void spi_flash_hal_erase_sector(spi_flash_host_driver_t *driver, uint32_t start_address); + +/** + * Erase a specific block by its start address. + * + * @param driver The driver context. + * @param start_address Start address of the block to erase. + */ +void spi_flash_hal_erase_block(spi_flash_host_driver_t *driver, uint32_t start_address); + +/** + * Program a page of the flash. + * + * @param driver The driver context. + * @param address Address of the page to program + * @param buffer Data to program + * @param length Size of the buffer in bytes, no larger than ``SPI_FLASH_HAL_MAX_WRITE_BYTES`` (64) bytes. + */ +void spi_flash_hal_program_page(spi_flash_host_driver_t *driver, const void *buffer, uint32_t address, uint32_t length); + +/** + * Read from the flash. The read command should be set by ``spi_flash_hal_configure_host_read_mode`` before. + * + * @param driver The driver context. + * @param buffer Buffer to store the read data + * @param address Address to read + * @param length Length to read, no larger than ``SPI_FLASH_HAL_MAX_READ_BYTES`` (64) bytes. + * + * @return always return ESP_OK. + */ +esp_err_t spi_flash_hal_read(spi_flash_host_driver_t *driver, void *buffer, uint32_t address, uint32_t read_len); + +/** + * Enable or disable the write protection of the flash chip. + * + * @param driver The driver context. + * @param wp true to enable the write protection, otherwise false. + * + * @return always return ESP_OK. + */ +esp_err_t spi_flash_hal_set_write_protect(spi_flash_host_driver_t *chip_drv, bool wp); + +/** + * Check whether the SPI host is idle and can perform other operations. + * + * @param driver The driver context. + * + * @return ture if idle, otherwise false. + */ +bool spi_flash_hal_host_idle(spi_flash_host_driver_t *driver); + +/** + * Configure the SPI host hardware registers for the specified read mode. + * + * Note that calling this configures SPI host registers, so if running any + * other commands as part of set_read_mode() then these must be run before + * calling this function. + * + * @param driver The driver context + * @param read_mode The HW read mode to use + * @param addr_bitlen Length of the address phase, in bits + * @param dummy_cyclelen_base Base cycles of the dummy phase, some extra dummy cycles may be appended to compensate the timing. + * @param read_command Actual reading command to send to flash chip on the bus. + * + * @return always return ESP_OK. + */ +esp_err_t spi_flash_hal_configure_host_read_mode(spi_flash_host_driver_t *driver, esp_flash_read_mode_t read_mode, + uint32_t addr_bitlen, uint32_t dummy_cyclelen_base, + uint32_t read_command); + +/** + * Poll until the last operation is done. + * + * @param driver The driver context. + */ +void spi_flash_hal_poll_cmd_done(spi_flash_host_driver_t *driver); + +/** + * Check whether the given buffer can be used as the write buffer directly. If 'chip' is connected to the main SPI bus, we can only write directly from + * regions that are accessible ith cache disabled. * + * + * @param driver The driver context + * @param p The buffer holding data to send. + * + * @return True if the buffer can be used to send data, otherwise false. + */ +static inline bool spi_flash_hal_supports_direct_write(spi_flash_host_driver_t *driver, const void *p) +{ +#ifdef ESP_PLATFORM + bool direct_write = ( ((spi_flash_memspi_data_t *)driver->driver_data)->spi != &SPI1 + || esp_ptr_in_dram(p) ); +#else + //If it is not on real chips, there is no limitation that the data has to be in DRAM. + bool direct_write = true; +#endif + return direct_write; +} + +/** + * Check whether the given buffer can be used as the read buffer directly. If 'chip' is connected to the main SPI bus, we can only read directly from + * regions that are accessible ith cache disabled. * + * + * @param driver The driver context + * @param p The buffer to hold the received data. + * + * @return True if the buffer can be used to receive data, otherwise false. + */ +static inline bool spi_flash_hal_supports_direct_read(spi_flash_host_driver_t *driver, const void *p) +{ +#ifdef ESP_PLATFORM +//currently the driver doesn't support to read through DMA, no word-aligned requirements + bool direct_read = ( ((spi_flash_memspi_data_t *)driver->driver_data)->spi != &SPI1 + || esp_ptr_in_dram(p) ); +#else + //If it is not on real chips, there is no limitation that the data has to be in DRAM. + bool direct_read = true; +#endif + return direct_read; +} \ No newline at end of file diff --git a/components/soc/include/hal/spi_flash_host_drv.h b/components/soc/include/hal/spi_flash_host_drv.h new file mode 100644 index 0000000000..5095bc8f50 --- /dev/null +++ b/components/soc/include/hal/spi_flash_host_drv.h @@ -0,0 +1,112 @@ +// Copyright 2010-2019 Espressif Systems (Shanghai) PTE LTD +// +// 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 "hal/spi_flash_ll.h" +#include "hal/esp_flash_err.h" + +/** Definition of a common transaction. Also holds the return value. */ +typedef struct { + uint8_t command; ///< Command to send, always 8bits + uint8_t mosi_len; ///< Output data length, in bits + uint8_t miso_len; ///< Input data length, in bits + uint32_t mosi_data; ///< Output data to slave + uint32_t miso_data[2]; ///< [out] Input data from slave, little endian +} spi_flash_trans_t; + + +struct spi_flash_host_driver_t; +typedef struct spi_flash_host_driver_t spi_flash_host_driver_t; + +/** Host driver configuration and context structure. */ +struct spi_flash_host_driver_t { + /** + * Configuration and static data used by the specific host driver. The type + * is determined by the host driver. + */ + void *driver_data; + /** + * Configure the device-related register before transactions. This saves + * some time to re-configure those registers when we send continuously + */ + esp_err_t (*dev_config)(spi_flash_host_driver_t *driver); + /** + * Send an user-defined spi transaction to the device. + */ + esp_err_t (*common_command)(spi_flash_host_driver_t *driver, spi_flash_trans_t *t); + /** + * Read flash ID. + */ + esp_err_t (*read_id)(spi_flash_host_driver_t *driver, uint32_t *id); + /** + * Erase whole flash chip. + */ + void (*erase_chip)(spi_flash_host_driver_t *driver); + /** + * Erase a specific sector by its start address. + */ + void (*erase_sector)(spi_flash_host_driver_t *driver, uint32_t start_address); + /** + * Erase a specific block by its start address. + */ + void (*erase_block)(spi_flash_host_driver_t *driver, uint32_t start_address); + /** + * Read the status of the flash chip. + */ + esp_err_t (*read_status)(spi_flash_host_driver_t *driver, uint8_t *out_sr); + /** + * Disable write protection. + */ + esp_err_t (*set_write_protect)(spi_flash_host_driver_t *driver, bool wp); + /** + * Program a page of the flash. Check ``max_write_bytes`` for the maximum allowed writing length. + */ + void (*program_page)(spi_flash_host_driver_t *driver, const void *buffer, uint32_t address, uint32_t length); + /** Check whether need to allocate new buffer to write */ + bool (*supports_direct_write)(spi_flash_host_driver_t *driver, const void *p); + /** Check whether need to allocate new buffer to read */ + bool (*supports_direct_read)(spi_flash_host_driver_t *driver, const void *p); + /** maximum length of program_page */ + int max_write_bytes; + /** + * Read data from the flash. Check ``max_read_bytes`` for the maximum allowed reading length. + */ + esp_err_t (*read)(spi_flash_host_driver_t *driver, void *buffer, uint32_t address, uint32_t read_len); + /** maximum length of read */ + int max_read_bytes; + /** + * Check whether the host is idle to perform new operations. + */ + bool (*host_idle)(spi_flash_host_driver_t *driver); + /** + * Configure the host to work at different read mode. + */ + esp_err_t (*configure_host_read_mode)(spi_flash_host_driver_t *driver, esp_flash_read_mode_t read_mode, uint32_t addr_bitlen, uint32_t dummy_bitlen_base, uint32_t read_command); + /** + * Internal use, poll the HW until the last operation is done. + */ + void (*poll_cmd_done)(spi_flash_host_driver_t *driver); + /** + * For some host (SPI1), they are shared with a cache. When the data is + * modified, the cache needs to be flushed. Left NULL if not supported. + */ + esp_err_t (*flush_cache)(spi_flash_host_driver_t* driver, uint32_t addr, uint32_t size); + /** + * Check if the given region is protected (e.g. is the bootloader). Left + * NULL if current host doesn't need protection. + */ + bool (*region_protected)(spi_flash_host_driver_t* driver, uint32_t addr, uint32_t size); +}; + diff --git a/components/soc/linker.lf b/components/soc/linker.lf index d91f728b6d..5cb2021091 100644 --- a/components/soc/linker.lf +++ b/components/soc/linker.lf @@ -12,4 +12,6 @@ entries: rtc_wdt (noflash_text) spi_hal_iram (noflash_text) spi_slave_hal_iram (noflash_text) - lldesc (noflash_text) \ No newline at end of file + spi_flash_hal_iram (noflash) + lldesc (noflash_text) + diff --git a/components/soc/src/hal/spi_flash_hal.c b/components/soc/src/hal/spi_flash_hal.c new file mode 100644 index 0000000000..6a88d35a17 --- /dev/null +++ b/components/soc/src/hal/spi_flash_hal.c @@ -0,0 +1,74 @@ +// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 "hal/spi_flash_hal.h" +#include "string.h" +#include "hal/hal_defs.h" + +#define APB_CYCLE_NS (1000*1000*1000LL/APB_CLK_FREQ) + +static const char TAG[] = "FLASH_HAL"; + +typedef struct { + int freq; + spi_flash_ll_clock_reg_t clock_reg_val; +} spi_flash_hal_clock_config_t; + + +static const spi_flash_hal_clock_config_t spi_flash_clk_cfg_reg[ESP_FLASH_SPEED_MAX] = { + {5e6, SPI_FLASH_LL_CLKREG_VAL_5MHZ}, + {10e6, SPI_FLASH_LL_CLKREG_VAL_10MHZ}, + {20e6, SPI_FLASH_LL_CLKREG_VAL_20MHZ}, + {26e6, SPI_FLASH_LL_CLKREG_VAL_26MHZ}, + {40e6, SPI_FLASH_LL_CLKREG_VAL_40MHZ}, + {80e6, SPI_FLASH_LL_CLKREG_VAL_80MHZ}, +}; + +static inline int get_dummy_n(bool gpio_is_used, int input_delay_ns, int eff_clk) +{ + const int apbclk_kHz = APB_CLK_FREQ / 1000; + //calculate how many apb clocks a period has + const int apbclk_n = APB_CLK_FREQ / eff_clk; + const int gpio_delay_ns = gpio_is_used ? (APB_CYCLE_NS * 2) : 0; + + //calculate how many apb clocks the delay is, the 1 is to compensate in case ``input_delay_ns`` is rounded off. + int apb_period_n = (1 + input_delay_ns + gpio_delay_ns) * apbclk_kHz / 1000 / 1000; + if (apb_period_n < 0) { + apb_period_n = 0; + } + + return apb_period_n / apbclk_n; +} + +esp_err_t spi_flash_hal_init(spi_flash_memspi_data_t *data_out, const spi_flash_memspi_config_t *cfg) +{ + if (!esp_ptr_internal(data_out)) { + return ESP_ERR_INVALID_ARG; + } + *data_out = (spi_flash_memspi_data_t) { + .spi = spi_flash_ll_get_hw(cfg->host_id), + .cs_num = cfg->cs_num, + .extra_dummy = get_dummy_n(!cfg->iomux, cfg->input_delay_ns, spi_flash_clk_cfg_reg[cfg->speed].freq), + .clock_conf = spi_flash_clk_cfg_reg[cfg->speed].clock_reg_val, + }; + + ESP_EARLY_LOGD(TAG, "extra_dummy: %d", data_out->extra_dummy); + return ESP_OK; +} + +static inline spi_dev_t *get_spi_dev(spi_flash_host_driver_t *chip_drv) +{ + return ((spi_flash_memspi_data_t *)chip_drv->driver_data)->spi; +} diff --git a/components/soc/src/hal/spi_flash_hal_iram.c b/components/soc/src/hal/spi_flash_hal_iram.c new file mode 100644 index 0000000000..ddd2c7d5f3 --- /dev/null +++ b/components/soc/src/hal/spi_flash_hal_iram.c @@ -0,0 +1,144 @@ +// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// +// 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 "hal/spi_flash_hal.h" +#include "string.h" +#include "hal/hal_defs.h" + +#define ADDRESS_MASK_24BIT 0xFFFFFF + +static inline spi_dev_t *get_spi_dev(spi_flash_host_driver_t *chip_drv) +{ + return ((spi_flash_memspi_data_t *)chip_drv->driver_data)->spi; +} + +void spi_flash_hal_poll_cmd_done(spi_flash_host_driver_t *driver) +{ + while (!spi_flash_ll_cmd_is_done(get_spi_dev(driver))) { + //nop + } +} + +esp_err_t spi_flash_hal_device_config(spi_flash_host_driver_t *driver) +{ + spi_flash_memspi_data_t *drv_data = (spi_flash_memspi_data_t *)driver->driver_data; + spi_dev_t *dev = get_spi_dev(driver); + spi_flash_ll_reset(dev); + spi_flash_ll_set_cs_pin(dev, drv_data->cs_num); + spi_flash_ll_set_clock(dev, &drv_data->clock_conf); + return ESP_OK; +} + +esp_err_t spi_flash_hal_configure_host_read_mode(spi_flash_host_driver_t *driver, esp_flash_read_mode_t read_mode, + uint32_t addr_bitlen, uint32_t dummy_cyclelen_base, + uint32_t read_command) +{ + // Add dummy cycles to compensate for latency of GPIO matrix and external delay, if necessary... + int dummy_cyclelen = dummy_cyclelen_base + ((spi_flash_memspi_data_t *)driver->driver_data)->extra_dummy; + + spi_dev_t *dev = get_spi_dev(driver); + spi_flash_ll_set_addr_bitlen(dev, addr_bitlen); + spi_flash_ll_set_command8(dev, read_command); + spi_flash_ll_read_phase(dev); + spi_flash_ll_set_dummy(dev, dummy_cyclelen); + spi_flash_ll_set_read_mode(dev, read_mode); + return ESP_OK; +} + +esp_err_t spi_flash_hal_common_command(spi_flash_host_driver_t *chip_drv, spi_flash_trans_t *trans) +{ + chip_drv->configure_host_read_mode(chip_drv, SPI_FLASH_FASTRD, 0, 0, 0); + spi_dev_t *dev = get_spi_dev(chip_drv); + spi_flash_ll_set_command8(dev, trans->command); + spi_flash_ll_set_addr_bitlen(dev, 0); + spi_flash_ll_set_miso_bitlen(dev, trans->miso_len); + spi_flash_ll_set_mosi_bitlen(dev, trans->mosi_len); + + spi_flash_ll_write_word(dev, trans->mosi_data); + + spi_flash_ll_user_start(dev); + chip_drv->poll_cmd_done(chip_drv); + spi_flash_ll_get_buffer_data(dev, trans->miso_data, 8); + return ESP_OK; +} + +void spi_flash_hal_erase_chip(spi_flash_host_driver_t *chip_drv) +{ + spi_dev_t *dev = get_spi_dev(chip_drv); + spi_flash_ll_erase_chip(dev); + chip_drv->poll_cmd_done(chip_drv); +} + +void spi_flash_hal_erase_sector(spi_flash_host_driver_t *chip_drv, uint32_t start_address) +{ + spi_dev_t *dev = get_spi_dev(chip_drv); + spi_flash_ll_set_addr_bitlen(dev, 24); + spi_flash_ll_set_address(dev, start_address & ADDRESS_MASK_24BIT); + spi_flash_ll_erase_sector(dev); + chip_drv->poll_cmd_done(chip_drv); +} + +void spi_flash_hal_erase_block(spi_flash_host_driver_t *chip_drv, uint32_t start_address) +{ + spi_dev_t *dev = get_spi_dev(chip_drv); + spi_flash_ll_set_addr_bitlen(dev, 24); + spi_flash_ll_set_address(dev, start_address & ADDRESS_MASK_24BIT); + spi_flash_ll_erase_block(dev); + chip_drv->poll_cmd_done(chip_drv); +} + +void spi_flash_hal_program_page(spi_flash_host_driver_t *chip_drv, const void *buffer, uint32_t address, uint32_t length) +{ + spi_dev_t *dev = get_spi_dev(chip_drv); + spi_flash_ll_set_addr_bitlen(dev, 24); + spi_flash_ll_set_address(dev, (address & ADDRESS_MASK_24BIT) | (length << 24)); + spi_flash_ll_program_page(dev, buffer, length); + chip_drv->poll_cmd_done(chip_drv); +} + +esp_err_t spi_flash_hal_read(spi_flash_host_driver_t *chip_drv, void *buffer, uint32_t address, uint32_t read_len) +{ + spi_dev_t *dev = get_spi_dev(chip_drv); + //the command is already set by ``spi_flash_hal_configure_host_read_mode`` before. + spi_flash_ll_set_address(dev, address << 8); + spi_flash_ll_set_miso_bitlen(dev, read_len * 8); + spi_flash_ll_user_start(dev); + chip_drv->poll_cmd_done(chip_drv); + spi_flash_ll_get_buffer_data(dev, buffer, read_len); + return ESP_OK; +} + + +bool spi_flash_hal_host_idle(spi_flash_host_driver_t *chip_drv) +{ + spi_dev_t *dev = get_spi_dev(chip_drv); + bool idle = spi_flash_ll_host_idle(dev); + + // Not clear if this is necessary, or only necessary if + // chip->spi == SPI1. But probably doesn't hurt... + if (dev == &SPI1) { + idle &= spi_flash_ll_host_idle(&SPI0); + } + + return idle; +} + +esp_err_t spi_flash_hal_set_write_protect(spi_flash_host_driver_t *chip_drv, bool wp) +{ + spi_dev_t *dev = get_spi_dev(chip_drv); + spi_flash_ll_set_write_protect(dev, wp); + chip_drv->poll_cmd_done(chip_drv); + return ESP_OK; +} diff --git a/components/spi_flash/CMakeLists.txt b/components/spi_flash/CMakeLists.txt index be290e7c32..eb17b31eef 100644 --- a/components/spi_flash/CMakeLists.txt +++ b/components/spi_flash/CMakeLists.txt @@ -8,11 +8,22 @@ else() "flash_mmap.c" "flash_ops.c" "partition.c" - "spi_flash_rom_patch.c") + "spi_flash_rom_patch.c" + "spi_flash_chip_drivers.c" + "spi_flash_chip_generic.c" + "spi_flash_chip_issi.c" + "spi_flash_os_func_app.c" + "spi_flash_os_func_noos.c" + "memspi_host_driver.c" + ) + if(NOT CONFIG_SPI_FLASH_USE_LEGACY_IMPL) + list(APPEND COMPONENT_SRCS "esp_flash_api.c") + endif() set(COMPONENT_PRIV_REQUIRES bootloader_support app_update soc) endif() set(COMPONENT_ADD_INCLUDEDIRS include) +set(COMPONENT_PRIV_INCLUDEDIRS private_include) set(COMPONENT_ADD_LDFRAGMENTS linker.lf) register_component() diff --git a/components/spi_flash/Kconfig b/components/spi_flash/Kconfig index 6ba4d5180d..8a10651ff0 100644 --- a/components/spi_flash/Kconfig +++ b/components/spi_flash/Kconfig @@ -77,4 +77,21 @@ menu "SPI Flash driver" bool "Allowed" endchoice + config SPI_FLASH_USE_LEGACY_IMPL + bool "Use the legacy implementation before IDF v4.0" + default n + help + The implementation of SPI flash has been greatly changed in IDF v4.0. + Enable this option to use the legacy implementation. + + menu "Auto-detect flash chips" + + config SPI_FLASH_SUPPORT_ISSI_CHIP + bool "ISSI" + default y + help + Enable this to support auto detection of ISSI chips if chip vendor not specified. + This adds support for variant chips, however will extend detecting time. + endmenu #auto detect flash chips + endmenu diff --git a/components/spi_flash/README.rst b/components/spi_flash/README.rst index 04376c6140..e65b1ad4c6 100644 --- a/components/spi_flash/README.rst +++ b/components/spi_flash/README.rst @@ -3,22 +3,40 @@ SPI Flash API Overview -------- -The spi_flash component contains API functions related to reading, writing, erasing, memory mapping for data in the external SPI flash. The spi_flash component also has higher-level API functions which work with partitions defined in the :doc:`partition table `. +The spi_flash component contains API functions related to reading, writing, +erasing, memory mapping for data in the external flash. The spi_flash +component also has higher-level API functions which work with partitions +defined in the :doc:`partition table `. -Note that all the functionality is limited to the "main" SPI flash chip, the same SPI flash chip from which programs are runs. For ``spi_flash_*`` functions, this is a software limitation. The underlying ROM functions which work with SPI flash do not have provisions for working with flash chips attached to SPI peripherals other than SPI0. +Different from the API before IDF v4.0, the functionality is not limited to +the "main" SPI flash chip (the same SPI flash chip from which program runs). +With different chip pointers, you can access to external flashes chips on not +only SPI0/1 but also HSPI/VSPI buses. + +Kconfig option :ref:``CONFIG_SPI_FLASH_USE_LEGACY_IMPL`` can be used to switch +``spi_flash_*`` functions back to the implementation before IDF v4.0. +However, the code size may get bigger if you use the new API and the old API +the same time. + +Encrypted reads and writes use the old implementation, even if +:ref:``CONFIG_SPI_FLASH_USE_LEGACY_IMPL`` is not enabled. As such, encrypted +flash operations are only supported with the main flash chip (and not with +other flash chips on SPI1 with different CS). SPI flash access API -------------------- This is the set of API functions for working with data in flash: -- :cpp:func:`spi_flash_read` reads data from flash to RAM -- :cpp:func:`spi_flash_write` writes data from RAM to flash -- :cpp:func:`spi_flash_erase_sector` erases individual sectors of flash -- :cpp:func:`spi_flash_erase_range` erases ranges of addresses in flash -- :cpp:func:`spi_flash_get_chip_size` returns flash chip size, in bytes, as configured in menuconfig +- :cpp:func:`esp_flash_read` reads data from flash to RAM +- :cpp:func:`esp_flash_write` writes data from RAM to flash +- :cpp:func:`esp_flash_erase_region` erases specific region of flash +- :cpp:func:`esp_flash_erase_chip` erases the whole flash +- :cpp:func:`esp_flash_get_chip_size` returns flash chip size, in bytes, as configured in menuconfig -Generally, try to avoid using the raw SPI flash functions in favor of :ref:`partition-specific functions `. +Generally, try to avoid using the raw SPI flash functions to the "main" SPI +flash chip in favour of :ref:`partition-specific functions +`. SPI Flash Size -------------- @@ -27,7 +45,7 @@ The SPI flash size is configured by writing a field in the software bootloader i By default, the SPI flash size is detected by esptool.py when this bootloader is written to flash, and the header is updated with the correct size. Alternatively, it is possible to generate a fixed flash size by setting :envvar:`CONFIG_ESPTOOLPY_FLASHSIZE` in ``make menuconfig``. -If it is necessary to override the configured flash size at runtime, it is possible to set the ``chip_size`` member of the ``g_rom_flashchip`` structure. This size is used by ``spi_flash_*`` functions (in both software & ROM) to check the bounds. +If it is necessary to override the configured flash size at runtime, it is possible to set the ``chip_size`` member of the ``g_rom_flashchip`` structure. This size is used by ``esp_flash_*`` functions (in both software & ROM) to check the bounds. Concurrency Constraints ----------------------- @@ -121,3 +139,67 @@ Differences between :cpp:func:`spi_flash_mmap` and :cpp:func:`esp_partition_mmap - :cpp:func:`esp_partition_mmap` may be given any arbitrary offset within the partition, it will adjust the returned pointer to mapped memory as necessary Note that since memory mapping happens in 64KB blocks, it may be possible to read data outside of the partition provided to ``esp_partition_mmap``. + +Implementation +-------------- + +The ``esp_flash_t`` structure holds chip data as well as three important parts of this API: + +1. The host driver, which provides the hardware support to access the chip; +2. The chip driver, which provides compability service to different chips; +3. The OS functions, provides support of some OS functions (e.g. lock, delay) + in different stages (1st/2st boot, or the app). + +Host driver +^^^^^^^^^^^ + +The host driver relies on an interface (``spi_flash_host_driver_t``) defined +in the ``spi_flash_host_drv.h`` (in the ``soc/include/hal`` folder). This +interface provides some common functions to communicate with the chip. + +In other files of the SPI HAL, some of these functions are implemented with +existing ESP32 memory-spi functionalities. However due to the speed +limitations of ESP32, the HAL layer can't provide high-speed implementations +to some reading commands (So we didn't do it at all). The files +(``memspi_host_driver.h`` and ``.c``) implement the high-speed version of +these commands with the ``common_command`` function provided in the HAL, and +wrap these functions as ``spi_flash_host_driver_t`` for upper layer to use. + +You can also implement your own host driver, even with the GPIO. As long as +all the functions in the ``spi_flash_host_driver_t`` are implemented, the +esp_flash API can access to the flash regardless of the lowlevel hardware. + +Chip driver +^^^^^^^^^^^ + +The chip driver, defined in ``spi_flash_chip_driver.h``, wraps basic +functions provided by the host driver for the API layer to use. + +Some operations need some commands to be sent first, or read some status +after. Some chips need different command or value, or need special +communication ways. + +There is a type of chip called ``generic chip`` which stands for common +chips. Other special chip drivers can be developed on the base of the generic +chip. + +The chip driver relies on the host driver. + +OS functions +^^^^^^^^^^^^ + +Currently the OS function layer provides a lock and a delay entrance. + +The lock is used to resolve the conflicts between the SPI chip access and +other functions. E.g. the cache (used for the code and psram data fetch) +should be disabled when the flash chip on the SPI0/1 is being accessed. Also, +some devices which don't have CS wire, or the wire is controlled by the +software (e.g. SD card via SPI interface), requires the bus to be monopolized +during a period. + +The delay is used by some long operations which requires the master to wait +or polling periodly. + + +The top API wraps these the chip driver and OS functions into an entire +component, and also provides some argument checking. diff --git a/components/spi_flash/README_legacy.rst b/components/spi_flash/README_legacy.rst new file mode 100644 index 0000000000..a8e5da15a6 --- /dev/null +++ b/components/spi_flash/README_legacy.rst @@ -0,0 +1,127 @@ +SPI Flash API (Legacy) +======================== + +Overview +-------- + +This is the readme for the APIs before IDF v4.0. Enable the kconfig option ``SPI_FLASH_USE_LEGACY_IMPL`` to use the +legacy implementation. + +The spi_flash component contains API functions related to reading, writing, erasing, memory mapping for data in the external SPI flash. The spi_flash component also has higher-level API functions which work with partitions defined in the :doc:`partition table `. + +Note that all the functionality is limited to the "main" SPI flash chip, the same SPI flash chip from which programs are runs. For ``spi_flash_*`` functions, this is a software limitation. The underlying ROM functions which work with SPI flash do not have provisions for working with flash chips attached to SPI peripherals other than SPI0. + +SPI flash access API +-------------------- + +This is the set of API functions for working with data in flash: + +- :cpp:func:`spi_flash_read` reads data from flash to RAM +- :cpp:func:`spi_flash_write` writes data from RAM to flash +- :cpp:func:`spi_flash_erase_sector` erases individual sectors of flash +- :cpp:func:`spi_flash_erase_range` erases ranges of addresses in flash +- :cpp:func:`spi_flash_get_chip_size` returns flash chip size, in bytes, as configured in menuconfig + +Generally, try to avoid using the raw SPI flash functions in favor of :ref:`partition-specific functions `. + +SPI Flash Size +-------------- + +The SPI flash size is configured by writing a field in the software bootloader image header, flashed at offset 0x1000. + +By default, the SPI flash size is detected by esptool.py when this bootloader is written to flash, and the header is updated with the correct size. Alternatively, it is possible to generate a fixed flash size by setting :envvar:`CONFIG_ESPTOOLPY_FLASHSIZE` in ``make menuconfig``. + +If it is necessary to override the configured flash size at runtime, it is possible to set the ``chip_size`` member of the ``g_rom_flashchip`` structure. This size is used by ``spi_flash_*`` functions (in both software & ROM) to check the bounds. + +Concurrency Constraints +----------------------- + +Because the SPI flash is also used for firmware execution via the instruction & data caches, these caches must be disabled while reading/writing/erasing. This means that both CPUs must be running code from IRAM and must only be reading data from DRAM while flash write operations occur. + +If you use the API functions documented here, then these constraints are applied automatically and transparently. However, note that it will have some performance impact on other tasks in the system. + +For differences between IRAM, DRAM, and flash cache, please refer to the :ref:`application memory layout ` documentation. + +To avoid reading flash cache accidentally, when one CPU initiates a flash write or erase operation, the other CPU is put into a blocked state, and all non-IRAM-safe interrupts are disabled on both CPUs until the flash operation completes. + +If one CPU initiates a flash write or erase operation, the other CPU is put into a blocked state to avoid reading flash cache accidentally. All interrupts not safe for IRAM are disabled on both CPUs until the flash operation completes. + +.. _iram-safe-interrupt-handlers: + +IRAM-Safe Interrupt Handlers +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If you have an interrupt handler that you want to execute while a flash operation is in progress (for example, for low latency operations), set the ``ESP_INTR_FLAG_IRAM`` flag when the :doc:`interrupt handler is registered `. + +You must ensure that all data and functions accessed by these interrupt handlers, including the ones that handlers call, are located in IRAM or DRAM. + +Use the ``IRAM_ATTR`` attribute for functions:: + + #include "esp_attr.h" + + void IRAM_ATTR gpio_isr_handler(void* arg) + { + // ... + } + +Use the ``DRAM_ATTR`` and ``DRAM_STR`` attributes for constant data:: + + void IRAM_ATTR gpio_isr_handler(void* arg) + { + const static DRAM_ATTR uint8_t INDEX_DATA[] = { 45, 33, 12, 0 }; + const static char *MSG = DRAM_STR("I am a string stored in RAM"); + } + +Note that knowing which data should be marked with ``DRAM_ATTR`` can be hard, the compiler will sometimes recognize that a variable or expression is constant (even if it is not marked ``const``) and optimize it into flash, unless it is marked with ``DRAM_ATTR``. + +If a function or symbol is not correctly put into IRAM/DRAM, and the interrupt handler reads from the flash cache during a flash operation, it will cause a crash due to Illegal Instruction exception (for code which should be in IRAM) or garbage data to be read (for constant data which should be in DRAM). + +.. _flash-partition-apis: + +Partition table API +------------------- + +ESP-IDF projects use a partition table to maintain information about various regions of SPI flash memory (bootloader, various application binaries, data, filesystems). More information on partition tables can be found :doc:`here `. + +This component provides API functions to enumerate partitions found in the partition table and perform operations on them. These functions are declared in ``esp_partition.h``: + +- :cpp:func:`esp_partition_find` checks a partition table for entries with specific type, returns an opaque iterator. +- :cpp:func:`esp_partition_get` returns a structure describing the partition for a given iterator. +- :cpp:func:`esp_partition_next` shifts the iterator to the next found partition. +- :cpp:func:`esp_partition_iterator_release` releases iterator returned by ``esp_partition_find``. +- :cpp:func:`esp_partition_find_first` - a convenience function which returns the structure describing the first partition found by ``esp_partition_find``. +- :cpp:func:`esp_partition_read`, :cpp:func:`esp_partition_write`, :cpp:func:`esp_partition_erase_range` are equivalent to :cpp:func:`spi_flash_read`, :cpp:func:`spi_flash_write`, :cpp:func:`spi_flash_erase_range`, but operate within partition boundaries. + +.. note:: + Application code should mostly use these ``esp_partition_*`` API functions instead of lower level ``spi_flash_*`` API functions. Partition table API functions do bounds checking and calculate correct offsets in flash, based on data stored in a partition table. + +SPI Flash Encryption +-------------------- + +It is possible to encrypt the contents of SPI flash and have it transparently decrypted by hardware. + +Refer to the :doc:`Flash Encryption documentation ` for more details. + +Memory mapping API +------------------ + +ESP32 features memory hardware which allows regions of flash memory to be mapped into instruction and data address spaces. This mapping works only for read operations. It is not possible to modify contents of flash memory by writing to a mapped memory region. + +Mapping happens in 64KB pages. Memory mapping hardware can map up to four megabytes of flash into data address space and up to 16 megabytes of flash into instruction address space. See the technical reference manual for more details about memory mapping hardware. + +Note that some 64KB pages are used to map the application itself into memory, so the actual number of available 64KB pages may be less. + +Reading data from flash using a memory mapped region is the only way to decrypt contents of flash when :doc:`flash encryption ` is enabled. Decryption is performed at the hardware level. + +Memory mapping API are declared in ``esp_spi_flash.h`` and ``esp_partition.h``: + +- :cpp:func:`spi_flash_mmap` maps a region of physical flash addresses into instruction space or data space of the CPU. +- :cpp:func:`spi_flash_munmap` unmaps previously mapped region. +- :cpp:func:`esp_partition_mmap` maps part of a partition into the instruction space or data space of the CPU. + +Differences between :cpp:func:`spi_flash_mmap` and :cpp:func:`esp_partition_mmap` are as follows: + +- :cpp:func:`spi_flash_mmap` must be given a 64KB aligned physical address. +- :cpp:func:`esp_partition_mmap` may be given any arbitrary offset within the partition, it will adjust the returned pointer to mapped memory as necessary + +Note that since memory mapping happens in 64KB blocks, it may be possible to read data outside of the partition provided to ``esp_partition_mmap``. diff --git a/components/spi_flash/component.mk b/components/spi_flash/component.mk index 2ff786e2d5..64ab7548d7 100644 --- a/components/spi_flash/component.mk +++ b/components/spi_flash/component.mk @@ -1,4 +1,5 @@ COMPONENT_ADD_INCLUDEDIRS := include +COMPONENT_PRIV_INCLUDEDIRS := private_include COMPONENT_ADD_LDFRAGMENTS += linker.lf diff --git a/components/spi_flash/esp_flash_api.c b/components/spi_flash/esp_flash_api.c new file mode 100644 index 0000000000..3a48154c7b --- /dev/null +++ b/components/spi_flash/esp_flash_api.c @@ -0,0 +1,747 @@ +// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// +// 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 +#include +#include + +#include "spi_flash_chip_driver.h" +#include "memspi_host_driver.h" +#include "esp32/rom/spi_flash.h" +#include "esp_log.h" +#include "sdkconfig.h" +#include "esp_heap_caps.h" + +static const char TAG[] = "spi_flash"; + +#define MAX_WRITE_CHUNK 8192 /* write in chunks */ +#define MAX_READ_CHUNK 16384 + +#ifdef CONFIG_ESPTOOLPY_FLASHFREQ_80M +#define DEFAULT_FLASH_SPEED ESP_FLASH_80MHZ +#elif defined CONFIG_ESPTOOLPY_FLASHFREQ_40M +#define DEFAULT_FLASH_SPEED ESP_FLASH_40MHZ +#elif defined CONFIG_ESPTOOLPY_FLASHFREQ_26M +#define DEFAULT_FLASH_SPEED ESP_FLASH_26MHZ +#elif defined CONFIG_ESPTOOLPY_FLASHFREQ_20M +#define DEFAULT_FLASH_SPEED ESP_FLASH_20MHZ +#else +#error flash frequency not defined! check sdkconfig.h +#endif + +#if defined(CONFIG_ESPTOOLPY_FLASHMODE_QIO) +#define DEFAULT_FLASH_MODE SPI_FLASH_QIO +#elif defined(CONFIG_ESPTOOLPY_FLASHMODE_QOUT) +#define DEFAULT_FLASH_MODE SPI_FLASH_QOUT +#elif defined(CONFIG_ESPTOOLPY_FLASHMODE_DIO) +#define DEFAULT_FLASH_MODE SPI_FLASH_DIO +#elif defined(CONFIG_ESPTOOLPY_FLASHMODE_DOUT) +#define DEFAULT_FLASH_MODE SPI_FLASH_DOUT +#else +#define DEFAULT_FLASH_MODE SPI_FLASH_FASTRD +#endif + +#ifdef CONFIG_SPI_FLASH_DANGEROUS_WRITE_ABORTS +#define UNSAFE_WRITE_ADDRESS abort() +#else +#define UNSAFE_WRITE_ADDRESS return ESP_ERR_INVALID_ARG +#endif + +/* CHECK_WRITE_ADDRESS macro to fail writes which land in the + bootloader, partition table, or running application region. +*/ +#if CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED +#define CHECK_WRITE_ADDRESS(CHIP, ADDR, SIZE) +#else /* FAILS or ABORTS */ +#define CHECK_WRITE_ADDRESS(CHIP, ADDR, SIZE) do { \ + if (CHIP && CHIP->host->region_protected && CHIP->host->region_protected(CHIP->host, ADDR, SIZE)) { \ + UNSAFE_WRITE_ADDRESS; \ + } \ + } while(0) +#endif // CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED + +#define IO_STR_LEN 7 + +static const char io_mode_str[][IO_STR_LEN] = { + "slowrd", + "fastrd", + "dout", + "dio", + "qout", + "qio", +}; + +_Static_assert(sizeof(io_mode_str)/IO_STR_LEN == SPI_FLASH_READ_MODE_MAX, "the io_mode_str should be consistent with the esp_flash_read_mode_t defined in spi_flash_ll.h"); + + +/* Static function to notify OS of a new SPI flash operation. + + If returns an error result, caller must abort. If returns ESP_OK, caller must + call spiflash_end() before returning. +*/ +static esp_err_t IRAM_ATTR spiflash_start(esp_flash_t *chip) +{ + if (chip->os_func != NULL && chip->os_func->start != NULL) { + esp_err_t err = chip->os_func->start(chip->os_func_data); + if (err != ESP_OK) { + return err; + } + } + chip->host->dev_config(chip->host); + return ESP_OK; +} + +/* Static function to notify OS that SPI flash operation is complete. + */ +static esp_err_t IRAM_ATTR spiflash_end(const esp_flash_t *chip, esp_err_t err) +{ + if (chip->os_func != NULL + && chip->os_func->end != NULL) { + esp_err_t end_err = chip->os_func->end(chip->os_func_data); + if (err == ESP_OK) { + err = end_err; // Only return the 'end' error if we haven't already failed + } + } + return err; +} + +/* Return true if regions 'a' and 'b' overlap at all, based on their start offsets and lengths. */ +inline static bool regions_overlap(uint32_t a_start, uint32_t a_len,uint32_t b_start, uint32_t b_len); + +/* Top-level API functions, calling into chip_drv functions via chip->drv */ + +static esp_err_t detect_spi_flash_chip(esp_flash_t *chip); + +bool esp_flash_chip_driver_initialized(const esp_flash_t *chip) +{ + if (!chip->chip_drv) return false; + return true; +} + +esp_err_t IRAM_ATTR esp_flash_init(esp_flash_t *chip) +{ + esp_err_t err = ESP_OK; + if (chip == NULL || chip->host == NULL || chip->host->driver_data == NULL || + ((memspi_host_data_t*)chip->host->driver_data)->spi == NULL) { + return ESP_ERR_INVALID_ARG; + } + + if (!esp_flash_chip_driver_initialized(chip)) { + // Detect chip_drv + err = detect_spi_flash_chip(chip); + if (err != ESP_OK) { + return err; + } + } + + // Detect flash size + uint32_t size; + err = esp_flash_get_size(chip, &size); + if (err != ESP_OK) { + ESP_LOGE(TAG, "failed to get chip size"); + return err; + } + + ESP_LOGI(TAG, "flash io: %s", io_mode_str[chip->read_mode]); + err = spiflash_start(chip); + if (err != ESP_OK) { + return err; + } + + if (err == ESP_OK) { + // Try to set the flash mode to whatever default mode was chosen + err = chip->chip_drv->set_read_mode(chip); + } + // Done: all fields on 'chip' are initialised + return spiflash_end(chip, err); +} + +static esp_err_t IRAM_ATTR detect_spi_flash_chip(esp_flash_t *chip) +{ + esp_err_t err; + uint32_t flash_id; + int retries = 10; + do { + err = spiflash_start(chip); + if (err != ESP_OK) { + return err; + } + + // Send generic RDID command twice, check for a matching result and retry in case we just powered on (inner + // function fails if it sees all-ones or all-zeroes.) + err = chip->host->read_id(chip->host, &flash_id); + + if (err == ESP_OK) { // check we see the same ID twice, in case of transient power-on errors + uint32_t new_id; + err = chip->host->read_id(chip->host, &new_id); + if (err == ESP_OK && (new_id != flash_id)) { + err = ESP_ERR_FLASH_NOT_INITIALISED; + } + } + + err = spiflash_end(chip, err); + } while (err != ESP_OK && retries-- > 0); + + + // Detect the chip and set the chip_drv structure for it + const spi_flash_chip_t **drivers = esp_flash_registered_chips; + while (*drivers != NULL && !esp_flash_chip_driver_initialized(chip)) { + chip->chip_drv = *drivers; + // start/end SPI operation each time, for multitasking + // and also so esp_flash_registered_flash_drivers can live in flash + ESP_LOGD(TAG, "trying chip: %s", chip->chip_drv->name); + + err = spiflash_start(chip); + if (err != ESP_OK) { + return err; + } + + if (chip->chip_drv->probe(chip, flash_id) != ESP_OK) { + chip->chip_drv = NULL; + } + // if probe succeeded, chip->drv stays set + drivers++; + + err = spiflash_end(chip, err); + if (err != ESP_OK) { + return err; + } + } + if (!esp_flash_chip_driver_initialized(chip)) { + return ESP_ERR_NOT_FOUND; + } + ESP_LOGI(TAG, "detected chip: %s", chip->chip_drv->name); + return ESP_OK; +} + +// Convenience macro for beginning of all API functions, +// check that the 'chip' parameter is properly initialised +// and supports the operation in question +#define VERIFY_OP(OP) do { \ + if (chip == NULL) { \ + chip = esp_flash_default_chip; \ + } \ + if (chip == NULL || !esp_flash_chip_driver_initialized(chip)) { \ + return ESP_ERR_FLASH_NOT_INITIALISED; \ + } \ + if (chip->chip_drv->OP == NULL) { \ + return ESP_ERR_FLASH_UNSUPPORTED_CHIP; \ + } \ + } while (0) + +esp_err_t IRAM_ATTR esp_flash_read_id(esp_flash_t *chip, uint32_t *id) +{ + if (chip == NULL) { + chip = esp_flash_default_chip; + } + if (chip == NULL || !esp_flash_chip_driver_initialized(chip)) { + return ESP_ERR_FLASH_NOT_INITIALISED; + } + if (id == NULL) { + return ESP_ERR_INVALID_ARG; + } + esp_err_t err = spiflash_start(chip); + if (err != ESP_OK) { + return err; + } + + err = chip->host->read_id(chip->host, id); + + return spiflash_end(chip, err); +} + +esp_err_t IRAM_ATTR esp_flash_get_size(esp_flash_t *chip, uint32_t *size) +{ + VERIFY_OP(detect_size); + if (size == NULL) { + return ESP_ERR_INVALID_ARG; + } + if (chip->size != 0) { + *size = chip->size; + return ESP_OK; + } + + esp_err_t err = spiflash_start(chip); + if (err != ESP_OK) { + return err; + } + uint32_t detect_size; + err = chip->chip_drv->detect_size(chip, &detect_size); + if (err == ESP_OK) { + chip->size = detect_size; + } + return spiflash_end(chip, err); +} + +esp_err_t IRAM_ATTR esp_flash_erase_chip(esp_flash_t *chip) +{ + VERIFY_OP(erase_chip); + CHECK_WRITE_ADDRESS(chip, 0, chip->size); + bool write_protect = false; + + esp_err_t err = spiflash_start(chip); + if (err != ESP_OK) { + return err; + } + + err = esp_flash_get_chip_write_protect(chip, &write_protect); + + if (err == ESP_OK && write_protect) { + err = ESP_ERR_FLASH_PROTECTED; + } + + if (err == ESP_OK) { + err = chip->chip_drv->erase_chip(chip); + } + + return spiflash_end(chip, err); +} + +esp_err_t IRAM_ATTR esp_flash_erase_region(esp_flash_t *chip, uint32_t start, uint32_t len) +{ + VERIFY_OP(erase_sector); + VERIFY_OP(erase_block); + CHECK_WRITE_ADDRESS(chip, start, len); + uint32_t block_erase_size = chip->chip_drv->erase_block == NULL ? 0 : chip->chip_drv->block_erase_size; + uint32_t sector_size = chip->chip_drv->sector_size; + bool write_protect = false; + + if (sector_size == 0 || (block_erase_size % sector_size) != 0) { + return ESP_ERR_FLASH_NOT_INITIALISED; + } + if (start > chip->size || start + len > chip->size) { + return ESP_ERR_INVALID_ARG; + } + if ((start % chip->chip_drv->sector_size) != 0 || (len % chip->chip_drv->sector_size) != 0) { + // Can only erase multiples of the sector size, starting at sector boundary + return ESP_ERR_INVALID_ARG; + } + + esp_err_t err = spiflash_start(chip); + if (err != ESP_OK) { + return err; + } + + // Check for write protection on whole chip + if (chip->chip_drv->get_chip_write_protect != NULL) { + err = chip->chip_drv->get_chip_write_protect(chip, &write_protect); + if (err == ESP_OK && write_protect) { + err = ESP_ERR_FLASH_PROTECTED; + } + } + + // Check for write protected regions overlapping the erase region + if (err == ESP_OK && chip->chip_drv->get_protected_regions != NULL && chip->chip_drv->num_protectable_regions > 0) { + uint64_t protected = 0; + err = chip->chip_drv->get_protected_regions(chip, &protected); + if (err == ESP_OK && protected != 0) { + for (int i = 0; i < chip->chip_drv->num_protectable_regions && err == ESP_OK; i++) { + const esp_flash_region_t *region = &chip->chip_drv->protectable_regions[i]; + if ((protected & BIT64(i)) + && regions_overlap(start, len, region->offset, region->size)) { + err = ESP_ERR_FLASH_PROTECTED; + } + } + } + } + + // Don't lock the SPI flash for the entire erase, as this may be very long + err = spiflash_end(chip, err); + + while (err == ESP_OK && len >= sector_size) { + err = spiflash_start(chip); + if (err != ESP_OK) { + return err; + } + + // If possible erase an entire multi-sector block + if (block_erase_size > 0 && len >= block_erase_size && (start % block_erase_size) == 0) { + err = chip->chip_drv->erase_block(chip, start); + start += block_erase_size; + len -= block_erase_size; + } + else { + // Otherwise erase individual sector only + err = chip->chip_drv->erase_sector(chip, start); + start += sector_size; + len -= sector_size; + } + + err = spiflash_end(chip, err); + } + return err; +} + +esp_err_t IRAM_ATTR esp_flash_get_chip_write_protect(esp_flash_t *chip, bool *write_protected) +{ + VERIFY_OP(get_chip_write_protect); + if (write_protected == NULL) { + return ESP_ERR_INVALID_ARG; + } + + esp_err_t err = spiflash_start(chip); + if (err != ESP_OK) { + return err; + } + + err = chip->chip_drv->get_chip_write_protect(chip, write_protected); + + return spiflash_end(chip, err); +} + +esp_err_t IRAM_ATTR esp_flash_set_chip_write_protect(esp_flash_t *chip, bool write_protect_chip) +{ + VERIFY_OP(set_chip_write_protect); + //TODO: skip writing if already locked or unlocked + + esp_err_t err = spiflash_start(chip); + if (err != ESP_OK) { + return err; + } + + err = chip->chip_drv->set_chip_write_protect(chip, write_protect_chip); + + return spiflash_end(chip, err); +} + +esp_err_t esp_flash_get_protectable_regions(const esp_flash_t *chip, const esp_flash_region_t **regions, uint32_t *num_regions) +{ + if(num_regions != NULL) { + *num_regions = 0; // In case caller doesn't check result + } + VERIFY_OP(get_protected_regions); + + if(regions == NULL || num_regions == NULL) { + return ESP_ERR_INVALID_ARG; + } + + *num_regions = chip->chip_drv->num_protectable_regions; + *regions = chip->chip_drv->protectable_regions; + return ESP_OK; +} + +static esp_err_t find_region(const esp_flash_t *chip, const esp_flash_region_t *region, uint8_t *index) +{ + if (region == NULL) { + return ESP_ERR_INVALID_ARG; + } + + for(*index = 0; *index < chip->chip_drv->num_protectable_regions; (*index)++) { + if (memcmp(&chip->chip_drv->protectable_regions[*index], + region, sizeof(esp_flash_region_t)) == 0) { + return ESP_OK; + } + } + + return ESP_ERR_NOT_FOUND; +} + +esp_err_t IRAM_ATTR esp_flash_get_protected_region(esp_flash_t *chip, const esp_flash_region_t *region, bool *protected) +{ + VERIFY_OP(get_protected_regions); + + if (protected == NULL) { + return ESP_ERR_INVALID_ARG; + } + + uint8_t index; + esp_err_t err = find_region(chip, region, &index); + if (err != ESP_OK) { + return err; + } + + uint64_t protection_mask = 0; + err = spiflash_start(chip); + if (err != ESP_OK) { + return err; + } + + err = chip->chip_drv->get_protected_regions(chip, &protection_mask); + if (err == ESP_OK) { + *protected = protection_mask & (1LL << index); + } + + return spiflash_end(chip, err); +} + +esp_err_t IRAM_ATTR esp_flash_set_protected_region(esp_flash_t *chip, const esp_flash_region_t *region, bool protected) +{ + VERIFY_OP(set_protected_regions); + + uint8_t index; + esp_err_t err = find_region(chip, region, &index); + if (err != ESP_OK) { + return err; + } + + uint64_t protection_mask = 0; + err = spiflash_start(chip); + if (err != ESP_OK) { + return err; + } + + err = chip->chip_drv->get_protected_regions(chip, &protection_mask); + if (err == ESP_OK) { + if (protected) { + protection_mask |= (1LL << index); + } else { + protection_mask &= ~(1LL << index); + } + err = chip->chip_drv->set_protected_regions(chip, protection_mask); + } + + return spiflash_end(chip, err); +} + +esp_err_t IRAM_ATTR esp_flash_read(esp_flash_t *chip, void *buffer, uint32_t address, uint32_t length) +{ + if (length == 0) { + return ESP_OK; + } + VERIFY_OP(read); + if (buffer == NULL || address > chip->size || address+length > chip->size) { + return ESP_ERR_INVALID_ARG; + } + + //when the cache is disabled, only the DRAM can be read, check whether we need to receive in another buffer in DRAM. + bool direct_read = chip->host->supports_direct_read(chip->host, buffer); + uint8_t* temp_buffer = NULL; + + if (!direct_read) { + uint32_t length_to_allocate = MAX(MAX_READ_CHUNK, length); + length_to_allocate = (length_to_allocate+3)&(~3); + temp_buffer = heap_caps_malloc(length_to_allocate, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); + ESP_LOGV(TAG, "allocate temp buffer: %p", temp_buffer); + if (temp_buffer == NULL) return ESP_ERR_NO_MEM; + } + + esp_err_t err = ESP_OK; + + do { + err = spiflash_start(chip); + if (err != ESP_OK) { + break; + } + //if required (dma buffer allocated), read to the buffer instead of the original buffer + uint8_t* buffer_to_read = (temp_buffer)? temp_buffer : buffer; + //each time, we at most read this length + //after that, we release the lock to allow some other operations + uint32_t length_to_read = MIN(MAX_READ_CHUNK, length); + + if (err == ESP_OK) { + err = chip->chip_drv->read(chip, buffer_to_read, address, length_to_read); + } + if (err != ESP_OK) { + spiflash_end(chip, err); + break; + } + //even if this is failed, the data is still valid, copy before quit + err = spiflash_end(chip, err); + + //copy back to the original buffer + if (temp_buffer) { + memcpy(buffer, temp_buffer, length_to_read); + } + address += length_to_read; + length -= length_to_read; + buffer += length_to_read; + } while (err == ESP_OK && length > 0); + + free(temp_buffer); + return err; +} + +esp_err_t IRAM_ATTR esp_flash_write(esp_flash_t *chip, const void *buffer, uint32_t address, uint32_t length) +{ + if (length == 0) { + return ESP_OK; + } + VERIFY_OP(write); + CHECK_WRITE_ADDRESS(chip, address, length); + if (buffer == NULL || address > chip->size || address+length > chip->size) { + return ESP_ERR_INVALID_ARG; + } + + //when the cache is disabled, only the DRAM can be read, check whether we need to copy the data first + bool direct_write = chip->host->supports_direct_write(chip->host, buffer); + + esp_err_t err = ESP_OK; + /* Write output in chunks, either by buffering on stack or + by artificially cutting into MAX_WRITE_CHUNK parts (in an OS + environment, this prevents writing from causing interrupt or higher priority task + starvation.) */ + do { + uint32_t write_len; + const void *write_buf; + if (direct_write) { + write_len = MIN(length, MAX_WRITE_CHUNK); + write_buf = buffer; + } else { + uint32_t buf[8]; + write_len = MIN(length, sizeof(buf)); + memcpy(buf, buffer, write_len); + write_buf = buf; + } + + err = spiflash_start(chip); + if (err != ESP_OK) { + return err; + } + + err = chip->chip_drv->write(chip, write_buf, address, write_len); + + address += write_len; + buffer = (void *)((intptr_t)buffer + write_len); + length -= write_len; + + err = spiflash_end(chip, err); + } while (err == ESP_OK && length > 0); + return err; +} + +esp_err_t IRAM_ATTR esp_flash_write_encrypted(esp_flash_t *chip, uint32_t address, const void *buffer, uint32_t length) +{ + VERIFY_OP(write_encrypted); + if (((memspi_host_data_t*)chip->host->driver_data)->spi != 0) { + // Encrypted operations have to use SPI0 + return ESP_ERR_FLASH_UNSUPPORTED_HOST; + } + if (buffer == NULL || address > chip->size || address+length > chip->size) { + return ESP_ERR_INVALID_ARG; + } + + esp_err_t err = spiflash_start(chip); + if (err != ESP_OK) { + return err; + } + + err = chip->chip_drv->write_encrypted(chip, buffer, address, length); + + return spiflash_end(chip, err); +} + + +inline static IRAM_ATTR bool regions_overlap(uint32_t a_start, uint32_t a_len,uint32_t b_start, uint32_t b_len) +{ + uint32_t a_end = a_start + a_len; + uint32_t b_end = b_start + b_len; + return (a_end > b_start && b_end > a_start); +} + +#define ESP_FLASH_HOST_CONFIG_DEFAULT() (memspi_host_config_t){ \ + .host_id = 1,\ + .speed = DEFAULT_FLASH_SPEED, \ + .cs_num = 0, \ + .iomux = true, \ + .input_delay_ns = 25,\ +} + +static DRAM_ATTR spi_flash_host_driver_t esp_flash_default_host_drv = ESP_FLASH_DEFAULT_HOST_DRIVER(); + +static DRAM_ATTR memspi_host_data_t default_driver_data; + +/* The default (ie initial boot) no-OS ROM esp_flash_os_functions_t */ +extern const esp_flash_os_functions_t esp_flash_noos_functions; + +static DRAM_ATTR esp_flash_t default_chip = { + .read_mode = DEFAULT_FLASH_MODE, + .host = &esp_flash_default_host_drv, + .os_func = &esp_flash_noos_functions, +}; + +esp_flash_t *esp_flash_default_chip = &default_chip; + +esp_err_t esp_flash_init_default_chip() +{ + memspi_host_config_t cfg = ESP_FLASH_HOST_CONFIG_DEFAULT(); + //the host is already initialized, only do init for the data and load it to the host + spi_flash_hal_init(&default_driver_data, &cfg); + default_chip.host->driver_data = &default_driver_data; + + // ROM TODO: account for non-standard default pins in efuse + // ROM TODO: to account for chips which are slow to power on, maybe keep probing in a loop here + esp_err_t err = esp_flash_init(&default_chip); + if (err != ESP_OK) { + return err; + } + if (default_chip.size < g_rom_flashchip.chip_size) { + ESP_EARLY_LOGE(TAG, "detected size(%dk) smaller than the size in the binary image header(%dk). probe failed.", default_chip.size/1024, g_rom_flashchip.chip_size/1024); + return ESP_ERR_FLASH_SIZE_NOT_MATCH; + } else if (default_chip.size > g_rom_flashchip.chip_size) { + ESP_EARLY_LOGW(TAG, "detected size larger than the size in the binary image header. use the size in the binary image header."); + default_chip.size = g_rom_flashchip.chip_size; + } + default_chip.size = g_rom_flashchip.chip_size; + + esp_flash_default_chip = &default_chip; + return ESP_OK; +} + + +/*------------------------------------------------------------------------------ + Adapter layer to original api before IDF v4.0 +------------------------------------------------------------------------------*/ + +static esp_err_t spi_flash_translate_rc(esp_err_t err) +{ + switch (err) { + case ESP_OK: + return ESP_OK; + case ESP_ERR_INVALID_ARG: + return ESP_ERR_INVALID_ARG; + case ESP_ERR_FLASH_NOT_INITIALISED: + case ESP_ERR_FLASH_PROTECTED: + return ESP_ERR_INVALID_STATE; + case ESP_ERR_NOT_FOUND: + case ESP_ERR_FLASH_UNSUPPORTED_HOST: + case ESP_ERR_FLASH_UNSUPPORTED_CHIP: + return ESP_ERR_NOT_SUPPORTED; + case ESP_ERR_FLASH_NO_RESPONSE: + return ESP_ERR_INVALID_RESPONSE; + default: + ESP_EARLY_LOGE(TAG, "unexpected spi flash error code: %x", err); + abort(); + } + return ESP_OK; +} + +#ifndef CONFIG_SPI_FLASH_USE_LEGACY_IMPL +esp_err_t spi_flash_erase_range(uint32_t start_addr, uint32_t size) +{ + esp_err_t err = esp_flash_erase_region(NULL, start_addr, size); + return spi_flash_translate_rc(err); +} + +esp_err_t spi_flash_write(size_t dst, const void *srcv, size_t size) +{ + esp_err_t err = esp_flash_write(NULL, srcv, dst, size); + return spi_flash_translate_rc(err); + + //CHECK_WRITE_ADDRESS(dst, size); +} + +esp_err_t spi_flash_read(size_t src, void *dstv, size_t size) +{ + esp_err_t err = esp_flash_read(NULL, dstv, src, size); + return spi_flash_translate_rc(err); +} + +esp_err_t spi_flash_unlock() +{ + esp_err_t err = esp_flash_set_chip_write_protect(NULL, false); + return spi_flash_translate_rc(err); +} + +#endif diff --git a/components/spi_flash/flash_ops.c b/components/spi_flash/flash_ops.c index 7b3f5b1f8a..dcfc6cc4cb 100644 --- a/components/spi_flash/flash_ops.c +++ b/components/spi_flash/flash_ops.c @@ -33,8 +33,8 @@ #include "esp_log.h" #include "esp32/clk.h" #include "esp_flash_partitions.h" -#include "esp_ota_ops.h" #include "cache_utils.h" +#include "esp_flash.h" /* bytes erased by SPIEraseBlock() ROM function */ #define BLOCK_ERASE_SIZE 65536 @@ -116,20 +116,10 @@ static const spi_flash_guard_funcs_t *s_flash_guard_ops; static __attribute__((unused)) bool is_safe_write_address(size_t addr, size_t size) { - bool result = true; - if (addr <= ESP_PARTITION_TABLE_OFFSET + ESP_PARTITION_TABLE_MAX_LEN) { + if (!esp_partition_main_flash_region_safe(addr, size)) { UNSAFE_WRITE_ADDRESS; } - - const esp_partition_t *p = esp_ota_get_running_partition(); - if (addr >= p->address && addr < p->address + p->size) { - UNSAFE_WRITE_ADDRESS; - } - if (addr < p->address && addr + size > p->address) { - UNSAFE_WRITE_ADDRESS; - } - - return result; + return true; } @@ -184,6 +174,7 @@ static inline void IRAM_ATTR spi_flash_guard_op_unlock() } } +#ifdef CONFIG_SPI_FLASH_USE_LEGACY_IMPL static esp_rom_spiflash_result_t IRAM_ATTR spi_flash_unlock() { static bool unlocked = false; @@ -198,6 +189,16 @@ static esp_rom_spiflash_result_t IRAM_ATTR spi_flash_unlock() } return ESP_ROM_SPIFLASH_RESULT_OK; } +#else +static esp_rom_spiflash_result_t IRAM_ATTR spi_flash_unlock() +{ + esp_err_t err = esp_flash_set_chip_write_protect(NULL, false); + if (err != ESP_OK) { + return ESP_ROM_SPIFLASH_RESULT_ERR; + } + return ESP_ROM_SPIFLASH_RESULT_OK; +} +#endif esp_err_t IRAM_ATTR spi_flash_erase_sector(size_t sec) { @@ -205,6 +206,8 @@ esp_err_t IRAM_ATTR spi_flash_erase_sector(size_t sec) return spi_flash_erase_range(sec * SPI_FLASH_SEC_SIZE, SPI_FLASH_SEC_SIZE); } +#ifdef CONFIG_SPI_FLASH_USE_LEGACY_IMPL +//deprecated, only used in compatible mode esp_err_t IRAM_ATTR spi_flash_erase_range(uint32_t start_addr, uint32_t size) { CHECK_WRITE_ADDRESS(start_addr, size); @@ -416,6 +419,7 @@ out: return spi_flash_translate_rc(rc); } +#endif esp_err_t IRAM_ATTR spi_flash_write_encrypted(size_t dest_addr, const void *src, size_t size) { @@ -483,6 +487,7 @@ esp_err_t IRAM_ATTR spi_flash_write_encrypted(size_t dest_addr, const void *src, return spi_flash_translate_rc(rc); } +#ifdef CONFIG_SPI_FLASH_USE_LEGACY_IMPL esp_err_t IRAM_ATTR spi_flash_read(size_t src, void *dstv, size_t size) { // Out of bound reads are checked in ROM code, but we can give better @@ -624,6 +629,7 @@ out: COUNTER_STOP(read); return spi_flash_translate_rc(rc); } +#endif esp_err_t IRAM_ATTR spi_flash_read_encrypted(size_t src, void *dstv, size_t size) { diff --git a/components/spi_flash/include/spi_flash_lowlevel.h b/components/spi_flash/include/esp_flash.h similarity index 54% rename from components/spi_flash/include/spi_flash_lowlevel.h rename to components/spi_flash/include/esp_flash.h index 0b4ebf9a31..904b09b493 100644 --- a/components/spi_flash/include/spi_flash_lowlevel.h +++ b/components/spi_flash/include/esp_flash.h @@ -1,31 +1,28 @@ +// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// +// 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 "esp_err.h" #include #include -#include "soc/spi_struct.h" +#include "hal/spi_flash_host_drv.h" -struct esp_flash_driver; +struct spi_flash_chip_t; +typedef struct spi_flash_chip_t spi_flash_chip_t; -/** @brief Mode used for reading from SPI flash */ -typedef enum { - ESP_FLASH_QIO, ///< Both address & data transferred using quad I/O - ESP_FLASH_QOUT, ///< Data read using quad I/O - ESP_FLASH_DIO, ///< Both address & data transferred using dual I/O - ESP_FLASH_DOUT, ///< Data read using dual I/O - ESP_FLASH_FASTRD, ///< Data read using single I/O, no limit on speed - ESP_FLASH_SLOWRD, ///< Data read using single I/O, some limits on speed - - ESP_FLASH_READ_MODE_MAX, -} esp_flash_read_mode_t; - -/** @brief Configured SPI flash clock speed */ -typedef enum { - ESP_FLASH_80MHZ, - ESP_FLASH_40MHZ, - ESP_FLASH_26MHZ, - ESP_FLASH_20MHZ, - ESP_FLASH_SPEED_MAX, -} esp_flash_speed_t; +typedef struct esp_flash_t esp_flash_t; /** @brief Structure for describing a region of flash */ typedef struct { @@ -33,56 +30,60 @@ typedef struct { uint32_t size; } esp_flash_region_t; -// TODO this is copied from SPI driver, should be unified somehow +/* OS-level integration hooks for accessing flash chips inside a running OS */ typedef struct { - int mosi_io_num; ///< GPIO pin for Master Out Slave In (=spi_d) signal - int miso_io_num; ///< GPIO pin for Master In Slave Out (=spi_q) signal - int sclk_io_num; ///< GPIO pin for Spi CLocK signal - int quadwp_io_num; ///< GPIO pin for WP (Write Protect) signal which is used as D2 in 4-bit communication modes, or -1 if not used. - int quadhd_io_num; ///< GPIO pin for HD (HolD) signal which is used as D3 in 4-bit communication modes, or -1 if not used. -} esp_flash_pin_cfg_t; + /** + * Called before commencing any flash operation. Does not need to be + * recursive (ie is called at most once for each call to 'end'). + */ + esp_err_t (*start)(void *arg); + + /** Called after completing any flash operation. */ + esp_err_t (*end)(void *arg); + + /** Delay for at least 'ms' milliseconds. Called in between 'start' and 'end'. */ + esp_err_t (*delay_ms)(void *arg, unsigned ms); +} esp_flash_os_functions_t; /** @brief Structure to describe a SPI flash chip connected to the system. Structure must be passed to esp_flash_init() before use. */ -typedef struct { - spi_dev_t *spi; ///< Pointer to hardware SPI bus registers used for connection (SP1, SPI2 or SPI3). Set before initialisation. - esp_flash_speed_t speed; ///< Configured SPI flash clock speed. Set before initialisation. +struct esp_flash_t { + const spi_flash_chip_t *chip_drv; ///< Pointer to chip-model-specific "adpater" structure. If NULL, will be detected during initialisatiopn. + spi_flash_host_driver_t *host; ///< Pointer to hardware-specific "host_driver" structure. + + const esp_flash_os_functions_t *os_func; ///< Pointer to os-specific hooker strcuture. + void *os_func_data; ///< Pointer to argument for os-specific hooker. + esp_flash_read_mode_t read_mode; ///< Configured SPI flash read mode. Set before initialisation. uint32_t size; ///< Size of SPI flash in bytes. If 0, size will be detected during initialisation. - const struct esp_flash_driver *drv; ///< Pointer to chip-model-specific "driver" structure. If NULL, will be detected during initialisatiopn. - const esp_flash_pin_cfg_t *pins; ///< Pin configuration for the chip +}; - void *driver_data; ///< Currently unused, allows drivers to store driver-implementation-specific data on initialisation -} esp_flash_chip_t; - -/** @brief Possible errors returned from SPI flash low-level API */ -typedef enum { - FLASH_OK = 0, ///< Success - FLASH_ERR_NOT_INITIALISED, ///< esp_flash_chip_t structure not correctly initialised by esp_flash_init(). - FLASH_ERR_INVALID_ARG, ///< A supplied argument was invalid. - FLASH_ERR_NOT_FOUND, ///< A requested value is not found. - FLASH_ERR_NO_RESPONSE, ///< Chip did not respond to the command, or timed out. - FLASH_ERR_UNSUPPORTED_HOST, ///< Requested operation isn't supported via this host SPI bus (chip->spi field). - FLASH_ERR_UNSUPPORTED_CHIP, ///< Requested operation isn't supported by this model of SPI flash chip. - FLASH_ERR_PROTECTED, ///< Write operation failed due to chip's write protection being enabled. -} esp_flash_err_t; /** @brief Initialise SPI flash chip interface. - * + * * This function must be called before any other API functions are called for this chip. * * @note Only the spi, speed & read_mode fields of the chip structure need to be initialised. Other fields will be auto-detected * if left set to zero or NULL. * - * @note If the chip->drv pointer is NULL, chip driver will be autodetected based on its manufacturer & product IDs. See + * @note If the chip->drv pointer is NULL, chip chip_drv will be autodetected based on its manufacturer & product IDs. See * esp_flash_registered_flash_drivers pointer for details of this process. * * @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted. - * @return FLASH_OK on success, or a flash error code if initialisation fails. + * @return ESP_OK on success, or a flash error code if initialisation fails. */ -esp_flash_err_t esp_flash_init(esp_flash_chip_t *chip); +esp_err_t esp_flash_init(esp_flash_t *chip); + +/** + * Check if appropriate chip driver is set. + * + * @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted. + * + * @return true if set, otherwise false. + */ +bool esp_flash_chip_driver_initialized(const esp_flash_t *chip); /** @brief Read flash ID via the common "RDID" SPI flash command. * @@ -91,9 +92,9 @@ esp_flash_err_t esp_flash_init(esp_flash_chip_t *chip); * * ID is a 24-bit value. Lower 16 bits of 'id' are the chip ID, upper 8 bits are the manufacturer ID. * - * @return FLASH_OK on success, or a flash error code if operation failed. + * @return ESP_OK on success, or a flash error code if operation failed. */ -esp_flash_err_t esp_flash_read_id(const esp_flash_chip_t *chip, uint32_t *id); +esp_err_t esp_flash_read_id(esp_flash_t *chip, uint32_t *id); /** @brief Detect flash size based on flash ID. * @@ -103,18 +104,18 @@ esp_flash_err_t esp_flash_read_id(const esp_flash_chip_t *chip, uint32_t *id); * @note Most flash chips use a common format for flash ID, where the lower 4 bits specify the size as a power of 2. If * the manufacturer doesn't follow this convention, the size may be incorrectly detected. * - * @return FLASH_OK on success, or a flash error code if operation failed. + * @return ESP_OK on success, or a flash error code if operation failed. */ -esp_flash_err_t esp_flash_detect_size(const esp_flash_chip_t *chip, uint32_t *size); +esp_err_t esp_flash_get_size(esp_flash_t *chip, uint32_t *size); /** @brief Erase flash chip contents * * @param chip Pointer to identify flash chip. Must have been successfully initialised via esp_flash_init() * * - * @return FLASH_OK on success, or a flash error code if operation failed. + * @return ESP_OK on success, or a flash error code if operation failed. */ -esp_flash_err_t esp_flash_erase_chip(const esp_flash_chip_t *chip); +esp_err_t esp_flash_erase_chip(esp_flash_t *chip); /** @brief Erase a region of the flash chip * @@ -122,45 +123,45 @@ esp_flash_err_t esp_flash_erase_chip(const esp_flash_chip_t *chip); * @param start Address to start erasing flash. Must be sector aligned. * @param len Length of region to erase. Must also be sector aligned. * - * Sector size is specifyed in chip->drv->sector_size field (typically 4096 bytes.) FLASH_ERR_INVALID_ARG will be + * Sector size is specifyed in chip->drv->sector_size field (typically 4096 bytes.) ESP_ERR_INVALID_ARG will be * returned if the start & length are not a multiple of this size. * * Erase is performed using block (multi-sector) erases where possible (block size is specified in * chip->drv->block_erase_size field, typically 65536 bytes). Remaining sectors are erased using individual sector erase * commands. * - * @return FLASH_OK on success, or a flash error code if operation failed. + * @return ESP_OK on success, or a flash error code if operation failed. */ -esp_flash_err_t esp_flash_erase_region(const esp_flash_chip_t *chip, uint32_t start, uint32_t len); +esp_err_t esp_flash_erase_region(esp_flash_t *chip, uint32_t start, uint32_t len); /** @brief Read if the entire chip is write protected * * @param chip Pointer to identify flash chip. Must have been successfully initialised via esp_flash_init() * @param[out] write_protected Pointer to boolean, set to the value of the write protect flag. * - * @note A correct result for this flag depends on the SPI flash chip model and driver in use (via the 'chip->drv' + * @note A correct result for this flag depends on the SPI flash chip model and chip_drv in use (via the 'chip->drv' * field). * - * @return FLASH_OK on success, or a flash error code if operation failed. + * @return ESP_OK on success, or a flash error code if operation failed. */ -esp_flash_err_t esp_flash_get_chip_write_protect(const esp_flash_chip_t *chip, bool *write_protected); +esp_err_t esp_flash_get_chip_write_protect(esp_flash_t *chip, bool *write_protected); /** @brief Set write protection for the SPI flash chip * * @param chip Pointer to identify flash chip. Must have been successfully initialised via esp_flash_init() * @param write_protected Boolean value for the write protect flag * - * @note Correct behaviour of this function depends on the SPI flash chip model and driver in use (via the 'chip->drv' + * @note Correct behaviour of this function depends on the SPI flash chip model and chip_drv in use (via the 'chip->drv' * field). * - * If write protection is enabled, destructive operations will fail with FLASH_ERR_PROTECTED. + * If write protection is enabled, destructive operations will fail with ESP_ERR_FLASH_PROTECTED. * * Some SPI flash chips may require a power cycle before write protect status can be cleared. Otherwise, * write protection can be removed via a follow-up call to this function. * - * @return FLASH_OK on success, or a flash error code if operation failed. + * @return ESP_OK on success, or a flash error code if operation failed. */ -esp_flash_err_t esp_flash_set_chip_write_protect(const esp_flash_chip_t *chip, bool write_protect_chip); +esp_err_t esp_flash_set_chip_write_protect(esp_flash_t *chip, bool write_protect_chip); /** @brief Read the list of individually protectable regions of this SPI flash chip. @@ -169,12 +170,13 @@ esp_flash_err_t esp_flash_set_chip_write_protect(const esp_flash_chip_t *chip, b * @param regions[out] Pointer to receive a pointer to the array of protectable regions of the chip. * @param[out] Pointer to an integer receiving the count of protectable regions in the array returned in 'regions'. * - * @note Correct behaviour of this function depends on the SPI flash chip model and driver in use (via the 'chip->drv' + * @note Correct behaviour of this function depends on the SPI flash chip model and chip_drv in use (via the 'chip->drv' * field). * - * @return FLASH_OK on success, or a flash error code if operation failed. + * @return ESP_OK on success, or a flash error code if operation failed. */ -esp_flash_err_t esp_flash_get_protectable_regions(const esp_flash_chip_t *chip, const esp_flash_region_t **regions, uint32_t *num_regions); +esp_err_t +esp_flash_get_protectable_regions(const esp_flash_t *chip, const esp_flash_region_t **regions, uint32_t *num_regions); /** @brief Detect if a region of the SPI flash chip is protected @@ -185,12 +187,12 @@ esp_flash_err_t esp_flash_get_protectable_regions(const esp_flash_chip_t *chip, * * @note It is possible for this result to be false and write operations to still fail, if protection is enabled for the entire chip. * - * @note Correct behaviour of this function depends on the SPI flash chip model and driver in use (via the 'chip->drv' + * @note Correct behaviour of this function depends on the SPI flash chip model and chip_drv in use (via the 'chip->drv' * field). * - * @return FLASH_OK on success, or a flash error code if operation failed. + * @return ESP_OK on success, or a flash error code if operation failed. */ -esp_flash_err_t esp_flash_get_protected_region(const esp_flash_chip_t *chip, const esp_flash_region_t *region, bool *protected); +esp_err_t esp_flash_get_protected_region(esp_flash_t *chip, const esp_flash_region_t *region, bool *protected); /** @brief Update the protected status for a region of the SPI flash chip * @@ -200,17 +202,17 @@ esp_flash_err_t esp_flash_get_protected_region(const esp_flash_chip_t *chip, con * * @note It is possible for the region protection flag to be cleared and write operations to still fail, if protection is enabled for the entire chip. * - * @note Correct behaviour of this function depends on the SPI flash chip model and driver in use (via the 'chip->drv' + * @note Correct behaviour of this function depends on the SPI flash chip model and chip_drv in use (via the 'chip->drv' * field). * - * @return FLASH_OK on success, or a flash error code if operation failed. + * @return ESP_OK on success, or a flash error code if operation failed. */ -esp_flash_err_t esp_flash_set_protected_region(const esp_flash_chip_t *chip, const esp_flash_region_t *region, bool protected); +esp_err_t esp_flash_set_protected_region(esp_flash_t *chip, const esp_flash_region_t *region, bool protected); /** @brief Read data from the SPI flash chip * * @param chip Pointer to identify flash chip. Must have been successfully initialised via esp_flash_init() - * @param buffer Pointer to a buffer where the data will be read. + * @param buffer Pointer to a buffer where the data will be read. To get better performance, this should be in the DRAM and word aligned. * @param address Address on flash to read from. Must be less than chip->size field. * @param length Length (in bytes) of data to read. * @@ -219,22 +221,26 @@ esp_flash_err_t esp_flash_set_protected_region(const esp_flash_chip_t *chip, con * @note If on-chip flash encryption is used, this function returns raw (ie encrypted) data. Use the flash cache * to transparently decrypt data. * - * @return FLASH_OK on success, or a flash error code if operation failed. + * @return + * - ESP_OK: success + * - ESP_ERR_NO_MEM: the buffer is not valid, however failed to malloc on + * the heap. + * - or a flash error code if operation failed. */ -esp_flash_err_t esp_flash_read(const esp_flash_chip_t *chip, void *buffer, uint32_t address, uint32_t length); +esp_err_t esp_flash_read(esp_flash_t *chip, void *buffer, uint32_t address, uint32_t length); /** @brief Write data to the SPI flash chip * * @param chip Pointer to identify flash chip. Must have been successfully initialised via esp_flash_init() * @param address Address on flash to write to. Must be previously erased (SPI NOR flash can only write bits 1->0). - * @param buffer Pointer to a buffer with the data to write. + * @param buffer Pointer to a buffer with the data to write. To get better performance, this should be in the DRAM and word aligned. * @param length Length (in bytes) of data to write. * * There are no alignment constraints on buffer, address or length. * - * @return FLASH_OK on success, or a flash error code if operation failed. + * @return ESP_OK on success, or a flash error code if operation failed. */ -esp_flash_err_t esp_flash_write(const esp_flash_chip_t *chip, uint32_t address, const void *buffer, uint32_t length); +esp_err_t esp_flash_write(esp_flash_t *chip, const void *buffer, uint32_t address, uint32_t length); /** @brief Encrypted and write data to the SPI flash chip using on-chip hardware flash encryption * @@ -245,21 +251,44 @@ esp_flash_err_t esp_flash_write(const esp_flash_chip_t *chip, uint32_t address, * * @note Both address & length must be 16 byte aligned, as this is the encryption block size * - * @return FLASH_OK on success, or a flash error code if operation failed. + * @return ESP_OK on success, or a flash error code if operation failed. */ -esp_flash_err_t esp_flash_write_encrypted(const esp_flash_chip_t *chip, uint32_t address, const void *buffer, uint32_t length); +esp_err_t esp_flash_write_encrypted(esp_flash_t *chip, uint32_t address, const void *buffer, uint32_t length); + /** @brief Pointer to the "default" SPI flash chip, ie the main chip attached to the MCU. This chip is used if the 'chip' argument pass to esp_flash_xxx API functions is ever NULL. */ -extern const esp_flash_chip_t *esp_flash_default_chip; +extern esp_flash_t *esp_flash_default_chip; /** @brief Initialise the default SPI flash chip * * Called by OS startup code. You do not need to call this in your own applications. */ -esp_flash_err_t esp_flash_init_default_chip(); +esp_err_t esp_flash_init_default_chip(); -/** Enable OS-level SPI flash protections in IDF */ -void esp_flash_low_level_app_init(); /* ROM TODO move this to IDF */ +/** + * Enable OS-level SPI flash protections in IDF + * + * @return ESP_OK if success, otherwise failed. See return value of ``esp_flash_init_os_functions``. + */ +esp_err_t esp_flash_app_init(); /* ROM TODO move this to IDF */ + +/** + * Enable OS-level SPI flash for a specific chip. + * + * @param chip The chip to init os functions. + * @param host_id Which SPI host to use, 0 for SPI1, 1 for HSPI2 and 2 for VSPI. + * + * @return ESP_OK if success, otherwise failed. See return value of ``esp_flash_init_os_functions``. + */ +esp_err_t esp_flash_init_os_functions(esp_flash_t *chip, int host_id); + +/* The default (ie initial boot) no-OS ROM esp_flash_os_functions_t */ +extern const esp_flash_os_functions_t spi1_default_os_functions; //todo: put into non-ROM headers + +/* Pointer to the current esp_flash_os_functions_t structure in use. + Can be changed at runtime to reflect different running conditions. + */ +//extern const esp_flash_os_functions_t *os_func; diff --git a/components/spi_flash/include/esp_spi_flash.h b/components/spi_flash/include/esp_spi_flash.h index 74c96fd36c..795142f4a1 100644 --- a/components/spi_flash/include/esp_spi_flash.h +++ b/components/spi_flash/include/esp_spi_flash.h @@ -25,7 +25,6 @@ extern "C" { #endif -#define ESP_ERR_FLASH_BASE 0x10010 #define ESP_ERR_FLASH_OP_FAIL (ESP_ERR_FLASH_BASE + 1) #define ESP_ERR_FLASH_OP_TIMEOUT (ESP_ERR_FLASH_BASE + 2) @@ -36,7 +35,7 @@ extern "C" { /** * @brief Initialize SPI flash access driver * - * This function must be called exactly once, before any other + * This function must be called exactly once, before any other * spi_flash_* functions are called. * Currently this function is called from startup code. There is * no need to call it from application code. diff --git a/components/spi_flash/include/memspi_host_driver.h b/components/spi_flash/include/memspi_host_driver.h new file mode 100644 index 0000000000..2347398aa1 --- /dev/null +++ b/components/spi_flash/include/memspi_host_driver.h @@ -0,0 +1,111 @@ +// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// +// 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 "hal/spi_flash_hal.h" + +/** Default configuration for the memspi (high speed version) */ +#define ESP_FLASH_DEFAULT_HOST_DRIVER() (spi_flash_host_driver_t) { \ + .dev_config = spi_flash_hal_device_config, \ + .common_command = spi_flash_hal_common_command, \ + .read_id = memspi_host_read_id_hs, \ + .erase_chip = spi_flash_hal_erase_chip, \ + .erase_sector = spi_flash_hal_erase_sector, \ + .erase_block = spi_flash_hal_erase_block, \ + .read_status = memspi_host_read_status_hs, \ + .set_write_protect = spi_flash_hal_set_write_protect, \ + .supports_direct_write = spi_flash_hal_supports_direct_write, \ + .supports_direct_read = spi_flash_hal_supports_direct_read, \ + .program_page = spi_flash_hal_program_page, \ + .max_write_bytes = SPI_FLASH_HAL_MAX_WRITE_BYTES, \ + .read = spi_flash_hal_read, \ + .max_read_bytes = SPI_FLASH_HAL_MAX_READ_BYTES, \ + .host_idle = spi_flash_hal_host_idle, \ + .configure_host_read_mode = spi_flash_hal_configure_host_read_mode, \ + .poll_cmd_done = spi_flash_hal_poll_cmd_done, \ + .flush_cache = memspi_host_flush_cache, \ + .region_protected = memspi_region_protected, \ +} + +/// configuration for the memspi host +typedef spi_flash_memspi_config_t memspi_host_config_t; +/// context for the memspi host +typedef spi_flash_memspi_data_t memspi_host_data_t; + +/** + * Initialize the memory SPI host. + * + * @param host Pointer to the host structure. + * @param data Pointer to allocated space to hold the context of host driver. + * @param cfg Pointer to configuration structure + * + * @return always return ESP_OK + */ +esp_err_t memspi_host_init_pointers(spi_flash_host_driver_t *host, memspi_host_data_t *data, const memspi_host_config_t *cfg); + +/******************************************************************************* + * NOTICE + * Rest part of this file are part of the HAL layer + * The HAL is not public api, don't use in application code. + * See readme.md in soc/include/hal/readme.md + ******************************************************************************/ + +/** + * High speed implementation of RDID through memspi interface relying on the + * ``common_command``. + * + * @param driver The driver context. + * @param id Output of the read ID from the slave. + * + * @return + * - ESP_OK: if success + * - ESP_ERR_FLASH_NO_RESPONSE: if no response from chip + * - or other cases from ``spi_hal_common_command`` + */ +esp_err_t memspi_host_read_id_hs(spi_flash_host_driver_t *driver, uint32_t *id); + +/** + * High speed implementation of RDSR through memspi interface relying on the + * ``common_command``. + * + * @param driver The driver context. + * @param id Output of the read ID from the slave. + * + * @return + * - ESP_OK: if success + * - or other cases from ``spi_hal_common_command`` + */ +esp_err_t memspi_host_read_status_hs(spi_flash_host_driver_t *driver, uint8_t *out_sr); + +/** + * Flush the cache (if needed) after the contents are modified. + * + * @param driver The driver context. + * @param addr Start address of the modified region + * @param size Size of the region modified. + * + * @return always ESP_OK. + */ +esp_err_t memspi_host_flush_cache(spi_flash_host_driver_t* driver, uint32_t addr, uint32_t size); + +/** + * Check if the given region is protected. + * + * @param driver The driver context. + * @param addr Start address of the region. + * @param size Size of the region to check. + * + * @return true if protected, otherwise false. + */ +bool memspi_region_protected(spi_flash_host_driver_t* driver, uint32_t addr, uint32_t size); \ No newline at end of file diff --git a/components/spi_flash/include/spi_flash_lowlevel_driver.h b/components/spi_flash/include/spi_flash_chip_driver.h similarity index 55% rename from components/spi_flash/include/spi_flash_lowlevel_driver.h rename to components/spi_flash/include/spi_flash_chip_driver.h index 0343cc7cb4..fd5c1e4c92 100644 --- a/components/spi_flash/include/spi_flash_lowlevel_driver.h +++ b/components/spi_flash/include/spi_flash_chip_driver.h @@ -1,23 +1,43 @@ -#pragma once -#include "spi_flash_lowlevel.h" +// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// +// 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. -/** @brief SPI flash driver definition structure. +#pragma once +#include "esp_flash.h" + +struct esp_flash_t; +typedef struct esp_flash_t esp_flash_t; + +typedef struct spi_flash_chip_t spi_flash_chip_t; +/** @brief SPI flash chip driver definition structure. * - * The driver structure contains chip-specific pointers to functions to perform SPI flash operations, and some + * The chip driver structure contains chip-specific pointers to functions to perform SPI flash operations, and some * chip-specific numeric values. * - * @note This is not a public API. Driver-specific functions are called from the public API (declared in - * spi_flash_lowlevel.h). They assume the caller has already validated arguments and enabled relevant protections + * @note This is not a public API. These functions are called from the public API (declared in + * esp_flash.h). They assume the caller has already validated arguments and enabled relevant protections * (disabling flash cache, prevent concurrent SPI access, etc.) * - * Do not call driver functions directly in other contexts. + * Do not call chip driver functions directly in other contexts. * - * A generic driver and it's related operations are defined in spi_flash_lowlevel_generic.h which can be used as - * building blocks for written new/specific SPI flash chip drivers. + * A generic driver for generic chips and its related operations are defined in + * spi_flash_chip_generic.h which can be used as building blocks for written + * new/specific SPI flash chip drivers. * * @note All of these functions may be called with SPI flash cache disabled, so must only ever access IRAM/DRAM/ROM. */ -typedef struct esp_flash_driver { +struct spi_flash_chip_t { + const char *name; ///< Name of the chip driver /* Probe to detect if a supported SPI flash chip is found. * * Attempts to configure 'chip' with these operations and probes for a matching SPI flash chip. @@ -31,28 +51,25 @@ typedef struct esp_flash_driver { * It is permissible for the driver to modify the 'chip' structure if probing succeeds (specifically, to assign something to the * driver_data pointer if that is useful for the driver.) * - * @return FLASH_OK if probing was successful, an error otherwise. Driver may - * assume that returning FLASH_OK means it has claimed this chip. + * @return ESP_OK if probing was successful, an error otherwise. Driver may + * assume that returning ESP_OK means it has claimed this chip. */ - esp_flash_err_t (*probe)(esp_flash_chip_t *chip, uint32_t flash_id); + esp_err_t (*probe)(esp_flash_t *chip, uint32_t flash_id); + + esp_err_t (*reset)(esp_flash_t *chip); - /* Read SPI flash ID - * - * Sends RDID (or an equivalent command) to the device. - */ - esp_flash_err_t (*read_id)(const esp_flash_chip_t *chip, uint32_t *id); /* Detect SPI flash size * - * Interrogate the chip to detect it's size. + * Interrogate the chip to detect its size. */ - esp_flash_err_t (*detect_size)(const esp_flash_chip_t *chip, uint32_t *size); + esp_err_t (*detect_size)(esp_flash_t *chip, uint32_t *size); /* Erase the entire chip Caller has verified the chip is not write protected. */ - esp_flash_err_t (*erase_chip)(const esp_flash_chip_t *chip); + esp_err_t (*erase_chip)(esp_flash_t *chip); /* Erase a sector of the chip. Sector size is specified in the 'sector_size' field. @@ -60,23 +77,23 @@ typedef struct esp_flash_driver { Caller has verified that this sector should be non-write-protected. */ - esp_flash_err_t (*erase_sector)(const esp_flash_chip_t *chip, uint32_t sector_address); + esp_err_t (*erase_sector)(esp_flash_t *chip, uint32_t sector_address); /* Erase a multi-sector block of the chip. Block size is specified in the 'block_erase_size' field. sector_address is an offset in bytes. Caller has verified that this block should be non-write-protected. */ - esp_flash_err_t (*erase_block)(const esp_flash_chip_t *chip, uint32_t block_address); + esp_err_t (*erase_block)(esp_flash_t *chip, uint32_t block_address); uint32_t sector_size; /* Sector is minimum erase size */ uint32_t block_erase_size; /* Optimal (fastest) block size for multi-sector erases on this chip */ /* Read the write protect status of the entire chip. */ - esp_flash_err_t (*get_chip_write_protect)(const esp_flash_chip_t *chip, bool *write_protected); + esp_err_t (*get_chip_write_protect)(esp_flash_t *chip, bool *write_protected); /* Set the write protect status of the entire chip. */ - esp_flash_err_t (*set_chip_write_protect)(const esp_flash_chip_t *chip, bool write_protect_chip); + esp_err_t (*set_chip_write_protect)(esp_flash_t *chip, bool write_protect_chip); /* Number of individually write protectable regions on this chip. Range 0-63. */ uint8_t num_protectable_regions; @@ -84,20 +101,20 @@ typedef struct esp_flash_driver { const esp_flash_region_t *protectable_regions; /* Get a bitmask describing all protectable regions on the chip. Each bit represents one entry in the protectable_regions array, ie bit (1<drv->set_read_mode(chip) in order to configure the chip's read mode correctly. */ - esp_flash_err_t (*read)(const esp_flash_chip_t *chip, void *buffer, uint32_t address, uint32_t length); + esp_err_t (*read)(esp_flash_t *chip, void *buffer, uint32_t address, uint32_t length); /* Write any amount of data to the chip. */ - esp_flash_err_t (*write)(const esp_flash_chip_t *chip, uint32_t address, const void *buffer, uint32_t length); + esp_err_t (*write)(esp_flash_t *chip, const void *buffer, uint32_t address, uint32_t length); /* Use the page program command to write data to the chip. @@ -110,33 +127,33 @@ typedef struct esp_flash_driver { * - The region between 'address' and 'address + length' will not cross a page_size aligned boundary (the write * implementation is expected to split such a write into two before calling page_program.) */ - esp_flash_err_t (*page_program)(const esp_flash_chip_t *chip, uint32_t address, const void *buffer, uint32_t length); + esp_err_t (*program_page)(esp_flash_t *chip, const void *buffer, uint32_t address, uint32_t length); /* Page size as written by the page_program function. Usually 256 bytes. */ uint32_t page_size; /* Perform an encrypted write to the chip, using internal flash encryption hardware. */ - esp_flash_err_t (*write_encrypted)(const esp_flash_chip_t *chip, uint32_t address, const void *buffer, uint32_t length); + esp_err_t (*write_encrypted)(esp_flash_t *chip, const void *buffer, uint32_t address, uint32_t length); /* Set the write enable flag. This function is called internally by other functions in this structure, before a destructive operation takes place. */ - esp_flash_err_t (*write_enable)(const esp_flash_chip_t *chip); + esp_err_t (*set_write_protect)(esp_flash_t *chip, bool write_protect); /* Wait for the SPI flash chip to be idle (any write operation to be complete.) This function is both called from the higher-level API functions, and from other functions in this structure. - timeout_ms should be a timeout (in milliseconds) before the function returns FLASH_ERR_NO_RESPONSE. This is useful to avoid hanging + timeout_ms should be a timeout (in milliseconds) before the function returns ESP_ERR_TIMEOUT. This is useful to avoid hanging if the chip is otherwise unresponsive (ie returns all 0xFF or similar.) */ - esp_flash_err_t (*wait_idle)(const esp_flash_chip_t *chip, unsigned timeout_ms); + esp_err_t (*wait_idle)(esp_flash_t *chip, unsigned timeout_ms); /* Configure both the SPI host and the chip for the read mode specified in chip->read_mode. * * This function is called by the higher-level API before the 'read' function is called. * - * Can return FLASH_ERR_UNSUPPORTED_HOST or FLASH_ERR_UNSUPPORTED_CHIP if the specified mode is unsupported. + * Can return ESP_ERR_FLASH_UNSUPPORTED_HOST or ESP_ERR_FLASH_UNSUPPORTED_CHIP if the specified mode is unsupported. */ - esp_flash_err_t (*set_read_mode)(const esp_flash_chip_t *chip); -} esp_flash_driver_t; + esp_err_t (*set_read_mode)(esp_flash_t *chip); +}; /* Pointer to an array of pointers to all known drivers for flash chips. This array is used by esp_flash_init() to detect the flash chip driver, if none is supplied by the caller. @@ -145,32 +162,4 @@ typedef struct esp_flash_driver { This pointer can be overwritten with a pointer to a new array, to update the list of known flash chips. */ -extern const esp_flash_driver_t **esp_flash_registered_flash_drivers; - -/* Provide OS-level integration hooks for accessing flash chips - inside a running OS */ -typedef struct -{ - /* Call this function before commencing any flash operation. - - Does not need to be recursive (ie is called at most once for each call to 'end'. - */ - esp_flash_err_t (*start)(const esp_flash_chip_t *chip); - - /* Call this function after completing any flash operation. */ - esp_flash_err_t (*end)(const esp_flash_chip_t *chip); - - /* Delay for at least 'ms' milliseconds. - - This function will be called in between 'start' and 'end'. - */ - esp_flash_err_t (*delay_ms)(unsigned ms); -} esp_flash_os_functions_t; - -/* The default (ie initial boot) no-OS ROM esp_flash_os_functions_t */ -const esp_flash_os_functions_t esp_flash_noos_functions; - -/* Pointer to the current esp_flash_os_functions_t structure in use. - Can be changed at runtime to reflect different running conditions. - */ -extern const esp_flash_os_functions_t *esp_flash_os_functions; +extern const spi_flash_chip_t **esp_flash_registered_chips; diff --git a/components/spi_flash/include/spi_flash_chip_generic.h b/components/spi_flash/include/spi_flash_chip_generic.h new file mode 100644 index 0000000000..676e173111 --- /dev/null +++ b/components/spi_flash/include/spi_flash_chip_generic.h @@ -0,0 +1,275 @@ +// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// +// 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 "esp_flash.h" +#include "spi_flash_chip_driver.h" + + +/* + * The 'chip_generic' SPI flash operations are a lowest common subset of SPI + * flash commands, that work across most chips. + * + * These can be used as-is via the esp_flash_common_chip_driver chip_drv, or + * they can be used as "base chip_drv" functions when creating a new + * spi_flash_host_driver_t chip_drv structure. + * + * All of the functions in this header are internal functions, not part of a + * public API. See esp_flash.h for the public API. + */ + +/** + * @brief Generic probe function + * + * @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted. + * @param flash_id expected manufacture id. + * + * @return ESP_OK if the id read from chip->drv_read_id matches (always). + */ +esp_err_t spi_flash_chip_generic_probe(esp_flash_t *chip, uint32_t flash_id); + +/** + * @brief Generic reset function + * + * @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted. + * + * @return ESP_OK if sending success, or error code passed from ``common_command`` or ``wait_idle`` functions of host driver. + */ +esp_err_t spi_flash_chip_generic_reset(esp_flash_t *chip); + +/** + * @brief Generic size detection function + * + * Tries to detect the size of chip by using the lower 4 bits of the chip->drv->read_id result = N, and assuming size is 2 ^ N. + * + * @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted. + * @param size Output of the detected size + * + * @return + * - ESP_OK if success + * - ESP_ERR_FLASH_UNSUPPORTED_CHIP if the manufacturer id is not correct, which may means an error in the reading + * - or other error passed from the ``read_id`` function of host driver + */ +esp_err_t spi_flash_chip_generic_detect_size(esp_flash_t *chip, uint32_t *size); + +/** + * @brief Erase chip by using the generic erase chip (C7h) command. + * + * @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted. + * + * @return + * - ESP_OK if success + * - or other error passed from the ``set_write_protect``, ``wait_idle`` or ``erase_chip`` function of host driver + */ +esp_err_t spi_flash_chip_generic_erase_chip(esp_flash_t *chip); + +/** + * @brief Erase sector by using the generic sector erase (20h) command. + * + * @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted. + * @param start_address Start address of the sector to erase + * + * @return + * - ESP_OK if success + * - or other error passed from the ``set_write_protect``, ``wait_idle`` or ``erase_sector`` function of host driver + */ +esp_err_t spi_flash_chip_generic_erase_sector(esp_flash_t *chip, uint32_t start_address); + +/** + * @brief Erase block by using the generic 64KB block erase (D8h) command + * + * @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted. + * @param start_address Start address of the block to erase + * + * @return + * - ESP_OK if success + * - or other error passed from the ``set_write_protect``, ``wait_idle`` or ``erase_block`` function of host driver + */ +esp_err_t spi_flash_chip_generic_erase_block(esp_flash_t *chip, uint32_t start_address); + +/** + * @brief Read from flash by using a read command that matches the programmed + * read mode. + * + * @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted. + * @param buffer Buffer to hold the data read from flash + * @param address Start address of the data on the flash + * @param length Length to read + * + * @return always ESP_OK currently + */ +esp_err_t spi_flash_chip_generic_read(esp_flash_t *chip, void *buffer, uint32_t address, uint32_t length); + +/** + * @brief Perform a page program using the page program (02h) command. + * + * @note Length of each call should not excced the limitation in + * ``chip->host->max_write_bytes``. This function is called in + * ``spi_flash_chip_generic_write`` recursively until the whole page is + * programmed. Strongly suggest to call ``spi_flash_chip_generic_write`` + * instead. + * + * @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted. + * @param buffer Buffer holding the data to program + * @param address Start address to write to flash + * @param length Length to write, no longer than ``chip->host->max_write_bytes``. + * + * @return + * - ESP_OK if success + * - or other error passed from the ``wait_idle`` or ``program_page`` function of host driver + */ +esp_err_t +spi_flash_chip_generic_page_program(esp_flash_t *chip, const void *buffer, uint32_t address, uint32_t length); + +/** + * @brief Perform a generic write. Split the write buffer into page program + * operations, and call chip->chip_drv->page-program() for each. + * + * @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted. + * @param buffer Buffer holding the data to program + * @param address Start address to write to flash + * @param length Length to write + * + * @return + * - ESP_OK if success + * - or other error passed from the ``wait_idle`` or ``program_page`` function of host driver + */ +esp_err_t spi_flash_chip_generic_write(esp_flash_t *chip, const void *buffer, uint32_t address, uint32_t length); + +/** + * @brief Perform a write using on-chip flash encryption. Not implemented yet. + * + * @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted. + * @param buffer Buffer holding the data to program + * @param address Start address to write to flash + * @param length Length to write + * + * @return always ESP_ERR_FLASH_UNSUPPORTED_HOST. + */ +esp_err_t +spi_flash_chip_generic_write_encrypted(esp_flash_t *chip, const void *buffer, uint32_t address, uint32_t length); + +/** + * @brief Send the write enable (06h) command and verify the expected bit (1) in + * the status register is set. + * + * @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted. + * @param write_protect true to enable write protection, false to send write enable. + * + * @return + * - ESP_OK if success + * - or other error passed from the ``wait_idle``, ``read_status`` or ``set_write_protect`` function of host driver + */ +esp_err_t spi_flash_chip_generic_write_enable(esp_flash_t *chip, bool write_protect); + +/** + * @brief Read flash status via the RDSR command (05h) and wait for bit 0 (write + * in progress bit) to be cleared. + * + * @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted. + * @param timeout_ms Time to wait before timeout, in ms. + * + * @return + * - ESP_OK if success + * - ESP_ERR_TIMEOUT if not idle before timeout + * - or other error passed from the ``wait_idle`` or ``read_status`` function of host driver + */ +esp_err_t spi_flash_chip_generic_wait_idle(esp_flash_t *chip, uint32_t timeout_ms); + +/** + * @brief Set the specified SPI read mode according to the data in the chip + * context. Set quad enable status register bit if needed. + * + * @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted. + * + * @return + * - ESP_OK if success +* - ESP_ERR_TIMEOUT if not idle before timeout + * - or other error passed from the ``set_write_protect`` or ``common_command`` function of host driver + */ +esp_err_t spi_flash_chip_generic_set_read_mode(esp_flash_t *chip); + +/** + * Generic SPI flash chip_drv, uses all the above functions for its operations. + * In default autodetection, this is used as a catchall if a more specific + * chip_drv is not found. + */ +extern const spi_flash_chip_t esp_flash_chip_generic; + +/******************************************************************************* + * Utilities +*******************************************************************************/ + +/** + * @brief Wait for the SPI host hardware state machine to be idle. + * + * This isn't a flash chip_drv operation, but it's called by + * spi_flash_chip_generic_wait_idle() and may be useful when implementing + * alternative drivers. + * + * timeout_ms will be decremented if the function needs to wait until the host hardware is idle. + * + * @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted. + * + * @return + * - ESP_OK if success + * - ESP_ERR_TIMEOUT if not idle before timeout + * - or other error passed from the ``set_write_protect`` or ``common_command`` function of host driver + */ +esp_err_t spi_flash_generic_wait_host_idle(esp_flash_t *chip, uint32_t *timeout_ms); + +/** + * @brief Utility function for set_read_mode chip_drv function + * + * Most setting of read mode follows a common pattern, except for how to enable Quad I/O modes (QIO/QOUT). + * These use different commands to read/write the status register, and a different bit is set/cleared. + * + * This is a generic utility function to implement set_read_mode() for this pattern. Also configures host + * registers via spi_flash_common_configure_host_read_mode(). + * + * @param qe_rdsr_command SPI flash command to read status register + * @param qe_wrsr_command SPI flash command to write status register + * @param qe_sr_bitwidth Width of the status register these commands operate on, in bits. + * @param qe_sr_bit Bit mask for enabling Quad Enable functions on this chip. + * + * @return always ESP_OK (currently). + */ +esp_err_t spi_flash_common_set_read_mode(esp_flash_t *chip, uint8_t qe_rdsr_command, uint8_t qe_wrsr_command, uint8_t qe_sr_bitwidth, unsigned qe_sr_bit); + +/** + * @brief Configure the host to use the specified read mode set in the ``chip->read_mode``. + * + * @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted. + * + * @return + * - ESP_OK if success + * - ESP_ERR_FLASH_NOT_INITIALISED if chip not initialized properly + * - or other error passed from the ``configure_host_mode`` function of host driver + */ +esp_err_t spi_flash_chip_generic_config_host_read_mode(esp_flash_t *chip); + +/** + * @brief Returns true if chip is configured for Quad I/O or Quad Fast Read. + * + * @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted. + * + * @return true if flash works in quad mode, otherwise false + */ +static inline bool spi_flash_is_quad_mode(const esp_flash_t *chip) +{ + return (chip->read_mode == SPI_FLASH_QIO) || (chip->read_mode == SPI_FLASH_QOUT); +} + diff --git a/components/spi_flash/include/spi_flash_chip_issi.h b/components/spi_flash/include/spi_flash_chip_issi.h new file mode 100644 index 0000000000..2b1d411552 --- /dev/null +++ b/components/spi_flash/include/spi_flash_chip_issi.h @@ -0,0 +1,27 @@ +// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// +// 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 "esp_flash.h" +#include "spi_flash_chip_driver.h" + + +/** + * ISSI SPI flash chip_drv, uses all the above functions for its operations. In + * default autodetection, this is used as a catchall if a more specific chip_drv + * is not found. + */ +extern const spi_flash_chip_t esp_flash_chip_issi; diff --git a/components/spi_flash/include/spi_flash_lowlevel_generic.h b/components/spi_flash/include/spi_flash_lowlevel_generic.h deleted file mode 100644 index 75866c721e..0000000000 --- a/components/spi_flash/include/spi_flash_lowlevel_generic.h +++ /dev/null @@ -1,151 +0,0 @@ -#pragma once -#include "spi_flash_lowlevel_driver.h" -/* The 'generic' SPI flash operations are a lowest common subset of SPI flash commands, that work across most chips. - * - * These can be used as-is vai the esp_flash_common_chip_driver driver, or they can be used as "base driver" functions when - * creating a new esp_flash_driver_t driver structure. - * - * - * All of the functions in this header are internal functions, not part of a public API. See spi_flash_lowlevel.h for - * the public API. - */ - -/* SPI commands (actual on-wire commands not SPI controller bitmasks) - Suitable for use with spi_flash_common_command static function. -*/ -#define CMD_RDID 0x9F -#define CMD_WRSR 0x01 -#define CMD_WRSR2 0x31 /* Not all SPI flash uses this command */ -#define CMD_WREN 0x06 -#define CMD_WRDI 0x04 -#define CMD_RDSR 0x05 -#define CMD_RDSR2 0x35 /* Not all SPI flash uses this command */ - -#define CMD_FASTRD_QIO 0xEB -#define CMD_FASTRD_QUAD 0x6B -#define CMD_FASTRD_DIO 0xBB -#define CMD_FASTRD_DUAL 0x3B -#define CMD_FASTRD 0x0B -#define CMD_READ 0x03 /* Speed limited */ - -#define CMD_CHIP_ERASE 0xC7 -#define CMD_SECTOR_ERASE 0x20 -#define CMD_LARGE_BLOCK_ERASE 0xD8 /* 64KB block erase command */ - -#define SR_WIP (1<<0) /* Status register write-in-progress bit */ -#define SR_WREN (1<<1) /* Status register write enable bit */ - - -/** @brief Execute a simple SPI flash command against the chip. - * - * @param chip Pointer to the chip to use. - * @param command Command to execute (an on-wire hex command.) - * @param mosi_data Up to 32 bits of MOSI data to write after the command. - * @param mosi_len Length of MOSI data (in bits.) - * @param miso_len Length of MISO data (in bits.) - * @return MISO value read back, if any (depending on miso_len value.) - */ -uint32_t spi_flash_common_command(const esp_flash_chip_t *chip, uint8_t command, uint32_t mosi_data, uint8_t mosi_len, uint8_t miso_len); - -/** @brief Returns true if the pin configuration for this chip uses the GPIO matrix for any signals. */ -bool spi_flash_uses_gpio_matrix(const esp_flash_chip_t *chip); - -/** @brief Generic probe function - * - * If chip->drv_read_id succeeds, the probe succeeds. - */ -esp_flash_err_t spi_flash_generic_probe(esp_flash_chip_t *chip, uint32_t flash_id); - -/** @brief Generic implementation of esp_flash_driver_t->read_id - * - * Uses the RDID command (9Fh) supported by most SPI flash chips. - * - * Results of all-zeroes or all-ones are considered failures (probably no chip attached.) - */ -esp_flash_err_t spi_flash_generic_read_id(const esp_flash_chip_t *chip, uint32_t *id); - -/** @brief Generic size detection function - * - * Tries to detect the size of chip by using the lower 4 bits of the chip->drv->read_id result = N, and assuming size is 2 ^ N. - */ -esp_flash_err_t spi_flash_generic_detect_size(const esp_flash_chip_t *chip, uint32_t *size); - -/** @brief Erase chip by using the generic erase chip (C7h) command. */ -esp_flash_err_t spi_flash_generic_erase_chip(const esp_flash_chip_t *chip); - -/** @brief Erase sector by using the generic sector erase (20h) command. */ -esp_flash_err_t spi_flash_generic_erase_sector(const esp_flash_chip_t *chip, uint32_t start_address); - -/** @brief Erase block by using the generic 64KB block erase (D8h) command */ -esp_flash_err_t spi_flash_generic_erase_block(const esp_flash_chip_t *chip, uint32_t start_address); - -/** @brief Read from flash by using a read command that matches the programmed read mode. */ -esp_flash_err_t spi_flash_generic_read(const esp_flash_chip_t *chip, void *buffer, uint32_t address, uint32_t length); - -/** @brief Perform a page program using the page program (02h) command. */ -esp_flash_err_t spi_flash_generic_page_program(const esp_flash_chip_t *chip, uint32_t address, const void *buffer, uint32_t length); - -/** @brief Perform a generic write. Split the write buffer into - one page operations, and call chip->drv->page-program() for each. -*/ -esp_flash_err_t spi_flash_generic_write(const esp_flash_chip_t *chip, uint32_t address, const void *buffer, uint32_t length); - -/** @brief Perform a write using on-chip flash encryption */ -esp_flash_err_t spi_flash_generic_write_encrypted(const esp_flash_chip_t *chip, uint32_t address, const void *buffer, uint32_t length); - -/** @brief Send the write enable (06h) command and verify the expected bit (1) in the status register is set. */ -esp_flash_err_t spi_flash_generic_write_enable(const esp_flash_chip_t *chip); - -/** @brief Wait for the SPI host hardware state machine to be idle. - - This isn't a flash driver operation, but it's called by spi_flash_generic_wait_idle() and may be useful when implementing alternative drivers. - - timeout_ms will be decremented if the function needs to wait until the host hardware is idle. -*/ -esp_flash_err_t spi_flash_generic_wait_host_idle(const esp_flash_chip_t *chip, uint32_t *timeout_ms); - -/** @brief Read flash status via the RDSR command (05h) and wait for bit 0 (write in progress bit) to be cleared. */ -esp_flash_err_t spi_flash_generic_wait_idle(const esp_flash_chip_t *chip, uint32_t timeout_ms); - -/** @brief Utility function to configure the SPI host hardware registers for the specified read mode. - - Called by spi_flash_generic_set_read_mode() but may also be useful - when implementing other SPI flash drivers. - - Note that calling this configures SPI host registers, so if running any other commands as part of set_read_mode() then these must be run before calling this function. - */ -esp_flash_err_t spi_flash_common_configure_host_read_mode(const esp_flash_chip_t *chip); - -/** @brief Utility function for set_read_mode driver function - * - * Most setting of read mode follows a common pattern, except for how to enable Quad I/O modes (QIO/QOUT). - * These use different commands to read/write the status register, and a different bit is set/cleared. - * - * This is a generic utility function to implement set_read_mode() for this pattern. Also configures host - * registers via spi_flash_common_configure_host_read_mode(). - * - * @param qe_rdsr_command SPI flash command to read status register - * @param qe_wrsr_command SPI flash command to write status register - * @param qe_sr_bitwidth Width of the status register these commands operate on, in bits. - * @param qe_sr_bit Bit mask for enabling Quad Enable functions on this chio. - */ -esp_flash_err_t spi_flash_common_set_read_mode(const esp_flash_chip_t *chip, uint8_t qe_rdsr_command, uint8_t qe_wrsr_command, uint8_t qe_sr_bitwidth, unsigned qe_sr_bit); - -/** @brief Set the specified SPI read mode. - * - * Includes setting SPI host hardware registers, but also setting quad enable status register bit if needed. - */ -esp_flash_err_t spi_flash_generic_set_read_mode(const esp_flash_chip_t *chip); - -/** @brief Returns true if chip is configured for Quad I/O or - Quad Fast Read */ -inline static bool spi_flash_is_quad_mode(const esp_flash_chip_t *chip) -{ - return chip->read_mode == ESP_FLASH_QIO || chip->read_mode == ESP_FLASH_QOUT; -} - -/* Generic SPI flash driver, uses all the above functions for its operations. In default autodetection, this is used as - a catchall if a more specific driver is not found. -*/ -extern const esp_flash_driver_t esp_flash_generic_chip_driver; - diff --git a/components/spi_flash/linker.lf b/components/spi_flash/linker.lf index e466c85a2b..e5d886ca4b 100644 --- a/components/spi_flash/linker.lf +++ b/components/spi_flash/linker.lf @@ -2,9 +2,7 @@ archive: libspi_flash.a entries: spi_flash_rom_patch (noflash_text) - spi_flash_lowlevel_api (noflash) - spi_flash_lowlevel_generic (noflash) - spi_flash_lowlevel_issi (noflash) - spi_flash_lowlevel_idf_app (noflash) - spi_flash_driver_hs (noflash) + spi_flash_chip_generic (noflash) + spi_flash_chip_issi (noflash) + memspi_host_driver (noflash) diff --git a/components/spi_flash/memspi_host_driver.c b/components/spi_flash/memspi_host_driver.c new file mode 100644 index 0000000000..871616386f --- /dev/null +++ b/components/spi_flash/memspi_host_driver.c @@ -0,0 +1,101 @@ +// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// +// 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 "spi_flash_defs.h" +#include "memspi_host_driver.h" +#include "string.h" +#include "esp_log.h" +#include "cache_utils.h" +#include "esp_flash_partitions.h" + +static const char TAG[] = "memspi"; +static const spi_flash_host_driver_t esp_flash_default_host = ESP_FLASH_DEFAULT_HOST_DRIVER(); + +esp_err_t memspi_host_init_pointers(spi_flash_host_driver_t *host, memspi_host_data_t *data, const memspi_host_config_t *cfg) +{ + memcpy(host, &esp_flash_default_host, sizeof(spi_flash_host_driver_t)); + esp_err_t err = spi_flash_hal_init(data, cfg); + if (err != ESP_OK) { + return err; + } + + host->driver_data = data; + //some functions are not required if not SPI1 + if (data->spi != &SPI1) { + host->flush_cache = NULL; + host->region_protected = NULL; + } + return ESP_OK; +} + +esp_err_t memspi_host_read_id_hs(spi_flash_host_driver_t *chip_drv, uint32_t *id) +{ + //NOTE: we do have a read id function, however it doesn't work in high freq + spi_flash_trans_t t = { + .command = CMD_RDID, + .mosi_data = 0, + .mosi_len = 0, + .miso_len = 24 + }; + chip_drv->common_command(chip_drv, &t); + uint32_t raw_flash_id = t.miso_data[0]; + ESP_EARLY_LOGV(TAG, "raw_chip_id: %X\n", raw_flash_id); + if (raw_flash_id == 0xFFFFFF || raw_flash_id == 0) { + ESP_EARLY_LOGE(TAG, "no response\n"); + return ESP_ERR_FLASH_NO_RESPONSE; + } + + // Byte swap the flash id as it's usually written the other way around + uint8_t mfg_id = raw_flash_id & 0xFF; + uint16_t flash_id = (raw_flash_id >> 16) | (raw_flash_id & 0xFF00); + *id = ((uint32_t)mfg_id << 16) | flash_id; + ESP_EARLY_LOGV(TAG, "chip_id: %X\n", *id); + return ESP_OK; +} + +esp_err_t memspi_host_read_status_hs(spi_flash_host_driver_t *driver, uint8_t *out_sr) +{ + //NOTE: we do have a read id function, however it doesn't work in high freq + spi_flash_trans_t t = { + .command = CMD_RDSR, + .mosi_data = 0, + .mosi_len = 0, + .miso_len = 8 + }; + esp_err_t err = driver->common_command(driver, &t); + if (err != ESP_OK) { + return err; + } + *out_sr = t.miso_data[0]; + return ESP_OK; +} + +esp_err_t memspi_host_flush_cache(spi_flash_host_driver_t* driver, uint32_t addr, uint32_t size) +{ + if (((memspi_host_data_t*)(driver->driver_data))->spi == &SPI1) { + spi_flash_check_and_flush_cache(addr, size); + } + return ESP_OK; +} + +bool memspi_region_protected(spi_flash_host_driver_t* driver, uint32_t addr, uint32_t size) +{ + if (((memspi_host_data_t*)(driver->driver_data))->spi != &SPI1) { + return false; + } + if (!esp_partition_main_flash_region_safe(addr, size)) { + return true; + } + return false; +} \ No newline at end of file diff --git a/components/spi_flash/partition.c b/components/spi_flash/partition.c index 5aa99a36ab..5da204ae56 100644 --- a/components/spi_flash/partition.c +++ b/components/spi_flash/partition.c @@ -24,6 +24,7 @@ #include "esp_flash_encrypt.h" #include "esp_log.h" #include "bootloader_common.h" +#include "esp_ota_ops.h" #define HASH_LEN 32 /* SHA-256 digest length */ @@ -355,3 +356,19 @@ bool esp_partition_check_identity(const esp_partition_t *partition_1, const esp_ } return false; } + +bool esp_partition_main_flash_region_safe(size_t addr, size_t size) +{ + bool result = true; + if (addr <= ESP_PARTITION_TABLE_OFFSET + ESP_PARTITION_TABLE_MAX_LEN) { + return false; + } + const esp_partition_t *p = esp_ota_get_running_partition(); + if (addr >= p->address && addr < p->address + p->size) { + return false; + } + if (addr < p->address && addr + size > p->address) { + return false; + } + return result; +} diff --git a/components/spi_flash/private_include/spi_flash_defs.h b/components/spi_flash/private_include/spi_flash_defs.h new file mode 100644 index 0000000000..b210271e74 --- /dev/null +++ b/components/spi_flash/private_include/spi_flash_defs.h @@ -0,0 +1,44 @@ +// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// +// 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 + +/* SPI commands (actual on-wire commands not SPI controller bitmasks) + Suitable for use with spi_flash_hal_common_command static function. +*/ +#define CMD_RDID 0x9F +#define CMD_WRSR 0x01 +#define SR_WIP (1<<0) /* Status register write-in-progress bit */ +#define SR_WREN (1<<1) /* Status register write enable bit */ +#define CMD_WRSR2 0x31 /* Not all SPI flash uses this command */ +#define CMD_WREN 0x06 +#define CMD_WRDI 0x04 +#define CMD_RDSR 0x05 +#define CMD_RDSR2 0x35 /* Not all SPI flash uses this command */ + +#define CMD_FASTRD_QIO 0xEB +#define CMD_FASTRD_QUAD 0x6B +#define CMD_FASTRD_DIO 0xBB +#define CMD_FASTRD_DUAL 0x3B +#define CMD_FASTRD 0x0B +#define CMD_READ 0x03 /* Speed limited */ + +#define CMD_CHIP_ERASE 0xC7 +#define CMD_SECTOR_ERASE 0x20 +#define CMD_LARGE_BLOCK_ERASE 0xD8 /* 64KB block erase command */ + +#define CMD_RST_EN 0x66 +#define CMD_RST_DEV 0x99 + + diff --git a/components/spi_flash/spi_flash_chip_drivers.c b/components/spi_flash/spi_flash_chip_drivers.c new file mode 100644 index 0000000000..ae10bcec37 --- /dev/null +++ b/components/spi_flash/spi_flash_chip_drivers.c @@ -0,0 +1,38 @@ +// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// +// 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 "spi_flash_chip_driver.h" +#include "spi_flash_chip_generic.h" +#include "spi_flash_chip_issi.h" +#include "sdkconfig.h" + +/* + * Default registered chip drivers. Note these are tested in order and first + * match is taken, so generic/catchall entries should go last. Note that the + * esp_flash_registered_flash_ops pointer can be changed to point to a different + * array of registered ops, if desired. + * + * It can be configured to support only available chips in the sdkconfig, to + * avoid possible issues, and speed up the auto-detecting. + */ +static const spi_flash_chip_t *default_registered_chips[] = { +#ifdef CONFIG_SPI_FLASH_SUPPORT_ISSI_CHIP + &esp_flash_chip_issi, +#endif + &esp_flash_chip_generic, + NULL, +}; + +const spi_flash_chip_t **esp_flash_registered_chips = default_registered_chips; diff --git a/components/spi_flash/spi_flash_chip_generic.c b/components/spi_flash/spi_flash_chip_generic.c new file mode 100644 index 0000000000..0e80e8eb1d --- /dev/null +++ b/components/spi_flash/spi_flash_chip_generic.c @@ -0,0 +1,405 @@ +// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// +// 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 // For MIN/MAX +#include "spi_flash_chip_generic.h" +#include "spi_flash_defs.h" +#include "esp_log.h" + +static const char TAG[] = "chip_generic"; + +#define SPI_FLASH_GENERIC_CHIP_ERASE_TIMEOUT 4000 +#define SPI_FLASH_GENERIC_SECTOR_ERASE_TIMEOUT 500 +#define SPI_FLASH_GENERIC_BLOCK_ERASE_TIMEOUT 1000 + +#define DEFAULT_IDLE_TIMEOUT 200 +#define DEFAULT_PAGE_PROGRAM_TIMEOUT 500 + +esp_err_t spi_flash_chip_generic_probe(esp_flash_t *chip, uint32_t flash_id) +{ + // This is the catch-all probe function, claim the chip always if nothing + // else has claimed it yet. + return ESP_OK; +} + +esp_err_t spi_flash_chip_generic_reset(esp_flash_t *chip) +{ + //this is written following the winbond spec.. + spi_flash_trans_t t; + t = (spi_flash_trans_t) { + .command = CMD_RST_EN, + }; + esp_err_t err = chip->host->common_command(chip->host, &t); + if (err != ESP_OK) { + return err; + } + + t = (spi_flash_trans_t) { + .command = CMD_RST_DEV, + }; + err = chip->host->common_command(chip->host, &t); + if (err != ESP_OK) { + return err; + } + + err = chip->chip_drv->wait_idle(chip, DEFAULT_IDLE_TIMEOUT); + return err; +} + +esp_err_t spi_flash_chip_generic_detect_size(esp_flash_t *chip, uint32_t *size) +{ + uint32_t id = 0; + *size = 0; + esp_err_t err = chip->host->read_id(chip->host, &id); + if (err != ESP_OK) { + return err; + } + + /* Can't detect size unless the high byte of the product ID matches the same convention, which is usually 0x40 or + * 0xC0 or similar. */ + if ((id & 0x0F00) != 0) { + return ESP_ERR_FLASH_UNSUPPORTED_CHIP; + } + + *size = 1 << (id & 0xFF); + return ESP_OK; +} + + +esp_err_t spi_flash_chip_generic_erase_chip(esp_flash_t *chip) +{ + esp_err_t err; + + err = chip->chip_drv->set_write_protect(chip, false); + if (err == ESP_OK) { + err = chip->chip_drv->wait_idle(chip, DEFAULT_IDLE_TIMEOUT); + } + if (err == ESP_OK) { + chip->host->erase_chip(chip->host); + //to save time, flush cache here + if (chip->host->flush_cache) { + err = chip->host->flush_cache(chip->host, 0, chip->size); + if (err != ESP_OK) { + return err; + } + } + err = chip->chip_drv->wait_idle(chip, SPI_FLASH_GENERIC_CHIP_ERASE_TIMEOUT); + } + return err; +} + +esp_err_t spi_flash_chip_generic_erase_sector(esp_flash_t *chip, uint32_t start_address) +{ + esp_err_t err = chip->chip_drv->set_write_protect(chip, false); + if (err == ESP_OK) { + err = chip->chip_drv->wait_idle(chip, DEFAULT_IDLE_TIMEOUT); + } + if (err == ESP_OK) { + chip->host->erase_sector(chip->host, start_address); + //to save time, flush cache here + if (chip->host->flush_cache) { + err = chip->host->flush_cache(chip->host, start_address, chip->chip_drv->sector_size); + if (err != ESP_OK) { + return err; + } + } + err = chip->chip_drv->wait_idle(chip, SPI_FLASH_GENERIC_SECTOR_ERASE_TIMEOUT); + } + return err; +} + +esp_err_t spi_flash_chip_generic_erase_block(esp_flash_t *chip, uint32_t start_address) +{ + esp_err_t err = chip->chip_drv->set_write_protect(chip, false); + if (err == ESP_OK) { + err = chip->chip_drv->wait_idle(chip, DEFAULT_IDLE_TIMEOUT); + } + if (err == ESP_OK) { + chip->host->erase_block(chip->host, start_address); + //to save time, flush cache here + if (chip->host->flush_cache) { + err = chip->host->flush_cache(chip->host, start_address, chip->chip_drv->block_erase_size); + if (err != ESP_OK) { + return err; + } + } + err = chip->chip_drv->wait_idle(chip, SPI_FLASH_GENERIC_BLOCK_ERASE_TIMEOUT); + } + return err; +} + +esp_err_t spi_flash_chip_generic_read(esp_flash_t *chip, void *buffer, uint32_t address, uint32_t length) +{ + esp_err_t err = ESP_OK; + // Configure the host, and return + spi_flash_chip_generic_config_host_read_mode(chip); + + while (err == ESP_OK && length > 0) { + uint32_t read_len = MIN(length, chip->host->max_read_bytes); + err = chip->host->read(chip->host, buffer, address, read_len); + + buffer += read_len; + length -= read_len; + address += read_len; + } + + return err; +} + +esp_err_t spi_flash_chip_generic_page_program(esp_flash_t *chip, const void *buffer, uint32_t address, uint32_t length) +{ + esp_err_t err; + + err = chip->chip_drv->wait_idle(chip, DEFAULT_IDLE_TIMEOUT); + + if (err == ESP_OK) { + // Perform the actual Page Program command + chip->host->program_page(chip->host, buffer, address, length); + + err = chip->chip_drv->wait_idle(chip, DEFAULT_PAGE_PROGRAM_TIMEOUT); + } + return err; +} + +esp_err_t spi_flash_chip_generic_write(esp_flash_t *chip, const void *buffer, uint32_t address, uint32_t length) +{ + esp_err_t err = ESP_OK; + const uint32_t page_size = chip->chip_drv->page_size; + + while (err == ESP_OK && length > 0) { + uint32_t page_len = MIN(chip->host->max_write_bytes, MIN(page_size, length)); + if ((address + page_len) / page_size != address / page_size) { + // Most flash chips can't page write across a page boundary + page_len = page_size - (address % page_size); + } + + err = chip->chip_drv->set_write_protect(chip, false); + + if (err == ESP_OK) { + err = chip->chip_drv->program_page(chip, buffer, address, page_len); + address += page_len; + buffer = (void *)((intptr_t)buffer + page_len); + length -= page_len; + } + } + if (err == ESP_OK && chip->host->flush_cache) { + err = chip->host->flush_cache(chip->host, address, length); + } + return err; +} + +esp_err_t spi_flash_chip_generic_write_encrypted(esp_flash_t *chip, const void *buffer, uint32_t address, uint32_t length) +{ + return ESP_ERR_FLASH_UNSUPPORTED_HOST; // TODO +} + +esp_err_t spi_flash_chip_generic_write_enable(esp_flash_t *chip, bool write_protect) +{ + esp_err_t err = ESP_OK; + + err = chip->chip_drv->wait_idle(chip, DEFAULT_IDLE_TIMEOUT); + + if (err == ESP_OK) { + chip->host->set_write_protect(chip->host, write_protect); + } + + uint8_t status; + err = chip->host->read_status(chip->host, &status); + if (err != ESP_OK) { + return err; + } + + if ((status & SR_WREN) == 0) { + // WREN flag has not been set! + err = ESP_ERR_NOT_FOUND; + } + + return err; +} + +esp_err_t spi_flash_generic_wait_host_idle(esp_flash_t *chip, uint32_t *timeout_ms) +{ + while (chip->host->host_idle(chip->host) && *timeout_ms > 0) { + if (*timeout_ms > 1) { + chip->os_func->delay_ms(chip->os_func_data, 1); + } + (*timeout_ms)--; + } + return (*timeout_ms > 0) ? ESP_OK : ESP_ERR_TIMEOUT; +} + +esp_err_t spi_flash_chip_generic_wait_idle(esp_flash_t *chip, uint32_t timeout_ms) +{ + timeout_ms++; // allow at least one pass before timeout, last one has no sleep cycle + + uint8_t status = 0; + while (timeout_ms > 0) { + + esp_err_t err = spi_flash_generic_wait_host_idle(chip, &timeout_ms); + if (err != ESP_OK) { + return err; + } + + err = chip->host->read_status(chip->host, &status); + if (err != ESP_OK) { + return err; + } + if ((status & SR_WIP) == 0) { + break; // Write in progress is complete + } + if (timeout_ms > 1) { + chip->os_func->delay_ms(chip->os_func_data, 1); + } + timeout_ms--; + } + + return (timeout_ms > 0) ? ESP_OK : ESP_ERR_TIMEOUT; +} + +esp_err_t spi_flash_chip_generic_config_host_read_mode(esp_flash_t *chip) +{ + uint32_t dummy_cyclelen_base; + uint32_t addr_bitlen; + uint32_t read_command; + + switch (chip->read_mode) { + case SPI_FLASH_QIO: + //for QIO mode, the 4 bit right after the address are used for continuous mode, should be set to 0 to avoid that. + addr_bitlen = 32; + dummy_cyclelen_base = 4; + read_command = CMD_FASTRD_QIO; + break; + case SPI_FLASH_QOUT: + addr_bitlen = 24; + dummy_cyclelen_base = 8; + read_command = CMD_FASTRD_QUAD; + break; + case SPI_FLASH_DIO: + //for DIO mode, the 4 bit right after the address are used for continuous mode, should be set to 0 to avoid that. + addr_bitlen = 28; + dummy_cyclelen_base = 2; + read_command = CMD_FASTRD_DIO; + break; + case SPI_FLASH_DOUT: + addr_bitlen = 24; + dummy_cyclelen_base = 8; + read_command = CMD_FASTRD_DUAL; + break; + case SPI_FLASH_FASTRD: + addr_bitlen = 24; + dummy_cyclelen_base = 8; + read_command = CMD_FASTRD; + break; + case SPI_FLASH_SLOWRD: + addr_bitlen = 24; + dummy_cyclelen_base = 0; + read_command = CMD_READ; + break; + default: + return ESP_ERR_FLASH_NOT_INITIALISED; + } + + return chip->host->configure_host_read_mode(chip->host, chip->read_mode, addr_bitlen, dummy_cyclelen_base, read_command); +} + +esp_err_t spi_flash_common_set_read_mode(esp_flash_t *chip, uint8_t qe_rdsr_command, uint8_t qe_wrsr_command, uint8_t qe_sr_bitwidth, unsigned qe_sr_bit) +{ + if (spi_flash_is_quad_mode(chip)) { + // Ensure quad modes are enabled, using the Quad Enable parameters supplied. + spi_flash_trans_t t = { + .command = qe_rdsr_command, + .mosi_data = 0, + .mosi_len = 0, + .miso_len = qe_sr_bitwidth, + }; + chip->host->common_command(chip->host, &t); + unsigned sr = t.miso_data[0]; + ESP_EARLY_LOGV(TAG, "set_read_mode: status before 0x%x", sr); + if ((sr & qe_sr_bit) == 0) { + //some chips needs the write protect to be disabled before writing to Status Register + chip->chip_drv->set_write_protect(chip, false); + + sr |= qe_sr_bit; + spi_flash_trans_t t = { + .command = qe_wrsr_command, + .mosi_data = sr, + .mosi_len = qe_sr_bitwidth, + .miso_len = 0, + }; + chip->host->common_command(chip->host, &t); + + /* Check the new QE bit has stayed set */ + spi_flash_trans_t t_rdsr = { + .command = qe_rdsr_command, + .mosi_data = 0, + .mosi_len = 0, + .miso_len = qe_sr_bitwidth + }; + chip->host->common_command(chip->host, &t_rdsr); + sr = t_rdsr.miso_data[0]; + ESP_EARLY_LOGV(TAG, "set_read_mode: status after 0x%x", sr); + if ((sr & qe_sr_bit) == 0) { + return ESP_ERR_FLASH_NO_RESPONSE; + } + + chip->chip_drv->set_write_protect(chip, true); + } + } + return ESP_OK; +} + +esp_err_t spi_flash_chip_generic_set_read_mode(esp_flash_t *chip) +{ + // On "generic" chips, this involves checking + // bit 1 (QE) of RDSR2 (35h) result + // (it works this way on GigaDevice & Fudan Micro chips, probably others...) + const uint8_t BIT_QE = 1 << 1; + return spi_flash_common_set_read_mode(chip, CMD_RDSR2, CMD_WRSR2, 8, BIT_QE); +} + +static const char chip_name[] = "generic"; + +const spi_flash_chip_t esp_flash_chip_generic = { + .name = chip_name, + .probe = spi_flash_chip_generic_probe, + .reset = spi_flash_chip_generic_reset, + .detect_size = spi_flash_chip_generic_detect_size, + .erase_chip = spi_flash_chip_generic_erase_chip, + .erase_sector = spi_flash_chip_generic_erase_sector, + .erase_block = spi_flash_chip_generic_erase_block, + .sector_size = 4 * 1024, + .block_erase_size = 64 * 1024, + + // TODO: figure out if generic chip-wide protection bits exist across some manufacturers + .get_chip_write_protect = NULL, + .set_chip_write_protect = NULL, + + // Chip write protection regions do not appear to be standardised + // at all, this is implemented in chip-specific drivers only. + .num_protectable_regions = 0, + .protectable_regions = NULL, + .get_protected_regions = NULL, + .set_protected_regions = NULL, + + .read = spi_flash_chip_generic_read, + .write = spi_flash_chip_generic_write, + .program_page = spi_flash_chip_generic_page_program, + .page_size = 256, + .write_encrypted = spi_flash_chip_generic_write_encrypted, + + .set_write_protect = spi_flash_chip_generic_write_enable, + .wait_idle = spi_flash_chip_generic_wait_idle, + .set_read_mode = spi_flash_chip_generic_set_read_mode, +}; diff --git a/components/spi_flash/spi_flash_lowlevel_issi.c b/components/spi_flash/spi_flash_chip_issi.c similarity index 53% rename from components/spi_flash/spi_flash_lowlevel_issi.c rename to components/spi_flash/spi_flash_chip_issi.c index f97ec5a0c0..d32c3a1929 100644 --- a/components/spi_flash/spi_flash_lowlevel_issi.c +++ b/components/spi_flash/spi_flash_chip_issi.c @@ -1,9 +1,9 @@ -// Copyright 2017 Espressif Systems (Shanghai) PTE LTD +// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD // // 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 @@ -11,44 +11,49 @@ // 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 "spi_flash_lowlevel_driver.h" -#include "spi_flash_lowlevel_generic.h" +#include "spi_flash_chip_generic.h" +#include "spi_flash_defs.h" /* Driver for ISSI flash chip, as used in ESP32 D2WD */ -esp_flash_err_t issi_probe(esp_flash_chip_t *chip, uint32_t flash_id) +esp_err_t spi_flash_chip_issi_probe(esp_flash_t *chip, uint32_t flash_id) { /* Check manufacturer and product IDs match our desired masks */ const uint8_t MFG_ID = 0x9D; if (flash_id >> 16 != MFG_ID) { - return FLASH_ERR_NOT_FOUND; + return ESP_ERR_NOT_FOUND; } const uint16_t FLASH_ID_MASK = 0xCF00; const uint16_t FLASH_ID_VALUE = 0x4000; if ((flash_id & FLASH_ID_MASK) != FLASH_ID_VALUE) { - return FLASH_ERR_NOT_FOUND; + return ESP_ERR_NOT_FOUND; } - return FLASH_OK; + return ESP_OK; } -esp_flash_err_t issi_set_read_mode(const esp_flash_chip_t *chip) +esp_err_t spi_flash_chip_issi_set_read_mode(esp_flash_t *chip) { /* ISSI uses bit 6 of "basic" SR as Quad Enable */ - const uint8_t BIT_QE = 1<<6; + const uint8_t BIT_QE = 1 << 6; return spi_flash_common_set_read_mode(chip, CMD_RDSR, CMD_WRSR, 8, BIT_QE); } +static const char chip_name[] = "issi"; -const esp_flash_driver_t esp_flash_issi_chip_driver = { - .probe = issi_probe, - .read_id = spi_flash_generic_read_id, - .detect_size = spi_flash_generic_detect_size, - .erase_chip = spi_flash_generic_erase_chip, - .erase_sector = spi_flash_generic_erase_sector, - .erase_block = spi_flash_generic_erase_block, +// The issi chip can use the functions for generic chips except from set read mode and probe, +// So we only replace these two functions. +const spi_flash_chip_t esp_flash_chip_issi = { + .name = chip_name, + .probe = spi_flash_chip_issi_probe, + .reset = spi_flash_chip_generic_reset, + .detect_size = spi_flash_chip_generic_detect_size, + .erase_chip = spi_flash_chip_generic_erase_chip, + .erase_sector = spi_flash_chip_generic_erase_sector, + .erase_block = spi_flash_chip_generic_erase_block, .sector_size = 4 * 1024, .block_erase_size = 64 * 1024, @@ -62,13 +67,13 @@ const esp_flash_driver_t esp_flash_issi_chip_driver = { .get_protected_regions = NULL, .set_protected_regions = NULL, - .read = spi_flash_generic_read, - .write = spi_flash_generic_write, - .page_program = spi_flash_generic_page_program, + .read = spi_flash_chip_generic_read, + .write = spi_flash_chip_generic_write, + .program_page = spi_flash_chip_generic_page_program, .page_size = 256, - .write_encrypted = spi_flash_generic_write_encrypted, + .write_encrypted = spi_flash_chip_generic_write_encrypted, - .write_enable = spi_flash_generic_write_enable, - .wait_idle = spi_flash_generic_wait_idle, - .set_read_mode = issi_set_read_mode, + .set_write_protect = spi_flash_chip_generic_write_enable, + .wait_idle = spi_flash_chip_generic_wait_idle, + .set_read_mode = spi_flash_chip_issi_set_read_mode, }; diff --git a/components/spi_flash/spi_flash_lowlevel_api.c b/components/spi_flash/spi_flash_lowlevel_api.c deleted file mode 100644 index 0a3d6f29a5..0000000000 --- a/components/spi_flash/spi_flash_lowlevel_api.c +++ /dev/null @@ -1,552 +0,0 @@ -// Copyright 2017 Espressif Systems (Shanghai) PTE LTD -// -// 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 -#include -#include - -#include "spi_flash_lowlevel_driver.h" -#include "spi_flash_lowlevel_generic.h" -#include "soc/spi_reg.h" - -#define MAX_WRITE_CHUNK 8192 /* write in chunks */ - -/* Static function to notify OS of a new SPI flash operation. - - If returns an error result, caller must abort. If returns FLASH_OK, caller must - call spiflash_end() before returning. -*/ -static esp_flash_err_t spiflash_start(const esp_flash_chip_t *chip) -{ - if (esp_flash_os_functions != NULL - && esp_flash_os_functions->start != NULL) { - esp_flash_err_t err = esp_flash_os_functions->start(chip); - if (err != FLASH_OK) { - return err; - } - } - return FLASH_OK; -} - -/* Static function to notify OS that SPI flash operation is complete. - */ -static esp_flash_err_t spiflash_end(const esp_flash_chip_t *chip, esp_flash_err_t err) -{ - if (esp_flash_os_functions != NULL - && esp_flash_os_functions->end != NULL) { - esp_flash_err_t end_err = esp_flash_os_functions->end(chip); - if (err == FLASH_OK) { - err = end_err; // Only return the 'end' error if we haven't already failed - } - } - return err; -} - -/* Return true if regions 'a' and 'b' overlap at all, based on their start offsets and lengths. */ -inline static bool regions_overlap(uint32_t a_start, uint32_t a_len,uint32_t b_start, uint32_t b_len); - -/* Top-level API functions, calling into driver functions via chip->drv */ - -static esp_flash_err_t detect_spi_flash_chip(esp_flash_chip_t *chip); - -esp_flash_err_t esp_flash_init(esp_flash_chip_t *chip) -{ - if (chip->spi == NULL) { - return FLASH_ERR_INVALID_ARG; - } - - // TODO: configure SPI host clock speed, pin configuration - - if (chip->drv == NULL) { - // Detect driver - esp_flash_err_t err = detect_spi_flash_chip(chip); - if (err != FLASH_OK) { - return err; - } - } - - esp_flash_err_t err = spiflash_start(chip); - if (err != FLASH_OK) { - return err; - } - - if (chip->size == 0) { - // Detect flash size - err = chip->drv->detect_size(chip, &chip->size); - } - - if (err == FLASH_OK) { - // Try to set the flash mode to whatever default mode was chosen - // (this isn't necessary at this point for functionality, but init will fail - // if this mode can't be set on this chip.) - err = chip->drv->set_read_mode(chip); - } - - // Done: all fields on 'chip' are initialised - return spiflash_end(chip, err); -} - -static esp_flash_err_t detect_spi_flash_chip(esp_flash_chip_t *chip) -{ - esp_flash_err_t err; - uint32_t flash_id; - int retries = 10; - do { - err = spiflash_start(chip); - if (err != FLASH_OK) { - return err; - } - - // Send generic RDID command twice, check for a matching result and retry in case we just powered on (inner - // function fails if it sees all-ones or all-zeroes.) - err = spi_flash_generic_read_id(chip, &flash_id); - - if (err == FLASH_OK) { // check we see the same ID twice, in case of transient power-on errors - uint32_t new_id; - err = spi_flash_generic_read_id(chip, &new_id); - if (err == FLASH_OK && (new_id != flash_id)) { - err = FLASH_ERR_NOT_INITIALISED; - } - } - - err = spiflash_end(chip, err); - } while (err != FLASH_OK && retries-- > 0); - - // Detect the chip and set the driver structure for it - const esp_flash_driver_t **drivers = esp_flash_registered_flash_drivers; - while (*drivers != NULL && chip->drv == NULL) { - chip->drv = *drivers; - - // start/end SPI operation each time, for multitasking - // and also so esp_flash_registered_flash_drivers can live in flash - err = spiflash_start(chip); - if (err != FLASH_OK) { - return err; - } - - if (chip->drv->probe(chip, flash_id) != FLASH_OK) { - chip->drv = NULL; - } - // if probe succeeded, chip->drv stays set - drivers++; - - err = spiflash_end(chip, err); - if (err != FLASH_OK) { - return err; - } - } - - return (chip->drv == NULL) ? FLASH_ERR_NOT_FOUND : FLASH_OK; -} - -// Convenience macro for beginning of all API functions, -// check that the 'chip' parameter is properly initialised -// and supports the operation in question -#define VERIFY_OP(OP) do { \ - if (chip == NULL) { \ - chip = esp_flash_default_chip; \ - } \ - if (chip == NULL || chip->drv == NULL) { \ - return FLASH_ERR_NOT_INITIALISED; \ - } \ - if (chip->drv->OP == NULL) { \ - return FLASH_ERR_UNSUPPORTED_CHIP; \ - } \ - } while (0) - -esp_flash_err_t esp_flash_read_id(const esp_flash_chip_t *chip, uint32_t *id) -{ - printf("chip %p esp_flash_default_chip %p\n", - chip, esp_flash_default_chip); - VERIFY_OP(read_id); - if (id == NULL) { - return FLASH_ERR_INVALID_ARG; - } - esp_flash_err_t err = spiflash_start(chip); - if (err != FLASH_OK) { - return err; - } - - err = chip->drv->read_id(chip, id); - - return spiflash_end(chip, err); -} - -esp_flash_err_t esp_flash_detect_size(const esp_flash_chip_t *chip, uint32_t *size) -{ - VERIFY_OP(detect_size); - if (size == NULL) { - return FLASH_ERR_INVALID_ARG; - } - *size = 0; - - esp_flash_err_t err = spiflash_start(chip); - if (err != FLASH_OK) { - return err; - } - - err = chip->drv->detect_size(chip, size); - - return spiflash_end(chip, err); -} - -esp_flash_err_t esp_flash_erase_chip(const esp_flash_chip_t *chip) -{ - VERIFY_OP(erase_chip); - bool write_protect = false; - - esp_flash_err_t err = spiflash_start(chip); - if (err != FLASH_OK) { - return err; - } - - err = esp_flash_get_chip_write_protect(chip, &write_protect); - - if (err == FLASH_OK && write_protect) { - err = FLASH_ERR_PROTECTED; - } - - if (err == FLASH_OK) { - err = chip->drv->erase_chip(chip); - } - - return spiflash_end(chip, err); -} - -esp_flash_err_t esp_flash_erase_region(const esp_flash_chip_t *chip, uint32_t start, uint32_t len) -{ - VERIFY_OP(erase_sector); - uint32_t block_erase_size = chip->drv->erase_block == NULL ? 0 : chip->drv->block_erase_size; - uint32_t sector_size = chip->drv->sector_size; - bool write_protect = false; - - if (sector_size == 0 || (block_erase_size % sector_size) != 0) { - return FLASH_ERR_NOT_INITIALISED; - } - if (start > chip->size || start + len > chip->size) { - return FLASH_ERR_INVALID_ARG; - } - if ((start % chip->drv->sector_size) != 0 || (len % chip->drv->sector_size) != 0) { - // Can only erase multiples of the sector size, starting at sector boundary - return FLASH_ERR_INVALID_ARG; - } - - esp_flash_err_t err = spiflash_start(chip); - if (err != FLASH_OK) { - return err; - } - - // Check for write protection on whole chip - if (chip->drv->get_chip_write_protect != NULL) { - err = chip->drv->get_chip_write_protect(chip, &write_protect); - if (err == FLASH_OK && write_protect) { - err = FLASH_ERR_PROTECTED; - } - } - - // Check for write protected regions overlapping the erase region - if (err == FLASH_OK && chip->drv->get_protected_regions != NULL && chip->drv->num_protectable_regions > 0) { - uint64_t protected = 0; - err = chip->drv->get_protected_regions(chip, &protected); - if (protected != 0) { - for (int i = 0; i < chip->drv->num_protectable_regions && err == FLASH_OK; i++) { - const esp_flash_region_t *region = &chip->drv->protectable_regions[i]; - if ((protected & (1LL << i)) - && regions_overlap(start, len, region->offset, region->size)) { - err = FLASH_ERR_PROTECTED; - } - } - } - } - - // Don't lock the SPI flash for the entire erase, as this may be very long - err = spiflash_end(chip, err); - - while (err == FLASH_OK && len >= sector_size) { - esp_flash_err_t err = spiflash_start(chip); - if (err != FLASH_OK) { - return err; - } - - // If possible erase an entire multi-sector block - if (block_erase_size > 0 && len >= block_erase_size && (start % block_erase_size) == 0) { - err = chip->drv->erase_block(chip, start); - start += block_erase_size; - len -= block_erase_size; - } - else { - // Otherwise erase individual sector only - err = chip->drv->erase_sector(chip, start); - start += sector_size; - len -= sector_size; - } - - err = spiflash_end(chip, err); - } - - return err; -} - -esp_flash_err_t esp_flash_get_chip_write_protect(const esp_flash_chip_t *chip, bool *write_protected) -{ - VERIFY_OP(get_chip_write_protect); - if (write_protected == NULL) { - return FLASH_ERR_INVALID_ARG; - } - - esp_flash_err_t err = spiflash_start(chip); - if (err != FLASH_OK) { - return err; - } - - err = chip->drv->get_chip_write_protect(chip, write_protected); - - return spiflash_end(chip, err); -} - -esp_flash_err_t esp_flash_set_chip_write_protect(const esp_flash_chip_t *chip, bool write_protect_chip) -{ - VERIFY_OP(set_chip_write_protect); - - esp_flash_err_t err = spiflash_start(chip); - if (err != FLASH_OK) { - return err; - } - - err = chip->drv->set_chip_write_protect(chip, write_protect_chip); - - return spiflash_end(chip, err); -} - -esp_flash_err_t esp_flash_get_protectable_regions(const esp_flash_chip_t *chip, const esp_flash_region_t **regions, uint32_t *num_regions) -{ - if(num_regions != NULL) { - *num_regions = 0; // In case caller doesn't check result - } - VERIFY_OP(get_protected_regions); - - if(regions == NULL || num_regions == NULL) { - return FLASH_ERR_INVALID_ARG; - } - - *num_regions = chip->drv->num_protectable_regions; - *regions = chip->drv->protectable_regions; - return FLASH_OK; -} - -static esp_flash_err_t find_region(const esp_flash_chip_t *chip, const esp_flash_region_t *region, uint8_t *index) -{ - if (region == NULL) { - return FLASH_ERR_INVALID_ARG; - } - - for(*index = 0; *index < chip->drv->num_protectable_regions; (*index)++) { - if (memcmp(&chip->drv->protectable_regions[*index], - region, sizeof(esp_flash_region_t)) == 0) { - return FLASH_OK; - } - } - - return FLASH_ERR_NOT_FOUND; -} - -esp_flash_err_t esp_flash_get_protected_region(const esp_flash_chip_t *chip, const esp_flash_region_t *region, bool *protected) -{ - VERIFY_OP(get_protected_regions); - - if (protected == NULL) { - return FLASH_ERR_INVALID_ARG; - } - - uint8_t index; - esp_flash_err_t err = find_region(chip, region, &index); - if (err != FLASH_OK) { - return err; - } - - uint64_t protection_mask = 0; - err = spiflash_start(chip); - if (err != FLASH_OK) { - return err; - } - - err = chip->drv->get_protected_regions(chip, &protection_mask); - if (err == FLASH_OK) { - *protected = protection_mask & (1LL << index); - } - - return spiflash_end(chip, err); -} - -esp_flash_err_t esp_flash_set_protected_region(const esp_flash_chip_t *chip, const esp_flash_region_t *region, bool protected) -{ - VERIFY_OP(set_protected_regions); - - uint8_t index; - esp_flash_err_t err = find_region(chip, region, &index); - if (err != FLASH_OK) { - return err; - } - - uint64_t protection_mask = 0; - err = spiflash_start(chip); - if (err != FLASH_OK) { - return err; - } - - err = chip->drv->get_protected_regions(chip, &protection_mask); - if (err == FLASH_OK) { - if (protected) { - protection_mask |= (1LL << index); - } else { - protection_mask &= ~(1LL << index); - } - err = chip->drv->set_protected_regions(chip, protection_mask); - } - - return spiflash_end(chip, err); -} - -esp_flash_err_t esp_flash_read(const esp_flash_chip_t *chip, void *buffer, uint32_t address, uint32_t length) -{ - VERIFY_OP(read); - if (buffer == NULL || address > chip->size || address+length > chip->size) { - return FLASH_ERR_INVALID_ARG; - } - - esp_flash_err_t err = spiflash_start(chip); - if (err != FLASH_OK) { - return err; - } - - if (err == FLASH_OK) { - err = chip->drv->set_read_mode(chip); - } - - if (err == FLASH_OK) { - err = chip->drv->read(chip, buffer, address, length); - } - - return spiflash_end(chip, err); -} - -esp_flash_err_t esp_flash_write(const esp_flash_chip_t *chip, uint32_t address, const void *buffer, uint32_t length) -{ - VERIFY_OP(write); - if (buffer == NULL || address > chip->size || address+length > chip->size) { - return FLASH_ERR_INVALID_ARG; - } - - /* If 'chip' is connected to the main SPI bus, we can only write directly from regions that are accessible - with cache disabled. */ -#ifdef ESP_PLATFORM - bool direct_write = ( chip->spi != &SPI1 - || ( (uintptr_t) address >= 0x3FFAE000 - && (uintptr_t) address < 0x40000000 ) ); -#else - bool direct_write = true; -#endif - - esp_flash_err_t err = FLASH_OK; - - /* Write output in chunks, either by buffering on stack or - by artificially cutting into MAX_WRITE_CHUNK parts (in an OS - environment, this prevents writing from causing interrupt or higher priority task - starvation.) */ - while(err == FLASH_OK && length > 0) { - uint32_t write_len; - const void *write_buf; - if (direct_write) { - write_len = MIN(length, MAX_WRITE_CHUNK); - write_buf = buffer; - } else { - uint32_t buf[8]; - write_len = MIN(length, sizeof(buf)); - memcpy(buf, buffer, write_len); - write_buf = buf; - } - - err = spiflash_start(chip); - if (err != FLASH_OK) { - return err; - } - - err = chip->drv->write(chip, address, write_buf, write_len); - - address += write_len; - buffer = (void *)((intptr_t)buffer + write_len); - length -= write_len; - - err = spiflash_end(chip, err); - } - return err; -} - -esp_flash_err_t esp_flash_write_encrypted(const esp_flash_chip_t *chip, uint32_t address, const void *buffer, uint32_t length) -{ - VERIFY_OP(write_encrypted); - if (chip->spi != 0) { - // Encrypted operations have to use SPI0 - return FLASH_ERR_UNSUPPORTED_HOST; - } - if (buffer == NULL || address > chip->size || address+length > chip->size) { - return FLASH_ERR_INVALID_ARG; - } - - esp_flash_err_t err = spiflash_start(chip); - if (err != FLASH_OK) { - return err; - } - - err = chip->drv->write_encrypted(chip, address, buffer, length); - - return spiflash_end(chip, err); -} - - -inline static bool regions_overlap(uint32_t a_start, uint32_t a_len,uint32_t b_start, uint32_t b_len) -{ - uint32_t a_end = a_start + a_len; - uint32_t b_end = b_start + b_len; - - return ((a_start >= b_start && a_start <= b_end) - || (a_end >= b_start && a_end <= b_end) - || (b_start >= a_start && b_start <= a_end) - || (b_end >= a_start && b_end <= a_end)); -} - -const esp_flash_chip_t *esp_flash_default_chip; - -static esp_flash_chip_t default_chip; - -esp_flash_err_t esp_flash_init_default_chip() -{ - default_chip.spi = &SPI1; - default_chip.read_mode = ESP_FLASH_FASTRD; // TODO: initialise properly - default_chip.speed = ESP_FLASH_20MHZ; // TODO: initialise properly - - // ROM TODO: account for non-standard default pins in efuse - - // ROM TODO: to account for chips which are slow to power on, maybe keep probing in a loop here - - esp_flash_err_t err = esp_flash_init(&default_chip); - if (err != FLASH_OK) { - return err; - } - - esp_flash_default_chip = &default_chip; - return FLASH_OK; -} - -const esp_flash_os_functions_t *esp_flash_os_functions = &esp_flash_noos_functions; diff --git a/components/spi_flash/spi_flash_lowlevel_driver.c b/components/spi_flash/spi_flash_lowlevel_driver.c deleted file mode 100644 index e901ef1e21..0000000000 --- a/components/spi_flash/spi_flash_lowlevel_driver.c +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2017 Espressif Systems (Shanghai) PTE LTD -// -// 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 "spi_flash_lowlevel_driver.h" - -extern const esp_flash_driver_t esp_flash_generic_chip_driver; -extern const esp_flash_driver_t esp_flash_issi_chip_driver; - -/* Default registered chip drivers. - - Note these are tested in order and first match is taken, so generic/catchall entries - should go last. - - Note that the esp_flash_registered_flash_ops pointer can be - changed to point to a different array of registered ops, if desired. -*/ -static const esp_flash_driver_t *default_registered_flash_drivers[] = { - &esp_flash_issi_chip_driver, - &esp_flash_generic_chip_driver, - NULL, -}; - -const esp_flash_driver_t **esp_flash_registered_flash_drivers = default_registered_flash_drivers; diff --git a/components/spi_flash/spi_flash_lowlevel_generic.c b/components/spi_flash/spi_flash_lowlevel_generic.c deleted file mode 100644 index aef068e30f..0000000000 --- a/components/spi_flash/spi_flash_lowlevel_generic.c +++ /dev/null @@ -1,498 +0,0 @@ -// Copyright 2017 Espressif Systems (Shanghai) PTE LTD -// -// 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 // For MIN/MAX -#include - -#include "spi_flash_lowlevel_driver.h" -#include "spi_flash_lowlevel_generic.h" - -#define SPI_FLASH_GENERIC_CHIP_ERASE_TIMEOUT 4000 -#define SPI_FLASH_GENERIC_SECTOR_ERASE_TIMEOUT 500 -#define SPI_FLASH_GENERIC_BLOCK_ERASE_TIMEOUT 1000 - -#define DEFAULT_IDLE_TIMEOUT 200 -#define DEFAULT_PAGE_PROGRAM_TIMEOUT 500 - -/* Hardware host-specific constants */ -#define MAX_WRITE_BYTES 32 -#define MAX_READ_BYTES 64 - -#define ADDRESS_MASK_24BIT 0xFFFFFF - -uint32_t spi_flash_common_command(const esp_flash_chip_t *chip, uint8_t command, uint32_t mosi_data, uint8_t mosi_len, uint8_t miso_len) -{ - typeof(chip->spi->user2) user2 = { - .usr_command_value = command, - .usr_command_bitlen = (8 -1), - }; - chip->spi->user2 = user2; - - typeof(chip->spi->user) user = { - .usr_miso = miso_len > 0, - .usr_mosi = mosi_len > 0, - .usr_dummy = 0, - .usr_command = 1, - }; - chip->spi->user = user; - - chip->spi->ctrl.val = 0; - chip->spi->miso_dlen.usr_miso_dbitlen = miso_len ? (miso_len - 1) : 0; - chip->spi->mosi_dlen.usr_mosi_dbitlen = mosi_len ? (mosi_len - 1) : 0; - - // TODO: there's a bug(?) here where if multiple bytes are written - // with each byte MSB-first (correct), but the bytes are - // written out LSB first... - // - // May be easier to just document this in the function interface... - chip->spi->data_buf[0] = mosi_data; - - if (spi_flash_uses_gpio_matrix(chip)) { - /* When flash pins are mapped via GPIO matrix, need a dummy cycle before reading via MISO */ - if (chip->speed == ESP_FLASH_80MHZ) { - chip->spi->user.usr_dummy = 1; - chip->spi->user1.usr_dummy_cyclelen = 1; - } else { - chip->spi->user.usr_dummy = 1; - chip->spi->user1.usr_dummy_cyclelen = 0; - } - } - - chip->spi->cmd.usr = 1; - while(chip->spi->cmd.usr != 0) - { } - - uint32_t miso = chip->spi->data_buf[0]; - - return miso; -} - -esp_flash_err_t spi_flash_generic_probe(esp_flash_chip_t *chip, uint32_t flash_id) -{ - // This is the catch-all probe function, claim the chip always if nothing - // else has claimed it yet. - return FLASH_OK; -} - -esp_flash_err_t spi_flash_generic_read_id(const esp_flash_chip_t *chip, uint32_t *id) -{ - uint32_t raw_flash_id = spi_flash_common_command(chip, CMD_RDID, 0, 0, 24); - if (raw_flash_id == 0xFFFFFF || raw_flash_id == 0) { - return FLASH_ERR_NO_RESPONSE; - } - - // Byte swap the flash id as it's usually written the other way around - uint8_t mfg_id = raw_flash_id & 0xFF; - uint16_t flash_id = (raw_flash_id >> 16) | (raw_flash_id & 0xFF00); - - *id = ((uint32_t)mfg_id << 16) | flash_id; - return FLASH_OK; -} - - -esp_flash_err_t spi_flash_generic_detect_size(const esp_flash_chip_t *chip, uint32_t *size) -{ - uint32_t id = 0; - *size = 0; - esp_flash_err_t err = chip->drv->read_id(chip, &id); - if (err != FLASH_OK) { - return err; - } - - /* Can't detect size unless the high byte of the product ID matches the same convention, which is usually 0x40 or - * 0xC0 or similar. */ - if ((id & 0x0F00) != 0) { - return FLASH_ERR_UNSUPPORTED_CHIP; - } - - *size = 1 << (id & 0xFF); - - return FLASH_OK; -} - - -esp_flash_err_t spi_flash_generic_erase_chip(const esp_flash_chip_t *chip) -{ - esp_flash_err_t err; - - err = chip->drv->write_enable(chip); - if (err == FLASH_OK) { - err = chip->drv->wait_idle(chip, DEFAULT_IDLE_TIMEOUT); - } - if (err == FLASH_OK) { - chip->spi->ctrl.val = 0; - chip->spi->cmd.flash_ce = 1; - while(chip->spi->cmd.val != 0) { } - err = chip->drv->wait_idle(chip, SPI_FLASH_GENERIC_CHIP_ERASE_TIMEOUT); - } - - return err; -} - -esp_flash_err_t spi_flash_generic_erase_sector(const esp_flash_chip_t *chip, uint32_t start_address) -{ - esp_flash_err_t err = chip->drv->write_enable(chip); - if (err == FLASH_OK) { - err = chip->drv->wait_idle(chip, DEFAULT_IDLE_TIMEOUT); - } - if (err == FLASH_OK) { - chip->spi->user1.usr_addr_bitlen = (24 - 1); - chip->spi->addr = start_address & ADDRESS_MASK_24BIT; - chip->spi->ctrl.val = 0; - chip->spi->cmd.flash_se = 1; - while(chip->spi->cmd.val != 0) { } - err = chip->drv->wait_idle(chip, SPI_FLASH_GENERIC_SECTOR_ERASE_TIMEOUT); - } - return err; -} - -esp_flash_err_t spi_flash_generic_erase_block(const esp_flash_chip_t *chip, uint32_t start_address) -{ - esp_flash_err_t err = chip->drv->write_enable(chip); - if (err == FLASH_OK) { - err = chip->drv->wait_idle(chip, DEFAULT_IDLE_TIMEOUT); - } - if (err == FLASH_OK) { - chip->spi->user1.usr_addr_bitlen = (24 - 1); - chip->spi->addr = start_address & ADDRESS_MASK_24BIT; - chip->spi->cmd.flash_be = 1; - while(chip->spi->cmd.val != 0) { } - err = chip->drv->wait_idle(chip, SPI_FLASH_GENERIC_BLOCK_ERASE_TIMEOUT); - } - return err; -} - -esp_flash_err_t spi_flash_generic_read(const esp_flash_chip_t *chip, void *buffer, uint32_t address, uint32_t length) -{ - esp_flash_err_t err = FLASH_OK; - - while (err == FLASH_OK && length > 0) { - uint32_t read_len = MIN(length, MAX_READ_BYTES); - chip->spi->miso_dlen.usr_miso_dbitlen = (read_len * 8) - 1; - chip->spi->addr = address << 8; - - chip->spi->cmd.usr = 1; - while(chip->spi->cmd.val != 0) {} - - if(((intptr_t)buffer % 4 == 0) && (read_len % 4 == 0)) { - // If everything is word-aligned, do a faster memcpy - xthal_memcpy(buffer, (void *)chip->spi->data_buf, read_len); - length -= read_len; - buffer = (void *)((intptr_t)buffer + read_len); - address += read_len; - } else { - // Otherwise, slow(er) path copies word by word - for (int i = 0; i < (read_len+3)/4; i++) { - int word_len = MIN(sizeof(uint32_t), length); - uint32_t word = chip->spi->data_buf[i]; - xthal_memcpy(buffer, &word, word_len); - length -= word_len; - buffer = (void *)((intptr_t)buffer + word_len); - address += word_len; - } - } - } - - - return err; -} - -esp_flash_err_t spi_flash_generic_page_program(const esp_flash_chip_t *chip, uint32_t address, const void *buffer, uint32_t length) -{ - esp_flash_err_t err; - - err = chip->drv->wait_idle(chip, DEFAULT_IDLE_TIMEOUT); - - if (err == FLASH_OK) { - err = chip->drv->write_enable(chip); - } - - if (err == FLASH_OK) { - // Perform the actual Page Program command - chip->spi->user.usr_dummy = 0; - chip->spi->user1.usr_addr_bitlen = (24 - 1); - chip->spi->addr = (address & ADDRESS_MASK_24BIT) | (length << 24); - - // Load data registers, word at a time - int num_words = (length+3) / 4; - for (int i = 0; i < num_words; i++) { - uint32_t word = 0; - uint32_t word_len = MIN(length, sizeof(word)); - xthal_memcpy(&word, buffer, word_len); - chip->spi->data_buf[i] = word; - length -= word_len; - buffer = (void *)((intptr_t)buffer + word_len); - } - - chip->spi->cmd.flash_pp = 1; - while (chip->spi->cmd.val != 0) { } - - err = chip->drv->wait_idle(chip, DEFAULT_PAGE_PROGRAM_TIMEOUT); - } - - - return err; -} - -esp_flash_err_t spi_flash_generic_write(const esp_flash_chip_t *chip, uint32_t address, const void *buffer, uint32_t length) -{ - esp_flash_err_t err = FLASH_OK; - const uint32_t page_size = chip->drv->page_size; - - while (err == FLASH_OK && length > 0) { - uint32_t page_len = MIN(MAX_WRITE_BYTES, MIN(page_size, length)); - if ((address + page_len) / page_size != address / page_size) { - // Most flash chips can't page write across a page boundary - page_len = page_size - (address % page_size); - } - err = chip->drv->page_program(chip, address, buffer, page_len); - address += page_len; - buffer = (void *)((intptr_t)buffer + page_len); - length -= page_len; - } - - - return err; -} - -esp_flash_err_t spi_flash_generic_write_encrypted(const esp_flash_chip_t *chip, uint32_t address, const void *buffer, uint32_t length) -{ - return FLASH_ERR_UNSUPPORTED_HOST; // TODO -} - -esp_flash_err_t spi_flash_generic_write_enable(const esp_flash_chip_t *chip) -{ - esp_flash_err_t err = FLASH_OK; - - err = chip->drv->wait_idle(chip, DEFAULT_IDLE_TIMEOUT); - - if (err == FLASH_OK) { - chip->spi->cmd.flash_wren = 1; - while(chip->spi->cmd.val != 0) { } - } - - uint8_t status = spi_flash_common_command(chip, CMD_RDSR, 0, 0, 8); - if ((status & SR_WREN) == 0) { - // WREN flag has not been set! - err = FLASH_ERR_NOT_FOUND; - } - - - return err; -} - -esp_flash_err_t spi_flash_generic_wait_host_idle(const esp_flash_chip_t *chip, uint32_t *timeout_ms) -{ - while(chip->spi->ext2.st != 0 && *timeout_ms > 0) { - if (*timeout_ms > 1) { - esp_flash_os_functions->delay_ms(1); - } - (*timeout_ms)--; - } - - // Not clear if this is necessary, or only necessary if - // chip->spi == SPI1. But probably doesn't hurt... - while(SPI0.ext2.st != 0 && *timeout_ms > 0) { - if (*timeout_ms > 1) { - esp_flash_os_functions->delay_ms(1); - } - (*timeout_ms)--; - } - - return (*timeout_ms > 0) ? FLASH_OK : FLASH_ERR_NO_RESPONSE; -} - -esp_flash_err_t spi_flash_generic_wait_idle(const esp_flash_chip_t *chip, uint32_t timeout_ms) -{ - timeout_ms++; // allow at least one pass before timeout, last one has no sleep cycle - - uint8_t status = 0; - while(timeout_ms > 0) { - - esp_flash_err_t err = spi_flash_generic_wait_host_idle(chip, &timeout_ms); - if (err != FLASH_OK) { - return err; - } - - status = spi_flash_common_command(chip, CMD_RDSR, 0, 0, 8); - if ((status & SR_WIP) == 0) { - break; // Write in progress is complete - } - if (timeout_ms > 1) { - esp_flash_os_functions->delay_ms(1); - } - timeout_ms--; - } - - return (timeout_ms > 0) ? FLASH_OK : FLASH_ERR_NO_RESPONSE; -} - -esp_flash_err_t spi_flash_common_configure_host_read_mode(const esp_flash_chip_t *chip) -{ - int dummy_cyclelen, addr_bitlen, read_command; - switch(chip->read_mode) { - case ESP_FLASH_QIO: - addr_bitlen = 32; - dummy_cyclelen = 4; // TODO check this works - read_command = CMD_FASTRD_QIO; - break; - case ESP_FLASH_QOUT: - addr_bitlen = 24; - dummy_cyclelen = 8; // TODO check this works - read_command = CMD_FASTRD_QUAD; - break; - case ESP_FLASH_DIO: - addr_bitlen = 32; - dummy_cyclelen = 0; - read_command = CMD_FASTRD_DIO; - break; - case ESP_FLASH_DOUT: - addr_bitlen = 24; - dummy_cyclelen = 8; - read_command = CMD_FASTRD_DUAL; - break; - case ESP_FLASH_FASTRD: - addr_bitlen = 24; - dummy_cyclelen = 8; - read_command = CMD_FASTRD; - break; - case ESP_FLASH_SLOWRD: - addr_bitlen = 24; - dummy_cyclelen = 0; - read_command = CMD_READ; - break; - default: - return FLASH_ERR_NOT_INITIALISED; - } - - // Add dummy cycles to compensate for GPIO matrix - // latency, if necessary... - if (spi_flash_uses_gpio_matrix(chip)) { - if (chip->speed == ESP_FLASH_80MHZ) { - dummy_cyclelen += 2; - } else if (chip->speed == ESP_FLASH_40MHZ) { - dummy_cyclelen += 1; - } - } - - chip->spi->user1.usr_dummy_cyclelen = (dummy_cyclelen - 1); - chip->spi->user1.usr_addr_bitlen = (addr_bitlen - 1); - chip->spi->user2.usr_command_value = read_command; - chip->spi->user2.usr_command_bitlen = (8 - 1); - - typeof (chip->spi->user) user = { - .usr_command = 1, - .usr_mosi = 0, - .usr_miso = 1, - .usr_dummy = (dummy_cyclelen > 0) ? 1 : 0, - .usr_addr = 1, - }; - chip->spi->user = user; - - typeof (chip->spi->ctrl) ctrl = { - .fread_qio = (chip->read_mode == ESP_FLASH_QIO), - .fread_quad = (chip->read_mode == ESP_FLASH_QOUT), - .fread_dio = (chip->read_mode == ESP_FLASH_DIO), - .fread_dual = (chip->read_mode == ESP_FLASH_DOUT), - .fastrd_mode = (chip->read_mode != ESP_FLASH_SLOWRD), - }; - chip->spi->ctrl = ctrl; - - return FLASH_OK; -} - -#include - -esp_flash_err_t spi_flash_common_set_read_mode(const esp_flash_chip_t *chip, uint8_t qe_rdsr_command, uint8_t qe_wrsr_command, uint8_t qe_sr_bitwidth, unsigned qe_sr_bit) -{ - if (spi_flash_is_quad_mode(chip)) { - // Ensure quad modes are enabled, using the Quad Enable parameters supplied. - unsigned sr = spi_flash_common_command(chip, qe_rdsr_command, 0, 0, qe_sr_bitwidth); - ets_printf("before 0x%x\n", sr); - if ((sr & qe_sr_bit) == 0) { - sr |= qe_sr_bit; - spi_flash_common_command(chip, qe_wrsr_command, sr, qe_sr_bitwidth, 0); - - /* Check the new QE bit has stayed set */ - sr = spi_flash_common_command(chip, qe_rdsr_command, 0, 0, qe_sr_bitwidth); - ets_printf("after 0x%x\n", sr); - if ((sr & qe_sr_bit) == 0) { - return FLASH_ERR_NO_RESPONSE; - } - } - } - - // Configure the host, and return - return spi_flash_common_configure_host_read_mode(chip); -} - -esp_flash_err_t spi_flash_generic_set_read_mode(const esp_flash_chip_t *chip) -{ - // On "generic" chips, this involves checking - // bit 1 (QE) of RDSR2 (35h) result - // (it works this way on GigaDevice & Fudan Micro chips, probably others...) - const uint8_t BIT_QE = 1<<1; - return spi_flash_common_set_read_mode(chip, CMD_RDSR2, CMD_WRSR2, 8, BIT_QE); -} - -bool spi_flash_uses_gpio_matrix(const esp_flash_chip_t *chip) -{ - if (chip->pins == NULL) { - return false; - } - if (chip->pins->mosi_io_num != -1 - || chip->pins->miso_io_num != -1 - || chip->pins->sclk_io_num != -1) { - return true; - } - if (spi_flash_is_quad_mode(chip)) { - if (chip->pins->quadwp_io_num != -1 - || chip->pins->quadhd_io_num != -1) { - return true; - } - } - return false; -} - -const esp_flash_driver_t esp_flash_generic_chip_driver = { - .probe = spi_flash_generic_probe, - .read_id = spi_flash_generic_read_id, - .detect_size = spi_flash_generic_detect_size, - .erase_chip = spi_flash_generic_erase_chip, - .erase_sector = spi_flash_generic_erase_sector, - .erase_block = spi_flash_generic_erase_block, - .sector_size = 4 * 1024, - .block_erase_size = 64 * 1024, - - // TODO: figure out if generic chip-wide protection bits exist across some manufacturers - .get_chip_write_protect = NULL, - .set_chip_write_protect = NULL, - - // Chip write protection regions do not appear to be standardised - // at all, this is implemented in chip-specific drivers only. - .num_protectable_regions = 0, - .protectable_regions = NULL, - .get_protected_regions = NULL, - .set_protected_regions = NULL, - - .read = spi_flash_generic_read, - .write = spi_flash_generic_write, - .page_program = spi_flash_generic_page_program, - .page_size = 256, - .write_encrypted = spi_flash_generic_write_encrypted, - - .write_enable = spi_flash_generic_write_enable, - .wait_idle = spi_flash_generic_wait_idle, - .set_read_mode = spi_flash_generic_set_read_mode, -}; diff --git a/components/spi_flash/spi_flash_lowlevel_idf_app.c b/components/spi_flash/spi_flash_lowlevel_idf_app.c deleted file mode 100644 index 9b71a1e863..0000000000 --- a/components/spi_flash/spi_flash_lowlevel_idf_app.c +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2017 Espressif Systems (Shanghai) PTE LTD -// -// 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 "spi_flash_lowlevel_driver.h" - -#include "rom/ets_sys.h" -#include "esp_attr.h" -#include "esp_spi_flash.h" - -static esp_flash_err_t start(const esp_flash_chip_t *chip) -{ - if (chip->spi == &SPI1) { - g_flash_guard_default_ops.start(); - } - - // TODO figure out if we can coexist with the SPI master driver here, for other peripherals - - return FLASH_OK; -} -static esp_flash_err_t end(const esp_flash_chip_t *chip) -{ - if (chip->spi == &SPI1) { - g_flash_guard_default_ops.end(); - } - - // TODO figure out if we can coexist with the SPI master driver here, for other peripherals - - return FLASH_OK; -} - -static esp_flash_err_t delay_ms(unsigned ms) -{ - ets_delay_us(1000 * ms); - return FLASH_OK; -} - - -const esp_flash_os_functions_t default_os_functions = { - .start = start, - .end = end, - .delay_ms = delay_ms, -}; - -void esp_flash_low_level_app_init() -{ - esp_flash_os_functions = &default_os_functions; -} - - diff --git a/components/spi_flash/spi_flash_os_func_app.c b/components/spi_flash/spi_flash_os_func_app.c new file mode 100644 index 0000000000..9b9738becf --- /dev/null +++ b/components/spi_flash/spi_flash_os_func_app.c @@ -0,0 +1,125 @@ +// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// +// 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 "esp32/rom/ets_sys.h" +#include "esp_attr.h" +#include "esp_spi_flash.h" //for ``g_flash_guard_default_ops`` +#include "esp_flash.h" + +/* + * OS functions providing delay service and arbitration among chips, and with the cache. + * + * The cache needs to be disabled when chips on the SPI1 bus is under operation, hence these functions need to be put + * into the IRAM,and their data should be put into the DRAM. + */ + +typedef struct { + int host_id; +} app_func_arg_t; + +// in the future we will have arbitration among devices, including flash on the same flash bus +static IRAM_ATTR esp_err_t spi_bus_acquire(int host_id) +{ + return ESP_OK; +} + +static IRAM_ATTR esp_err_t spi_bus_release(int host_id) +{ + return ESP_OK; +} + +//for SPI1, we have to disable the cache and interrupts before using the SPI bus +static IRAM_ATTR esp_err_t spi1_start(void *arg) +{ + g_flash_guard_default_ops.start(); + + spi_bus_acquire(((app_func_arg_t *)arg)->host_id); + + return ESP_OK; +} +static IRAM_ATTR esp_err_t spi1_end(void *arg) +{ + g_flash_guard_default_ops.end(); + + spi_bus_release(((app_func_arg_t *)arg)->host_id); + + return ESP_OK; +} + +static esp_err_t spi23_start(void *arg) +{ + spi_bus_acquire(((app_func_arg_t *)arg)->host_id); + return ESP_OK; +} + +static esp_err_t spi23_end(void *arg) +{ + spi_bus_release(((app_func_arg_t *)arg)->host_id); + return ESP_OK; +} + +static IRAM_ATTR esp_err_t delay_ms(void *arg, unsigned ms) +{ + ets_delay_us(1000 * ms); + return ESP_OK; +} + +static DRAM_ATTR app_func_arg_t spi1_arg = { + .host_id = 0, //for SPI1, +}; + +static app_func_arg_t spi2_arg = { + .host_id = 1, //for SPI2, +}; + +static app_func_arg_t spi3_arg = { + .host_id = 2, //for SPI3, +}; + +//for SPI1, we have to disable the cache and interrupts before using the SPI bus +const DRAM_ATTR esp_flash_os_functions_t spi1_default_os_functions = { + .start = spi1_start, + .end = spi1_end, + .delay_ms = delay_ms, +}; + +const esp_flash_os_functions_t spi23_default_os_functions = { + .start = spi23_start, + .end = spi23_end, + .delay_ms = delay_ms, +}; + +esp_err_t esp_flash_init_os_functions(esp_flash_t *chip, int host_id) +{ + if (host_id == 0) { + //SPI1 + chip->os_func = &spi1_default_os_functions; + chip->os_func_data = &spi1_arg; + } else if (host_id == 1 || host_id == 2) { + //SPI2,3 + chip->os_func = &spi23_default_os_functions; + chip->os_func_data = (host_id == 1) ? &spi2_arg : &spi3_arg; + } else { + return ESP_ERR_INVALID_ARG; + } + return ESP_OK; +} + +esp_err_t esp_flash_app_init() +{ + return esp_flash_init_os_functions(esp_flash_default_chip, 0); +} + + diff --git a/components/spi_flash/spi_flash_lowlevel_noos.c b/components/spi_flash/spi_flash_os_func_noos.c similarity index 54% rename from components/spi_flash/spi_flash_lowlevel_noos.c rename to components/spi_flash/spi_flash_os_func_noos.c index 3fb4b8c4bd..4b494279b0 100644 --- a/components/spi_flash/spi_flash_lowlevel_noos.c +++ b/components/spi_flash/spi_flash_os_func_noos.c @@ -1,9 +1,9 @@ -// Copyright 2017 Espressif Systems (Shanghai) PTE LTD +// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD // // 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 @@ -11,37 +11,34 @@ // 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 "spi_flash_lowlevel_driver.h" +#include "esp_flash.h" -#include "rom/ets_sys.h" -#include "rom/cache.h" +#include "esp32/rom/ets_sys.h" +#include "esp32/rom/cache.h" #include "esp_attr.h" -#include "esp_spi_flash.h" -static esp_flash_err_t start(const esp_flash_chip_t *chip) + +static esp_err_t start(void *arg) { - if (chip->spi == &SPI1) { - Cache_Read_Disable(0); - Cache_Read_Disable(1); - } - return FLASH_OK; + Cache_Read_Disable(0); + Cache_Read_Disable(1); + return ESP_OK; } -static esp_flash_err_t end(const esp_flash_chip_t *chip) +static esp_err_t end(void *arg) { - if (chip->spi == &SPI1) { - Cache_Flush(0); - Cache_Flush(1); - Cache_Read_Enable(0); - Cache_Read_Enable(1); - } - return FLASH_OK; + Cache_Flush(0); + Cache_Flush(1); + Cache_Read_Enable(0); + Cache_Read_Enable(1); + return ESP_OK; } -static esp_flash_err_t delay_ms(unsigned ms) +static esp_err_t delay_ms(void *arg, unsigned ms) { ets_delay_us(1000 * ms); - return FLASH_OK; + return ESP_OK; } const esp_flash_os_functions_t esp_flash_noos_functions = { diff --git a/components/spi_flash/test/test_cache_disabled.c b/components/spi_flash/test/test_cache_disabled.c index ba4e8534e6..747ccdf3ab 100644 --- a/components/spi_flash/test/test_cache_disabled.c +++ b/components/spi_flash/test/test_cache_disabled.c @@ -30,7 +30,7 @@ static IRAM_ATTR void cache_test_task(void *arg) vTaskDelete(NULL); } -TEST_CASE("spi_flash_cache_enabled() works on both CPUs", "[spi_flash]") +TEST_CASE("spi_flash_cache_enabled() works on both CPUs", "[spi_flash][esp_flash]") { result_queue = xQueueCreate(1, sizeof(bool)); diff --git a/components/spi_flash/test/test_esp_flash.c b/components/spi_flash/test/test_esp_flash.c new file mode 100644 index 0000000000..58d6f50a8d --- /dev/null +++ b/components/spi_flash/test/test_esp_flash.c @@ -0,0 +1,549 @@ +#include +#include +#include +#include +#include + +#include +#include "esp_flash.h" +#include "spi_flash_chip_generic.h" +#include +#include "esp_log.h" + +#include + +#include "unity.h" +#include "driver/spi_common.h" +#include "memspi_host_driver.h" +#include "driver/gpio.h" +#include "soc/io_mux_reg.h" + + +#define FUNC_SPI 1 + +static uint8_t sector_buf[4096]; + +// #define TEST_SPI1_CS1 +// #define TEST_SPI2_CS0 +// #define TEST_SPI3_CS0 +#define TEST_SPI_SPEED ESP_FLASH_10MHZ +#define TEST_SPI_READ_MODE SPI_FLASH_FASTRD +//#define FORCE_GPIO_MATRIX + +#ifdef TEST_SPI2_CS0 +#define TEST_HOST HSPI_HOST +#define TEST_CS 0 +#define TEST_CS_PIN HSPI_IOMUX_PIN_NUM_CS +#define HSPI_PIN_NUM_MOSI HSPI_IOMUX_PIN_NUM_MOSI +#define HSPI_PIN_NUM_MISO HSPI_IOMUX_PIN_NUM_MISO +#define HSPI_PIN_NUM_CLK HSPI_IOMUX_PIN_NUM_CLK +#define HSPI_PIN_NUM_HD HSPI_IOMUX_PIN_NUM_HD +#define HSPI_PIN_NUM_WP HSPI_IOMUX_PIN_NUM_WP +#define TEST_INPUT_DELAY 20 +#elif defined TEST_SPI3_CS0 +#define TEST_HOST VSPI_HOST +#define TEST_CS 0 +#define TEST_CS_PIN VSPI_IOMUX_PIN_NUM_CS +#define VSPI_PIN_NUM_MOSI VSPI_IOMUX_PIN_NUM_MOSI +#define VSPI_PIN_NUM_MISO VSPI_IOMUX_PIN_NUM_MISO +#define VSPI_PIN_NUM_CLK VSPI_IOMUX_PIN_NUM_CLK +#define VSPI_PIN_NUM_HD VSPI_IOMUX_PIN_NUM_HD +#define VSPI_PIN_NUM_WP VSPI_IOMUX_PIN_NUM_WP +#define TEST_INPUT_DELAY 0 +#elif defined TEST_SPI1_CS1 +#define TEST_HOST SPI_HOST +#define TEST_CS 1 +// #define TEST_CS_PIN 14 +#define TEST_CS_PIN 16 //the pin which is usually used by the PSRAM +// #define TEST_CS_PIN 27 +#define TEST_INPUT_DELAY 25 + +#define EXTRA_SPI1_CLK_IO 17 //the pin which is usually used by the PSRAM clk + +#else +#define SKIP_EXTENDED_CHIP_TEST +#endif + + +static const char TAG[] = "test_esp_flash"; + + +#ifndef SKIP_EXTENDED_CHIP_TEST + +static esp_flash_t *test_chip = NULL; +static esp_flash_t chip_init; +static spi_flash_host_driver_t chip_host_driver; +static memspi_host_data_t driver_data = {}; + +static void IRAM_ATTR cs_initialize(spi_host_device_t host, int cs_io_num, int cs_num, bool use_iomux) +{ + int spics_in = spi_periph_signal[host].spics_in; + int spics_out = spi_periph_signal[host].spics_out[cs_num]; + uint32_t iomux_reg = GPIO_PIN_MUX_REG[TEST_CS_PIN]; + //to avoid the panic caused by flash data line conflicts during cs line initialization, disable the cache temporarily + //some data from flash to be used should be read before the cache disabling + g_flash_guard_default_ops.start(); + if (use_iomux) { + GPIO.func_in_sel_cfg[spics_in].sig_in_sel = 0; + PIN_INPUT_ENABLE(iomux_reg); + GPIO.func_out_sel_cfg[spics_out].oen_sel = 0; + GPIO.func_out_sel_cfg[spics_out].oen_inv_sel = false; + PIN_FUNC_SELECT(iomux_reg, FUNC_SPI); + } else { + PIN_INPUT_ENABLE(iomux_reg); + if (cs_io_num < 32) { + GPIO.enable_w1ts = (0x1 << cs_io_num); + } else { + GPIO.enable1_w1ts.data = (0x1 << (cs_io_num - 32)); + } + GPIO.pin[cs_io_num].pad_driver = 0; + gpio_matrix_out(cs_io_num, spics_out, false, false); + if (cs_num == 0) { + gpio_matrix_in(cs_io_num, spics_in, false); + } + PIN_FUNC_SELECT(iomux_reg, PIN_FUNC_GPIO); + } + g_flash_guard_default_ops.end(); +} + +static void setup_new_chip(esp_flash_read_mode_t io_mode, esp_flash_speed_t speed) +{ + chip_init = (esp_flash_t) { + .read_mode = io_mode, + }; + +#ifdef TEST_SPI2_CS0 + bool spi_chan_claimed = spicommon_periph_claim(HSPI_HOST, "spi flash"); + TEST_ASSERT(spi_chan_claimed); + + spi_bus_config_t hspi_bus_cfg = { + .mosi_io_num = HSPI_PIN_NUM_MOSI, + .miso_io_num = HSPI_PIN_NUM_MISO, + .sclk_io_num = HSPI_PIN_NUM_CLK, + .quadhd_io_num = HSPI_PIN_NUM_HD, + .quadwp_io_num = HSPI_PIN_NUM_WP, + .max_transfer_sz = 64, + }; +#ifdef FORCE_GPIO_MATRIX + hspi_bus_cfg.quadhd_io_num = 23; +#endif + + uint32_t flags; + esp_err_t ret = spicommon_bus_initialize_io(HSPI_HOST, &hspi_bus_cfg, 0, SPICOMMON_BUSFLAG_MASTER | (&hspi_bus_cfg)->flags, &flags); + TEST_ESP_OK(ret); + bool use_iomux = (flags & SPICOMMON_BUSFLAG_NATIVE_PINS) ? 1 : 0; + + printf("setup flash on SPI2 (HSPI) CS0...\n"); + printf("use iomux:%d\n", use_iomux); + memspi_host_config_t cfg = { + .host_id = 2, + .speed = speed, + .iomux = use_iomux, + .cs_num = TEST_CS, + .input_delay_ns = TEST_INPUT_DELAY, + }; +#elif defined TEST_SPI3_CS0 + bool spi_chan_claimed = spicommon_periph_claim(VSPI_HOST, "spi flash"); + TEST_ASSERT(spi_chan_claimed); + + spi_bus_config_t vspi_bus_cfg = { + .mosi_io_num = VSPI_PIN_NUM_MOSI, + .miso_io_num = VSPI_PIN_NUM_MISO, + .sclk_io_num = VSPI_PIN_NUM_CLK, + .quadhd_io_num = VSPI_PIN_NUM_HD, + .quadwp_io_num = VSPI_PIN_NUM_WP, + .max_transfer_sz = 64, + }; +#ifdef FORCE_GPIO_MATRIX + vspi_bus_cfg.quadhd_io_num = 23; +#endif + + uint32_t flags; + esp_err_t ret = spicommon_bus_initialize_io(VSPI_HOST, &vspi_bus_cfg, 0, SPICOMMON_BUSFLAG_MASTER | (&vspi_bus_cfg)->flags, &flags); + TEST_ESP_OK(ret); + bool use_iomux = (flags & SPICOMMON_BUSFLAG_NATIVE_PINS) ? 1 : 0; + //TEST_ASSERT(use_iomux); + + printf("setup flash on SPI3 (VSPI) CS0...\n"); + printf("use iomux:%d\n", use_iomux); + memspi_host_config_t cfg = { + .host_id = 3, + .speed = speed, + .iomux = use_iomux, + .cs_num = TEST_CS, + .input_delay_ns = TEST_INPUT_DELAY, + }; +#elif defined TEST_SPI1_CS1 + printf("setup flash on SPI1 CS1...\n"); + memspi_host_config_t cfg = { + .host_id = 1, + .speed = speed, + .iomux = true, + .cs_num = TEST_CS, + .input_delay_ns = TEST_INPUT_DELAY, + }; + bool use_iomux = (TEST_CS_PIN == spi_periph_signal[TEST_HOST].spics0_iomux_pin) && (driver_data.cs_num == 0); + +# ifdef EXTRA_SPI1_CLK_IO + gpio_matrix_out(EXTRA_SPI1_CLK_IO, SPICLK_OUT_IDX, 0, 0); +# endif +#endif + + esp_err_t err = memspi_host_init_pointers(&chip_host_driver, &driver_data, &cfg); + cs_initialize(TEST_HOST, TEST_CS_PIN, driver_data.cs_num, use_iomux); + TEST_ESP_OK(err); + chip_init.host = &chip_host_driver; + + esp_flash_init_os_functions(&chip_init, TEST_HOST); + + err = esp_flash_init(&chip_init); + TEST_ESP_OK(err); + test_chip = &chip_init; +} + +void teardown_test_chip() +{ + if (TEST_HOST == HSPI_HOST || TEST_HOST == VSPI_HOST) { + spicommon_periph_free(TEST_HOST); + } +} + +#endif + +static void test_metadata(esp_flash_t *chip) +{ + ESP_LOGI(TAG, "Testing chip %p...", chip); + uint32_t id, size; + TEST_ESP_OK(esp_flash_read_id(chip, &id)); + TEST_ESP_OK(esp_flash_get_size(chip, &size)); + printf("Flash ID %08x detected size %d bytes\n", id, size); +} + +TEST_CASE("SPI flash metadata functions", "[esp_flash]") +{ +#ifndef SKIP_EXTENDED_CHIP_TEST + setup_new_chip(TEST_SPI_READ_MODE, TEST_SPI_SPEED); + test_metadata(test_chip); + teardown_test_chip(); +#endif + test_metadata(NULL); +} + +static uint32_t erase_test_region(esp_flash_t *chip, int num_sectors) +{ + const esp_partition_t *part = get_test_data_partition(); + uint32_t offs = part->address; + + /* chip should be initialised */ + TEST_ASSERT(esp_flash_default_chip != NULL + && esp_flash_chip_driver_initialized(esp_flash_default_chip)); + + TEST_ASSERT(num_sectors * 4096 <= part->size); + + bzero(sector_buf, sizeof(sector_buf)); + + printf("Erase @ 0x%x...\n", offs); + TEST_ASSERT_EQUAL_HEX32(ESP_OK, esp_flash_erase_region(chip, offs, num_sectors * 4096) ); + + printf("Verify erased...\n"); + for (int i = 0; i < num_sectors; i++) { + TEST_ASSERT_EQUAL_HEX32(ESP_OK, esp_flash_read(chip, sector_buf, offs + i * 4096, sizeof(sector_buf))); + + printf("Buffer starts 0x%02x 0x%02x 0x%02x 0x%02x\n", sector_buf[0], sector_buf[1], sector_buf[2], sector_buf[3]); + for (int i = 0; i < sizeof(sector_buf); i++) { + TEST_ASSERT_EQUAL_HEX8(0xFF, sector_buf[i]); + } + } + + return offs; +} + +void test_simple_read_write(void *chip) +{ + ESP_LOGI(TAG, "Testing chip %p...", chip); + uint32_t offs = erase_test_region(chip, 1); + + const int test_seed = 778; + srand(test_seed); + for (int i = 0 ; i < sizeof(sector_buf); i++) { + sector_buf[i] = rand(); + } + + printf("Write %p...\n", (void *)offs); + TEST_ASSERT_EQUAL(ESP_OK, esp_flash_write(chip, sector_buf, offs, sizeof(sector_buf)) ); + + bzero(sector_buf, sizeof(sector_buf)); + + printf("Read back...\n"); + TEST_ASSERT_EQUAL(ESP_OK, esp_flash_read(chip, sector_buf, offs, sizeof(sector_buf)) ); + + printf("Buffer starts 0x%02x 0x%02x 0x%02x 0x%02x\n", sector_buf[0], sector_buf[1], sector_buf[2], sector_buf[3]); + + srand(test_seed); + for (int i = 0; i < sizeof(sector_buf); i++) { + TEST_ASSERT_EQUAL_HEX8(rand() & 0xFF, sector_buf[i]); + } +} + +TEST_CASE("SPI flash simple read/write", "[esp_flash]") +{ + test_simple_read_write(NULL); +#ifndef SKIP_EXTENDED_CHIP_TEST + setup_new_chip(TEST_SPI_READ_MODE, TEST_SPI_SPEED); + test_simple_read_write(test_chip); + teardown_test_chip(); +#endif +} + +void test_unaligned_read_write(void *chip) +{ + ESP_LOGI(TAG, "Testing chip %p...", chip); + uint32_t offs = erase_test_region(chip, 2); + + const char *msg = "i am a message"; + TEST_ASSERT(strlen(msg) + 1 % 4 != 0); + TEST_ASSERT_EQUAL(ESP_OK, esp_flash_write(chip, msg, offs + 1, strlen(msg) + 1) ); + + char buf[strlen(msg) + 1]; + + memset(buf, 0xEE, sizeof(buf)); + + TEST_ASSERT_EQUAL(ESP_OK, esp_flash_read(chip, buf, offs + 1, strlen(msg) + 1) ); + TEST_ASSERT_EQUAL_STRING_LEN(msg, buf, strlen(msg)); + TEST_ASSERT(memcmp(buf, msg, strlen(msg) + 1) == 0); +} + +TEST_CASE("SPI flash unaligned read/write", "[esp_flash]") +{ +#ifndef SKIP_EXTENDED_CHIP_TEST + setup_new_chip(TEST_SPI_READ_MODE, TEST_SPI_SPEED); + test_unaligned_read_write(test_chip); + teardown_test_chip(); +#endif + test_unaligned_read_write(NULL); +} + +void test_single_read_write(void *chip) +{ + ESP_LOGI(TAG, "Testing chip %p...", chip); + uint32_t offs = erase_test_region(chip, 2); + + for (unsigned v = 0; v < 512; v++) { + TEST_ASSERT_EQUAL_HEX(ESP_OK, esp_flash_write(chip, &v, offs + v, 1) ); + } + + for (unsigned v = 0; v < 512; v++) { + uint8_t readback; + TEST_ASSERT_EQUAL_HEX(ESP_OK, esp_flash_read(chip, &readback, offs + v, 1) ); + TEST_ASSERT_EQUAL_HEX8(v, readback); + } +} + +TEST_CASE("SPI flash single byte reads/writes", "[esp_flash]") +{ + test_single_read_write(NULL); +#ifndef SKIP_EXTENDED_CHIP_TEST + setup_new_chip(TEST_SPI_READ_MODE, TEST_SPI_SPEED); + test_single_read_write(test_chip); + teardown_test_chip(); +#endif +} + + +/* this test is notable because it generates a lot of unaligned reads/writes, + and also reads/writes across both a sector boundary & many page boundaries. +*/ +void test_three_byte_read_write(void *chip) +{ + ESP_LOGI(TAG, "Testing chip %p...", chip); + uint32_t offs = erase_test_region(chip, 2); + ets_printf("offs:%X\n", offs); + + for (uint32_t v = 0; v < 2000; v++) { + TEST_ASSERT_EQUAL(ESP_OK, esp_flash_write(chip, &v, offs + 3 * v, 3) ); + } + + for (uint32_t v = 0; v < 2000; v++) { + uint32_t readback; + TEST_ASSERT_EQUAL(ESP_OK, esp_flash_read(chip, &readback, offs + 3 * v, 3) ); + TEST_ASSERT_EQUAL_HEX32(v & 0xFFFFFF, readback & 0xFFFFFF); + } +} + +TEST_CASE("SPI flash three byte reads/writes", "[esp_flash]") +{ +#ifndef SKIP_EXTENDED_CHIP_TEST + setup_new_chip(TEST_SPI_READ_MODE, TEST_SPI_SPEED); + test_three_byte_read_write(test_chip); + teardown_test_chip(); +#endif + test_three_byte_read_write(NULL); +} + +void test_erase_large_region(esp_flash_t *chip) +{ + ESP_LOGI(TAG, "Testing chip %p...", chip); + + const esp_partition_t *part = get_test_data_partition(); + + /* Write some noise at the start and the end of the region */ + const char *ohai = "OHAI"; + uint32_t readback; + TEST_ASSERT_EQUAL(ESP_OK, esp_flash_write(chip, ohai, part->address, 5)); + TEST_ASSERT_EQUAL(ESP_OK, esp_flash_write(chip, ohai, part->address + part->size - 5, 5)); + + /* sanity check what we just wrote. since the partition may haven't been erased, we only check the part which is written to 0. */ + uint32_t written_data = *((const uint32_t *)ohai); + TEST_ASSERT_EQUAL(ESP_OK, esp_flash_read(chip, &readback, part->address + part->size - 5, 4)); + TEST_ASSERT_EQUAL_HEX32(0, readback & (~written_data)); + TEST_ASSERT_EQUAL(ESP_OK, esp_flash_read(chip, &readback, part->address, 4)); + TEST_ASSERT_EQUAL_HEX32(0, readback & (~written_data)); + + /* Erase whole region */ + TEST_ASSERT_EQUAL(ESP_OK, esp_flash_erase_region(chip, part->address, part->size)); + + /* ensure both areas we wrote are now all-FFs */ + TEST_ASSERT_EQUAL(ESP_OK, esp_flash_read(chip, &readback, part->address, 4)); + TEST_ASSERT_EQUAL_HEX32(0xFFFFFFFF, readback); + + TEST_ASSERT_EQUAL(ESP_OK, esp_flash_read(chip, &readback, part->address + part->size - 5, 4)); + TEST_ASSERT_EQUAL_HEX32(0xFFFFFFFF, readback); +} + +TEST_CASE("SPI flash erase large region", "[esp_flash]") +{ + test_erase_large_region(NULL); +#ifndef SKIP_EXTENDED_CHIP_TEST + setup_new_chip(TEST_SPI_READ_MODE, TEST_SPI_SPEED); + test_erase_large_region(test_chip); + teardown_test_chip(); +#endif +} + +static const uint8_t large_const_buffer[16400] = { + 203, // first byte + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, + [50 ... 99] = 2, + [1600 ... 2000] = 3, + [8000 ... 9000] = 77, + [15000 ... 16398] = 8, + 43 // last byte +}; + +static void test_write_large_buffer(esp_flash_t *chip, const uint8_t *source, size_t length); +static void write_large_buffer(esp_flash_t *chip, const esp_partition_t *part, const uint8_t *source, size_t length); +static void read_and_check(esp_flash_t *chip, const esp_partition_t *part, const uint8_t *source, size_t length); + +TEST_CASE("SPI flash test reading with all speed/mode permutations", "[esp_flash]") +{ + const int length = sizeof(large_const_buffer); + uint8_t *source_buf = malloc(length); + TEST_ASSERT_NOT_NULL(source_buf); + srand(778); + for (int i = 0; i < length; i++) { + source_buf[i] = rand(); + } + + const esp_partition_t *part = get_test_data_partition(); + TEST_ASSERT(part->size > length + 2 + SPI_FLASH_SEC_SIZE); + +#ifndef SKIP_EXTENDED_CHIP_TEST + //use the lowest speed to write and read to make sure success + setup_new_chip(TEST_SPI_READ_MODE, ESP_FLASH_SPEED_MIN); + write_large_buffer(test_chip, part, source_buf, length); + read_and_check(test_chip, part, source_buf, length); + teardown_test_chip(); + + esp_flash_read_mode_t io_mode = SPI_FLASH_READ_MODE_MIN; + while (io_mode != SPI_FLASH_READ_MODE_MAX) { + esp_flash_speed_t speed = ESP_FLASH_SPEED_MIN; + while (speed != ESP_FLASH_SPEED_MAX) { + ESP_LOGI(TAG, "test flash io mode: %d, speed: %d", io_mode, speed); + setup_new_chip(io_mode, speed); + read_and_check(test_chip, part, source_buf, length); + teardown_test_chip(); + speed++; + } + io_mode++; + } +#endif + + //test main flash BTW + write_large_buffer(NULL, part, source_buf, length); + read_and_check(NULL, part, source_buf, length); + + free(source_buf); +} + +TEST_CASE("Test esp_flash_write large const buffer", "[esp_flash]") +{ + //buffer in flash + test_write_large_buffer(NULL, large_const_buffer, sizeof(large_const_buffer)); +#ifndef SKIP_EXTENDED_CHIP_TEST + setup_new_chip(TEST_SPI_READ_MODE, TEST_SPI_SPEED); + test_write_large_buffer(test_chip, large_const_buffer, sizeof(large_const_buffer)); + teardown_test_chip(); +#endif +} + +#ifndef SKIP_EXTENDED_CHIP_TEST +TEST_CASE("Test esp_flash_write large RAM buffer", "[esp_flash]") +{ + // buffer in RAM + uint8_t *source_buf = malloc(sizeof(large_const_buffer)); + TEST_ASSERT_NOT_NULL(source_buf); + memcpy(source_buf, large_const_buffer, sizeof(large_const_buffer)); + + setup_new_chip(TEST_SPI_READ_MODE, TEST_SPI_SPEED); + test_write_large_buffer(test_chip, source_buf, sizeof(large_const_buffer)); + teardown_test_chip(); + + free(source_buf); +} +#endif + +static void write_large_buffer(esp_flash_t *chip, const esp_partition_t *part, const uint8_t *source, size_t length) +{ + printf("Writing chip %p, %d bytes from source %p\n", chip, length, source); + + ESP_ERROR_CHECK( esp_flash_erase_region(chip, part->address, (length + SPI_FLASH_SEC_SIZE) & ~(SPI_FLASH_SEC_SIZE - 1)) ); + + // note writing to unaligned address + ESP_ERROR_CHECK( esp_flash_write(chip, source, part->address + 1, length) ); +} + +static void read_and_check(esp_flash_t *chip, const esp_partition_t *part, const uint8_t *source, size_t length) +{ + printf("Checking chip %p, %d bytes\n", chip, length); + uint8_t *buf = malloc(length); + TEST_ASSERT_NOT_NULL(buf); + ESP_ERROR_CHECK( esp_flash_read(chip, buf, part->address + 1, length) ); + TEST_ASSERT_EQUAL_HEX8_ARRAY(source, buf, length); + free(buf); + + // check nothing was written at beginning or end + uint8_t ends[8]; + + ESP_ERROR_CHECK( esp_flash_read(chip, ends, part->address, sizeof(ends)) ); + TEST_ASSERT_EQUAL_HEX8(0xFF, ends[0]); + TEST_ASSERT_EQUAL_HEX8(source[0], ends[1]); + + ESP_ERROR_CHECK( esp_flash_read(chip, ends, part->address + length, sizeof(ends)) ); + + TEST_ASSERT_EQUAL_HEX8(source[length - 1], ends[0]); + TEST_ASSERT_EQUAL_HEX8(0xFF, ends[1]); + TEST_ASSERT_EQUAL_HEX8(0xFF, ends[2]); + TEST_ASSERT_EQUAL_HEX8(0xFF, ends[3]); +} + +static void test_write_large_buffer(esp_flash_t *chip, const uint8_t *source, size_t length) +{ + ESP_LOGI(TAG, "Testing chip %p...", chip); + const esp_partition_t *part = get_test_data_partition(); + TEST_ASSERT(part->size > length + 2 + SPI_FLASH_SEC_SIZE); + + write_large_buffer(chip, part, source, length); + read_and_check(chip, part, source, length); +} + diff --git a/components/spi_flash/test/test_large_flash_writes.c b/components/spi_flash/test/test_large_flash_writes.c index f530ca3788..11824cd11b 100644 --- a/components/spi_flash/test/test_large_flash_writes.c +++ b/components/spi_flash/test/test_large_flash_writes.c @@ -42,13 +42,13 @@ static const uint8_t large_const_buffer[16400] = { static void test_write_large_buffer(const uint8_t *source, size_t length); -TEST_CASE("Test spi_flash_write large const buffer", "[spi_flash]") +TEST_CASE("Test spi_flash_write large const buffer", "[spi_flash][esp_flash]") { // buffer in flash test_write_large_buffer(large_const_buffer, sizeof(large_const_buffer)); } -TEST_CASE("Test spi_flash_write large RAM buffer", "[spi_flash]") +TEST_CASE("Test spi_flash_write large RAM buffer", "[spi_flash][esp_flash]") { // buffer in RAM uint8_t *source_buf = malloc(sizeof(large_const_buffer)); diff --git a/components/spi_flash/test/test_low_level.c b/components/spi_flash/test/test_low_level.c deleted file mode 100644 index da77f892fb..0000000000 --- a/components/spi_flash/test/test_low_level.c +++ /dev/null @@ -1,181 +0,0 @@ -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -static uint8_t sector_buf[4096]; - -TEST_CASE("SPI flash metadata functions", "[spi_flash_ll]") -{ - uint32_t id, size; - - TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_read_id(NULL, &id) ); - - TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_detect_size(NULL, &size) ); - - printf("Flash ID %08x detected size %d bytes\n", id, size); -} - -static uint32_t erase_test_region(int num_sectors) -{ - const esp_partition_t *part = get_test_data_partition(); - uint32_t offs = part->address; - - /* chip should be initialised */ - TEST_ASSERT(esp_flash_default_chip != NULL - && esp_flash_default_chip->drv != NULL); - - TEST_ASSERT(num_sectors * 4096 <= part->size); - - bzero(sector_buf, sizeof(sector_buf)); - - printf("Erase @ 0x%x...\n", offs); - TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_erase_region(NULL, offs, num_sectors * 4096) ); - - printf("Verify erased...\n"); - TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_read(NULL, sector_buf, offs, sizeof(sector_buf)) ); - - printf("Buffer starts 0x%02x 0x%02x 0x%02x 0x%02x\n", sector_buf[0], sector_buf[1], sector_buf[2], sector_buf[3]); - for (int i = 0; i < sizeof(sector_buf); i++) { - TEST_ASSERT_EQUAL_HEX8(0xFF, sector_buf[i]); - } - - return offs; -} - -TEST_CASE("SPI flash simple read/write", "[spi_flash_ll]") -{ - uint32_t offs = erase_test_region(1); - - for (int i =0 ; i < sizeof(sector_buf); i++) { - sector_buf[i] = i & 0xFF; - } - - printf("Write...\n"); - TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_write(NULL, offs, sector_buf, sizeof(sector_buf)) ); - - bzero(sector_buf, sizeof(sector_buf)); - - printf("Read back...\n"); - TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_read(NULL, sector_buf, offs, sizeof(sector_buf)) ); - - printf("Buffer starts 0x%02x 0x%02x 0x%02x 0x%02x\n", sector_buf[0], sector_buf[1], sector_buf[2], sector_buf[3]); - - for (int i = 0; i < sizeof(sector_buf); i++) { - TEST_ASSERT_EQUAL_HEX8(i & 0xFF, sector_buf[i]); - } -} - -TEST_CASE("SPI flash unaligned read/write", "[spi_flash_ll]") -{ - uint32_t offs = erase_test_region(2); - - const char *msg = "i am a message"; - TEST_ASSERT(strlen(msg)+1 % 4 != 0); - TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_write(NULL, offs+1, msg, strlen(msg)+1) ); - - char buf[strlen(msg) + 1]; - - memset(buf, 0xEE, sizeof(buf)); - - TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_read(NULL, buf, offs+1, strlen(msg)+1) ); - TEST_ASSERT_EQUAL_STRING_LEN(msg, buf, strlen(msg)); - TEST_ASSERT(memcmp(buf, msg, strlen(msg)+1) == 0); -} - - -TEST_CASE("SPI flash single byte reads/writes", "[spi_flash_ll]") -{ - uint32_t offs = erase_test_region(2); - - for (unsigned v = 0; v < 512; v++) { - TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_write(NULL, offs+v, &v, 1) ); - } - - for (unsigned v = 0; v < 512; v++) { - uint8_t readback; - TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_read(NULL, &readback, offs+v, 1) ); - TEST_ASSERT_EQUAL_HEX8(v, readback); - } -} - -/* this test is notable because it generates a lot of unaligned reads/writes, - and also reads/writes across both a sector boundary & many page boundaries. -*/ -TEST_CASE("SPI flash three byte reads/writes", "[spi_flash_ll]") -{ - uint32_t offs = erase_test_region(2); - - for (uint32_t v = 0; v < 2000; v++) { - TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_write(NULL, offs+3*v, &v, 3) ); - } - - for (uint32_t v = 0; v < 2000; v++) { - uint32_t readback; - TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_read(NULL, &readback, offs+3*v, 3) ); - TEST_ASSERT_EQUAL_HEX32(v & 0xFFFFFF, readback & 0xFFFFFF); - } -} - -TEST_CASE("SPI flash erase large region", "[spi_flash_ll]") -{ - const esp_partition_t *part = get_test_data_partition(); - - /* Write some noise at the start and the end of the region */ - const char *ohai = "OHAI"; - uint32_t readback; - TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_write(NULL, part->address, ohai, 5)); - TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_write(NULL, part->address + part->size - 5, ohai, 5)); - - /* sanity check what we just wrote */ - TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_read(NULL, &readback, part->address + part->size - 5, 4)); - TEST_ASSERT_EQUAL_HEX32(*((const uint32_t*)ohai), readback); - - /* Erase whole region */ - TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_erase_region(NULL, part->address, part->size)); - - /* ensure both areas we wrote are now all-FFs */ - TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_read(NULL, &readback, part->address, 4)); - TEST_ASSERT_EQUAL_HEX32(0xFFFFFFFF, readback); - - TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_read(NULL, &readback, part->address + part->size - 5, 4)); - TEST_ASSERT_EQUAL_HEX32(0xFFFFFFFF, readback); -} - -TEST_CASE("SPI flash test reading with all speed/mode permutations", "[spi_flash_ll]") -{ - /* Note: this only works if the SPI flash chip supports all these modes & speeds */ - - uint32_t offs = erase_test_region(1); - - /* Write some test data */ - const char *message = "This is some test data."; - TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_write(NULL, offs, message, strlen(message)+1) ); - - // Start by copying the default chip to a structure we can tweak - esp_flash_chip_t chip = *esp_flash_default_chip; - - for (chip.read_mode = 0; - chip.read_mode != ESP_FLASH_READ_MODE_MAX; - chip.read_mode++) { - for (chip.speed = 0; - chip.speed != ESP_FLASH_SPEED_MAX; - chip.speed++) { - printf("mode %d speed %d\n", chip.read_mode, chip.speed); - - TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_init(&chip) ); - - char *buf[strlen(message)+1]; - memset(buf, 0xFF, sizeof(buf)); - TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_read(&chip, buf, offs, sizeof(buf)) ); - TEST_ASSERT_EQUAL_STRING_LEN(message, buf, strlen(message)); - } - } -} diff --git a/components/spi_flash/test/test_mmap.c b/components/spi_flash/test/test_mmap.c index 4cdb775943..fe14a8be05 100644 --- a/components/spi_flash/test/test_mmap.c +++ b/components/spi_flash/test/test_mmap.c @@ -200,7 +200,7 @@ TEST_CASE("Can mmap unordered pages into contiguous memory", "[spi_flash]") pages[i]=startpage+(nopages-1)-i; printf("Offset %x page %d\n", i*0x10000, pages[i]); } - + printf("Attempting mapping of unordered pages to contiguous memory area\n"); spi_flash_mmap_handle_t handle1; @@ -277,7 +277,7 @@ TEST_CASE("flash_mmap invalidates just-written data", "[spi_flash]") TEST_CASE("flash_mmap can mmap after get enough free MMU pages", "[spi_flash]") { - //this test case should make flash size >= 4MB, because max size of Dcache can mapped is 4MB + //this test case should make flash size >= 4MB, because max size of Dcache can mapped is 4MB setup_mmap_tests(); printf("Mapping %x (+%x)\n", start, end - start); diff --git a/components/spi_flash/test/test_out_of_bounds_write.c b/components/spi_flash/test/test_out_of_bounds_write.c index 9b411b0365..bc4a5d5745 100644 --- a/components/spi_flash/test/test_out_of_bounds_write.c +++ b/components/spi_flash/test/test_out_of_bounds_write.c @@ -9,9 +9,9 @@ static const char *data = "blah blah blah"; #if CONFIG_SPI_FLASH_DANGEROUS_WRITE_FAILS -#define TEST_TAGS "[spi_flash]" +#define TEST_TAGS "[spi_flash][esp_flash]" #else // ABORTS -#define TEST_TAGS "[spi_flash][ignore]" +#define TEST_TAGS "[spi_flash][esp_flash][ignore]" #endif TEST_CASE("can't overwrite bootloader", TEST_TAGS) diff --git a/components/spi_flash/test/test_partitions.c b/components/spi_flash/test/test_partitions.c index d859d3dd2c..705f0bed75 100644 --- a/components/spi_flash/test/test_partitions.c +++ b/components/spi_flash/test/test_partitions.c @@ -25,7 +25,7 @@ #include #include -TEST_CASE("Test erase partition", "[spi_flash]") +TEST_CASE("Test erase partition", "[spi_flash][esp_flash]") { const esp_partition_t *part = get_test_data_partition(); diff --git a/components/spi_flash/test/test_read_write.c b/components/spi_flash/test/test_read_write.c index ecc7e23c49..febe95bf80 100644 --- a/components/spi_flash/test/test_read_write.c +++ b/components/spi_flash/test/test_read_write.c @@ -79,7 +79,7 @@ static void IRAM_ATTR test_read(int src_off, int dst_off, int len) spi_flash_disable_interrupts_caches_and_other_cpu(); esp_rom_spiflash_result_t rc = esp_rom_spiflash_write(start, src_buf, sizeof(src_buf)); spi_flash_enable_interrupts_caches_and_other_cpu(); - TEST_ASSERT_EQUAL_INT(rc, ESP_ROM_SPIFLASH_RESULT_OK); + TEST_ASSERT_EQUAL_HEX(rc, ESP_ROM_SPIFLASH_RESULT_OK); memset(dst_buf, 0x55, sizeof(dst_buf)); memset(dst_gold, 0x55, sizeof(dst_gold)); fill(dst_gold + dst_off, src_off, len); @@ -87,7 +87,7 @@ static void IRAM_ATTR test_read(int src_off, int dst_off, int len) TEST_ASSERT_EQUAL_INT(cmp_or_dump(dst_buf, dst_gold, sizeof(dst_buf)), 0); } -TEST_CASE("Test spi_flash_read", "[spi_flash]") +TEST_CASE("Test spi_flash_read", "[spi_flash][esp_flash]") { setup_tests(); #if CONFIG_SPI_FLASH_MINIMAL_TEST @@ -158,14 +158,16 @@ static void IRAM_ATTR test_write(int dst_off, int src_off, int len) fill(dst_gold + dst_off, src_off, len); } ESP_ERROR_CHECK(spi_flash_write(start + dst_off, src_buf + src_off, len)); + spi_flash_disable_interrupts_caches_and_other_cpu(); esp_rom_spiflash_result_t rc = esp_rom_spiflash_read(start, dst_buf, sizeof(dst_buf)); spi_flash_enable_interrupts_caches_and_other_cpu(); - TEST_ASSERT_EQUAL_INT(rc, ESP_ROM_SPIFLASH_RESULT_OK); + TEST_ASSERT_EQUAL_HEX(rc, ESP_ROM_SPIFLASH_RESULT_OK); + TEST_ASSERT_EQUAL_INT(cmp_or_dump(dst_buf, dst_gold, sizeof(dst_buf)), 0); } -TEST_CASE("Test spi_flash_write", "[spi_flash]") +TEST_CASE("Test spi_flash_write", "[spi_flash][esp_flash]") { setup_tests(); #if CONFIG_SPI_FLASH_MINIMAL_TEST diff --git a/components/spi_flash/test/test_spi_flash.c b/components/spi_flash/test/test_spi_flash.c index 7e3f2d228a..0010f86d83 100644 --- a/components/spi_flash/test/test_spi_flash.c +++ b/components/spi_flash/test/test_spi_flash.c @@ -124,7 +124,7 @@ static void read_task(void* varg) { vTaskDelete(NULL); } -TEST_CASE("spi flash functions can run along with IRAM interrupts", "[spi_flash]") +TEST_CASE("spi flash functions can run along with IRAM interrupts", "[spi_flash][esp_flash]") { const size_t size = 128; read_task_arg_t read_arg = { @@ -170,7 +170,7 @@ TEST_CASE("spi flash functions can run along with IRAM interrupts", "[spi_flash] #if portNUM_PROCESSORS > 1 -TEST_CASE("spi_flash deadlock with high priority busy-waiting task", "[spi_flash]") +TEST_CASE("spi_flash deadlock with high priority busy-waiting task", "[spi_flash][esp_flash]") { typedef struct { QueueHandle_t queue; diff --git a/components/spiffs/test_spiffs_host/sdkconfig/sdkconfig.h b/components/spiffs/test_spiffs_host/sdkconfig/sdkconfig.h index 44966c5581..5fd1979b19 100644 --- a/components/spiffs/test_spiffs_host/sdkconfig/sdkconfig.h +++ b/components/spiffs/test_spiffs_host/sdkconfig/sdkconfig.h @@ -17,3 +17,5 @@ #define CONFIG_PARTITION_TABLE_OFFSET 0x8000 #define CONFIG_ESPTOOLPY_FLASHSIZE "8MB" +//currently use the legacy implementation, since the stubs for new HAL are not done yet +#define CONFIG_SPI_FLASH_USE_LEGACY_IMPL diff --git a/components/wear_levelling/test_wl_host/sdkconfig/sdkconfig.h b/components/wear_levelling/test_wl_host/sdkconfig/sdkconfig.h index 0fe66cbbec..d630f1a8e4 100644 --- a/components/wear_levelling/test_wl_host/sdkconfig/sdkconfig.h +++ b/components/wear_levelling/test_wl_host/sdkconfig/sdkconfig.h @@ -4,3 +4,6 @@ #define CONFIG_LOG_DEFAULT_LEVEL 3 #define CONFIG_PARTITION_TABLE_OFFSET 0x8000 #define CONFIG_ESPTOOLPY_FLASHSIZE "8MB" +//currently use the legacy implementation, since the stubs for new HAL are not done yet +#define CONFIG_SPI_FLASH_USE_LEGACY_IMPL + From aebcbd98bf09521045348c7bf8be7898aaf05529 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Thu, 13 Jun 2019 15:59:11 +0300 Subject: [PATCH 071/486] ci: fix a duplicate job the CI. --- .gitlab-ci.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b771bd3011..08285f1d9c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1578,12 +1578,6 @@ UT_001_43: - ESP32_IDF - UT_T1_1 -UT_001_43: - <<: *unit_test_template - tags: - - ESP32_IDF - - UT_T1_1 - UT_001_44: <<: *unit_test_template tags: From f78eea97fe803875a4b0940ce50155cbb870f70b Mon Sep 17 00:00:00 2001 From: zhiweijian Date: Mon, 27 May 2019 21:43:43 +0800 Subject: [PATCH 072/486] Component/bt: add gattc and gatts coex example --- .../bluetooth/gattc_gatts_coex/CMakeLists.txt | 6 + examples/bluetooth/gattc_gatts_coex/Makefile | 10 + examples/bluetooth/gattc_gatts_coex/README.md | 14 + .../gattc_gatts_coex/main/CMakeLists.txt | 4 + .../gattc_gatts_coex/main/component.mk | 4 + .../gattc_gatts_coex/main/gattc_gatts_coex.c | 1028 +++++++++++++++++ .../gattc_gatts_coex/sdkconfig.defaults | 6 + 7 files changed, 1072 insertions(+) create mode 100644 examples/bluetooth/gattc_gatts_coex/CMakeLists.txt create mode 100644 examples/bluetooth/gattc_gatts_coex/Makefile create mode 100644 examples/bluetooth/gattc_gatts_coex/README.md create mode 100644 examples/bluetooth/gattc_gatts_coex/main/CMakeLists.txt create mode 100644 examples/bluetooth/gattc_gatts_coex/main/component.mk create mode 100644 examples/bluetooth/gattc_gatts_coex/main/gattc_gatts_coex.c create mode 100644 examples/bluetooth/gattc_gatts_coex/sdkconfig.defaults diff --git a/examples/bluetooth/gattc_gatts_coex/CMakeLists.txt b/examples/bluetooth/gattc_gatts_coex/CMakeLists.txt new file mode 100644 index 0000000000..f520a3ae27 --- /dev/null +++ b/examples/bluetooth/gattc_gatts_coex/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(gattc_gatts_coex) diff --git a/examples/bluetooth/gattc_gatts_coex/Makefile b/examples/bluetooth/gattc_gatts_coex/Makefile new file mode 100644 index 0000000000..e8b511866a --- /dev/null +++ b/examples/bluetooth/gattc_gatts_coex/Makefile @@ -0,0 +1,10 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := gattc_gatts_coex + +COMPONENT_ADD_INCLUDEDIRS := components/include + +include $(IDF_PATH)/make/project.mk diff --git a/examples/bluetooth/gattc_gatts_coex/README.md b/examples/bluetooth/gattc_gatts_coex/README.md new file mode 100644 index 0000000000..0fb1bf6ce1 --- /dev/null +++ b/examples/bluetooth/gattc_gatts_coex/README.md @@ -0,0 +1,14 @@ +ESP-IDF Gattc and Gatts Coexistence example +============================================== + +This example demonstrates the coexistence of gattc and gatts. + +This example creates a GATT service and starts ADV. The ADV name is `ESP_GATTS_DEMO`, then waits to be connected. At the same time, a gatt client is created, the ADV name is `ESP_GATTS_DEMO`, the device is connected, and the data is exchanged. If the device is not found within 120 seconds, the example will stop scanning. + +ESP-IDF also allows users to create a GATT service via an attribute table, rather than add attributes one by one. And it is recommended for users to use. For more information about this method, please refer to [gatt_server_service_table_demo](../gatt_server_service_table). + +To test this example, you can run the [gatt_client_demo](../gatt_client), which can scan for and connect to this example automatically, and run [gatt_server_demo](../gatt_server), Waiting to be connected. They will start exchanging data once the GATT client has enabled the notification function of the GATT server. + +Please check the [tutorial](tutorial/Gatt_Server_Example_Walkthrough.md) for more information about the gatts part of this example. +Please check the [tutorial](tutorial/Gatt_Client_Example_Walkthrough.md) for more information about the gattc part of this example. + diff --git a/examples/bluetooth/gattc_gatts_coex/main/CMakeLists.txt b/examples/bluetooth/gattc_gatts_coex/main/CMakeLists.txt new file mode 100644 index 0000000000..a3c8592c8d --- /dev/null +++ b/examples/bluetooth/gattc_gatts_coex/main/CMakeLists.txt @@ -0,0 +1,4 @@ +set(COMPONENT_SRCS "gattc_gatts_coex.c") +set(COMPONENT_ADD_INCLUDEDIRS ".") + +register_component() diff --git a/examples/bluetooth/gattc_gatts_coex/main/component.mk b/examples/bluetooth/gattc_gatts_coex/main/component.mk new file mode 100644 index 0000000000..a98f634eae --- /dev/null +++ b/examples/bluetooth/gattc_gatts_coex/main/component.mk @@ -0,0 +1,4 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/examples/bluetooth/gattc_gatts_coex/main/gattc_gatts_coex.c b/examples/bluetooth/gattc_gatts_coex/main/gattc_gatts_coex.c new file mode 100644 index 0000000000..3e4989372c --- /dev/null +++ b/examples/bluetooth/gattc_gatts_coex/main/gattc_gatts_coex.c @@ -0,0 +1,1028 @@ +/* + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include +#include +#include +#include +#include "nvs.h" +#include "nvs_flash.h" +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/event_groups.h" +#include "esp_bt.h" +#include "esp_gap_ble_api.h" +#include "esp_gattc_api.h" +#include "esp_gatt_defs.h" +#include "esp_bt_main.h" +#include "esp_gatt_common_api.h" +#include "esp_gatts_api.h" +#include "esp_bt_defs.h" +#include "esp_system.h" +#include "sdkconfig.h" + + +#define GATTS_SERVICE_UUID_TEST_A 0x00FF +#define GATTS_CHAR_UUID_TEST_A 0xFF01 +#define GATTS_NUM_HANDLE_TEST_A 4 + +#define GATTS_SERVICE_UUID_TEST_B 0x00EE +#define GATTS_CHAR_UUID_TEST_B 0xEE01 +#define GATTS_NUM_HANDLE_TEST_B 4 + +#define GATTS_DEMO_CHAR_VAL_LEN_MAX 0x40 +#define PREPARE_BUF_MAX_SIZE 1024 + +#define adv_config_flag (1 << 0) +#define scan_rsp_config_flag (1 << 1) + +#define GATTS_PROFILE_NUM 2 +#define GATTS_PROFILE_A_APP_ID 0 +#define GATTS_PROFILE_B_APP_ID 1 +#define GATTC_PROFILE_NUM 1 +#define GATTC_PROFILE_C_APP_ID 0 +// gattc +#define REMOTE_SERVICE_UUID 0x00FF +#define REMOTE_NOTIFY_CHAR_UUID 0xFF01 +#define INVALID_HANDLE 0 +#define GATTS_ADV_NAME "ESP_GATTS_DEMO" +#define COEX_TAG "GATTC_GATTS_COEX" +#define NOTIFY_ENABLE 0x0001 +#define INDICATE_ENABLE 0x0002 +#define NOTIFY_INDICATE_DISABLE 0x0000 + +static const char remote_device_name[] = "ESP_GATTS_DEMO"; + +typedef struct { + uint8_t *prepare_buf; + int prepare_len; +} prepare_type_env_t; + +struct gatts_profile_inst { + esp_gatts_cb_t gatts_cb; + uint16_t gatts_if; + uint16_t app_id; + uint16_t conn_id; + uint16_t service_handle; + esp_gatt_srvc_id_t service_id; + uint16_t char_handle; + esp_bt_uuid_t char_uuid; + esp_gatt_perm_t perm; + esp_gatt_char_prop_t property; + uint16_t descr_handle; + esp_bt_uuid_t descr_uuid; +}; + +struct gattc_profile_inst { + esp_gattc_cb_t gattc_cb; + uint16_t gattc_if; + uint16_t app_id; + uint16_t conn_id; + uint16_t service_start_handle; + uint16_t service_end_handle; + uint16_t char_handle; + esp_bd_addr_t remote_bda; +}; + +///Declare the static function +static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); +static void gatts_profile_b_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); +/* Declare static functions */ +static void esp_gattc_cb(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param); +static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param); +static void example_write_event_env(esp_gatt_if_t gatts_if, prepare_type_env_t *prepare_write_env, esp_ble_gatts_cb_param_t *param); +static void example_exec_write_event_env(prepare_type_env_t *prepare_write_env, esp_ble_gatts_cb_param_t *param); + +static esp_gatt_char_prop_t a_property = 0; +static esp_gatt_char_prop_t b_property = 0; +static prepare_type_env_t a_prepare_write_env; +static prepare_type_env_t b_prepare_write_env; +static uint8_t adv_config_done = 0; +static uint8_t char1_str[] = {0x11, 0x22, 0x33}; +static bool connect = false; +static bool get_server = false; +static esp_gattc_char_elem_t *char_elem_result = NULL; +static esp_gattc_descr_elem_t *descr_elem_result = NULL; + +esp_attr_value_t gatts_demo_char1_val = { + .attr_max_len = GATTS_DEMO_CHAR_VAL_LEN_MAX, + .attr_len = sizeof(char1_str), + .attr_value = char1_str, +}; + +static uint8_t service_uuid128[32] = { + /* LSB <--------------------------------------------------------------------------------> MSB */ + //first uuid, 16bit, [12],[13] is the value + 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0xAB, 0xCD, 0x00, 0x00, + //second uuid, 32bit, [12], [13], [14], [15] is the value + 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0xAB, 0xCD, 0xAB, 0xCD, +}; + +static esp_ble_adv_data_t adv_data = { + .set_scan_rsp = false, + .include_name = true, + .include_txpower = true, + .min_interval = 0x20, + .max_interval = 0x40, + .appearance = 0x00, + .manufacturer_len = 0, + .p_manufacturer_data = NULL, + .service_data_len = 0, + .p_service_data = NULL, + .service_uuid_len = 32, + .p_service_uuid = service_uuid128, + .flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT), +}; + +// scan response data +static esp_ble_adv_data_t scan_rsp_data = { + .set_scan_rsp = true, + .include_name = true, + .include_txpower = true, + .min_interval = 0x0006, + .max_interval = 0x0010, + .appearance = 0x00, + .manufacturer_len = 0, + .p_manufacturer_data = NULL, + .service_data_len = 0, + .p_service_data = NULL, + .service_uuid_len = 0, + .p_service_uuid = NULL, + .flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT), +}; + +static esp_ble_adv_params_t adv_params = { + .adv_int_min = 0x20, + .adv_int_max = 0x40, + .adv_type = ADV_TYPE_IND, + .own_addr_type = BLE_ADDR_TYPE_PUBLIC, + .channel_map = ADV_CHNL_ALL, + .adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY, +}; + +/* One gatt-based profile one app_id and one gatts_if, this array will store the gatts_if returned by ESP_GATTS_REG_EVT */ +static struct gatts_profile_inst gatts_profile_tab[GATTS_PROFILE_NUM] = { + [GATTS_PROFILE_A_APP_ID] = { + .gatts_cb = gatts_profile_a_event_handler, + .gatts_if = ESP_GATT_IF_NONE, /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */ + }, + [GATTS_PROFILE_B_APP_ID] = { + .gatts_cb = gatts_profile_b_event_handler, /* This demo does not implement, similar as profile A */ + .gatts_if = ESP_GATT_IF_NONE, /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */ + }, +}; + +static esp_bt_uuid_t remote_filter_service_uuid = { + .len = ESP_UUID_LEN_16, + .uuid = {.uuid16 = REMOTE_SERVICE_UUID,}, +}; + +static esp_bt_uuid_t remote_filter_char_uuid = { + .len = ESP_UUID_LEN_16, + .uuid = {.uuid16 = REMOTE_NOTIFY_CHAR_UUID,}, +}; + +static esp_bt_uuid_t notify_descr_uuid = { + .len = ESP_UUID_LEN_16, + .uuid = {.uuid16 = ESP_GATT_UUID_CHAR_CLIENT_CONFIG,}, +}; + +static esp_ble_scan_params_t ble_scan_params = { + .scan_type = BLE_SCAN_TYPE_ACTIVE, + .own_addr_type = BLE_ADDR_TYPE_PUBLIC, + .scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL, + .scan_interval = 0x50, + .scan_window = 0x30, + .scan_duplicate = BLE_SCAN_DUPLICATE_DISABLE +}; + +/* One gatt-based profile one app_id and one gattc_if, this array will store the gattc_if returned by ESP_GATTS_REG_EVT */ +static struct gattc_profile_inst gattc_profile_tab[GATTC_PROFILE_NUM] = { + [GATTC_PROFILE_C_APP_ID] = { + .gattc_cb = gattc_profile_event_handler, + .gattc_if = ESP_GATT_IF_NONE, /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */ + }, +}; + +static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) +{ + uint8_t *adv_name = NULL; + uint8_t adv_name_len = 0; + + switch (event) { + + case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: + adv_config_done &= (~adv_config_flag); + if (adv_config_done == 0) { + esp_ble_gap_start_advertising(&adv_params); + } + break; + case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT: + adv_config_done &= (~scan_rsp_config_flag); + if (adv_config_done == 0) { + esp_ble_gap_start_advertising(&adv_params); + } + break; + + case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: + //advertising start complete event to indicate advertising start successfully or failed + if (param->adv_start_cmpl.status != ESP_BT_STATUS_SUCCESS) { + ESP_LOGE(COEX_TAG, "Advertising start failed\n"); + } + ESP_LOGI(COEX_TAG, "Advertising start successfully\n"); + break; + case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT: + if (param->adv_stop_cmpl.status != ESP_BT_STATUS_SUCCESS) { + ESP_LOGE(COEX_TAG, "Advertising stop failed\n"); + } else { + ESP_LOGI(COEX_TAG, "Stop adv successfully\n"); + } + break; + case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT: + ESP_LOGI(COEX_TAG, "update connection params status = %d, min_int = %d, max_int = %d,conn_int = %d,latency = %d, timeout = %d\n", + param->update_conn_params.status, + param->update_conn_params.min_int, + param->update_conn_params.max_int, + param->update_conn_params.conn_int, + param->update_conn_params.latency, + param->update_conn_params.timeout); + break; + case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT: + if (param->scan_stop_cmpl.status != ESP_BT_STATUS_SUCCESS) { + ESP_LOGE(COEX_TAG, "scan stop failed, error status = %x\n", param->scan_stop_cmpl.status); + break; + } + ESP_LOGI(COEX_TAG, "ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT, stop scan successfully\n"); + break; + case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: { + ESP_LOGI(COEX_TAG, "ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT, set scan sparameters complete\n"); + //the unit of the duration is second + uint32_t duration = 120; + esp_ble_gap_start_scanning(duration); + break; + } + case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT: + if (param->scan_start_cmpl.status != ESP_BT_STATUS_SUCCESS) { + ESP_LOGE(COEX_TAG, "scan start failed, error status = %x\n", param->scan_start_cmpl.status); + break; + } + ESP_LOGI(COEX_TAG, "ESP_GAP_BLE_SCAN_START_COMPLETE_EVT, scan start success\n"); + break; + case ESP_GAP_BLE_SCAN_RESULT_EVT: { + esp_ble_gap_cb_param_t *scan_result = (esp_ble_gap_cb_param_t *)param; + switch (scan_result->scan_rst.search_evt) { + case ESP_GAP_SEARCH_INQ_RES_EVT: + adv_name = esp_ble_resolve_adv_data(scan_result->scan_rst.ble_adv, + ESP_BLE_AD_TYPE_NAME_CMPL, &adv_name_len); + if (adv_name != NULL) { + if (strlen(remote_device_name) == adv_name_len && strncmp((char *)adv_name, remote_device_name, adv_name_len) == 0) { + if (connect == false) { + connect = true; + ESP_LOGI(COEX_TAG, "connect to the remote device %s\n", remote_device_name); + esp_ble_gap_stop_scanning(); + esp_ble_gattc_open(gattc_profile_tab[GATTC_PROFILE_C_APP_ID].gattc_if, scan_result->scan_rst.bda, scan_result->scan_rst.ble_addr_type, true); + } + } + } + break; + case ESP_GAP_SEARCH_INQ_CMPL_EVT: + ESP_LOGI(COEX_TAG, "ESP_GAP_SEARCH_INQ_CMPL_EVT, scan stop\n"); + break; + default: + break; + } + break; + } + default: + break; + } +} + +static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) +{ + esp_ble_gattc_cb_param_t *p_data = (esp_ble_gattc_cb_param_t *)param; + + switch (event) { + case ESP_GATTC_REG_EVT: + ESP_LOGI(COEX_TAG, "REG_EVT\n"); + esp_err_t scan_ret = esp_ble_gap_set_scan_params(&ble_scan_params); + if (scan_ret) { + ESP_LOGE(COEX_TAG, "set scan params error, error code = %x", scan_ret); + } + break; + case ESP_GATTC_CONNECT_EVT: { + ESP_LOGI(COEX_TAG, "ESP_GATTC_CONNECT_EVT conn_id %d, if %d\n", p_data->connect.conn_id, gattc_if); + gattc_profile_tab[GATTC_PROFILE_C_APP_ID].conn_id = p_data->connect.conn_id; + memcpy(gattc_profile_tab[GATTC_PROFILE_C_APP_ID].remote_bda, p_data->connect.remote_bda, sizeof(esp_bd_addr_t)); + ESP_LOGI(COEX_TAG, "REMOTE BDA:"); + esp_log_buffer_hex(COEX_TAG, gattc_profile_tab[GATTC_PROFILE_C_APP_ID].remote_bda, sizeof(esp_bd_addr_t)); + esp_err_t mtu_ret = esp_ble_gattc_send_mtu_req (gattc_if, p_data->connect.conn_id); + if (mtu_ret) { + ESP_LOGE(COEX_TAG, "config MTU error, error code = %x\n", mtu_ret); + } + break; + } + case ESP_GATTC_OPEN_EVT: + if (param->open.status != ESP_GATT_OK) { + ESP_LOGE(COEX_TAG, "open failed, status %d\n", p_data->open.status); + break; + } + ESP_LOGI(COEX_TAG, "open success\n"); + break; + case ESP_GATTC_DIS_SRVC_CMPL_EVT: + if (param->dis_srvc_cmpl.status != ESP_GATT_OK) { + ESP_LOGE(COEX_TAG, "discover service failed, status %d\n", param->dis_srvc_cmpl.status); + break; + } + ESP_LOGI(COEX_TAG, "discover service complete conn_id %d\n", param->dis_srvc_cmpl.conn_id); + esp_ble_gattc_search_service(gattc_if, param->cfg_mtu.conn_id, &remote_filter_service_uuid); + break; + case ESP_GATTC_CFG_MTU_EVT: + if (param->cfg_mtu.status != ESP_GATT_OK) { + ESP_LOGE(COEX_TAG,"config mtu failed, error status = %x\n", param->cfg_mtu.status); + } + ESP_LOGI(COEX_TAG, "ESP_GATTC_CFG_MTU_EVT, Status %d, MTU %d, conn_id %d\n", param->cfg_mtu.status, param->cfg_mtu.mtu, param->cfg_mtu.conn_id); + break; + case ESP_GATTC_SEARCH_RES_EVT: { + ESP_LOGI(COEX_TAG, "SEARCH RES: conn_id = %x is primary service %d\n", p_data->search_res.conn_id, p_data->search_res.is_primary); + ESP_LOGI(COEX_TAG, "start handle %d end handle %d current handle value %d\n", p_data->search_res.start_handle, p_data->search_res.end_handle, p_data->search_res.srvc_id.inst_id); + if (p_data->search_res.srvc_id.uuid.len == ESP_UUID_LEN_16 && p_data->search_res.srvc_id.uuid.uuid.uuid16 == REMOTE_SERVICE_UUID) { + ESP_LOGI(COEX_TAG, "service found\n"); + get_server = true; + gattc_profile_tab[GATTC_PROFILE_C_APP_ID].service_start_handle = p_data->search_res.start_handle; + gattc_profile_tab[GATTC_PROFILE_C_APP_ID].service_end_handle = p_data->search_res.end_handle; + ESP_LOGI(COEX_TAG, "UUID16: %x\n", p_data->search_res.srvc_id.uuid.uuid.uuid16); + } + break; + } + case ESP_GATTC_SEARCH_CMPL_EVT: + if (p_data->search_cmpl.status != ESP_GATT_OK) { + ESP_LOGE(COEX_TAG, "search service failed, error status = %x\n", p_data->search_cmpl.status); + break; + } + if(p_data->search_cmpl.searched_service_source == ESP_GATT_SERVICE_FROM_REMOTE_DEVICE) { + ESP_LOGI(COEX_TAG, "Get service information from remote device\n"); + } else if (p_data->search_cmpl.searched_service_source == ESP_GATT_SERVICE_FROM_NVS_FLASH) { + ESP_LOGI(COEX_TAG, "Get service information from flash\n"); + } else { + ESP_LOGI(COEX_TAG, "unknown service source\n"); + } + ESP_LOGI(COEX_TAG, "ESP_GATTC_SEARCH_CMPL_EVT\n"); + if (get_server) { + uint16_t count = 0; + esp_gatt_status_t status = esp_ble_gattc_get_attr_count( gattc_if, + p_data->search_cmpl.conn_id, + ESP_GATT_DB_CHARACTERISTIC, + gattc_profile_tab[GATTC_PROFILE_C_APP_ID].service_start_handle, + gattc_profile_tab[GATTC_PROFILE_C_APP_ID].service_end_handle, + INVALID_HANDLE, + &count); + if (status != ESP_GATT_OK) { + ESP_LOGE(COEX_TAG, "esp_ble_gattc_get_attr_count error\n"); + } + + if (count > 0) { + char_elem_result = (esp_gattc_char_elem_t *)malloc(sizeof(esp_gattc_char_elem_t) * count); + if (!char_elem_result) { + ESP_LOGE(COEX_TAG, "gattc no mem\n"); + }else { + status = esp_ble_gattc_get_char_by_uuid( gattc_if, + p_data->search_cmpl.conn_id, + gattc_profile_tab[GATTC_PROFILE_C_APP_ID].service_start_handle, + gattc_profile_tab[GATTC_PROFILE_C_APP_ID].service_end_handle, + remote_filter_char_uuid, + char_elem_result, + &count); + if (status != ESP_GATT_OK) { + ESP_LOGE(COEX_TAG, "esp_ble_gattc_get_char_by_uuid error\n"); + } + + /* Every service have only one char in our 'ESP_GATTS_DEMO' demo, so we used first 'char_elem_result' */ + if (count > 0 && (char_elem_result[0].properties & ESP_GATT_CHAR_PROP_BIT_NOTIFY)) { + gattc_profile_tab[GATTC_PROFILE_C_APP_ID].char_handle = char_elem_result[0].char_handle; + esp_ble_gattc_register_for_notify (gattc_if, gattc_profile_tab[GATTC_PROFILE_C_APP_ID].remote_bda, char_elem_result[0].char_handle); + } + } + /* free char_elem_result */ + free(char_elem_result); + } else { + ESP_LOGE(COEX_TAG, "no char found\n"); + } + } + break; + case ESP_GATTC_REG_FOR_NOTIFY_EVT: { + ESP_LOGI(COEX_TAG, "ESP_GATTC_REG_FOR_NOTIFY_EVT\n"); + if (p_data->reg_for_notify.status != ESP_GATT_OK) { + ESP_LOGE(COEX_TAG, "REG FOR NOTIFY failed: error status = %d\n", p_data->reg_for_notify.status); + } else { + uint16_t count = 0; + uint16_t notify_en = 1; + esp_gatt_status_t ret_status = esp_ble_gattc_get_attr_count( gattc_if, + gattc_profile_tab[GATTC_PROFILE_C_APP_ID].conn_id, + ESP_GATT_DB_DESCRIPTOR, + gattc_profile_tab[GATTC_PROFILE_C_APP_ID].service_start_handle, + gattc_profile_tab[GATTC_PROFILE_C_APP_ID].service_end_handle, + gattc_profile_tab[GATTC_PROFILE_C_APP_ID].char_handle, + &count); + if (ret_status != ESP_GATT_OK) { + ESP_LOGE(COEX_TAG, "esp_ble_gattc_get_attr_count error\n"); + } + if (count > 0) { + descr_elem_result = malloc(sizeof(esp_gattc_descr_elem_t) * count); + if (!descr_elem_result) { + ESP_LOGE(COEX_TAG, "malloc error, gattc no mem\n"); + } else { + ret_status = esp_ble_gattc_get_descr_by_char_handle( gattc_if, + gattc_profile_tab[GATTC_PROFILE_C_APP_ID].conn_id, + p_data->reg_for_notify.handle, + notify_descr_uuid, + descr_elem_result, + &count); + if (ret_status != ESP_GATT_OK) { + ESP_LOGE(COEX_TAG, "esp_ble_gattc_get_descr_by_char_handle error\n"); + } + /* Every char has only one descriptor in our 'ESP_GATTS_DEMO' demo, so we used first 'descr_elem_result' */ + if (count > 0 && descr_elem_result[0].uuid.len == ESP_UUID_LEN_16 && descr_elem_result[0].uuid.uuid.uuid16 == ESP_GATT_UUID_CHAR_CLIENT_CONFIG) { + ret_status = esp_ble_gattc_write_char_descr( gattc_if, + gattc_profile_tab[GATTC_PROFILE_C_APP_ID].conn_id, + descr_elem_result[0].handle, + sizeof(notify_en), + (uint8_t *)¬ify_en, + ESP_GATT_WRITE_TYPE_RSP, + ESP_GATT_AUTH_REQ_NONE); + } + + if (ret_status != ESP_GATT_OK) { + ESP_LOGE(COEX_TAG, "esp_ble_gattc_write_char_descr error\n"); + } + + /* free descr_elem_result */ + free(descr_elem_result); + } + } else { + ESP_LOGE(COEX_TAG, "decsr not found\n"); + } + + } + break; + } + case ESP_GATTC_NOTIFY_EVT: + if (p_data->notify.is_notify) { + ESP_LOGI(COEX_TAG, "ESP_GATTC_NOTIFY_EVT, receive notify value:"); + } else { + ESP_LOGI(COEX_TAG, "ESP_GATTC_NOTIFY_EVT, receive indicate value:"); + } + esp_log_buffer_hex(COEX_TAG, p_data->notify.value, p_data->notify.value_len); + break; + case ESP_GATTC_WRITE_DESCR_EVT: + if (p_data->write.status != ESP_GATT_OK) { + ESP_LOGE(COEX_TAG, "write descr failed, error status = %x\n", p_data->write.status); + break; + } + ESP_LOGI(COEX_TAG, "write descr success \n"); + uint8_t write_char_data[35]; + for (int i = 0; i < sizeof(write_char_data); ++ i) { + write_char_data[i] = i % 256; + } + esp_ble_gattc_write_char( gattc_if, + gattc_profile_tab[GATTC_PROFILE_C_APP_ID].conn_id, + gattc_profile_tab[GATTC_PROFILE_C_APP_ID].char_handle, + sizeof(write_char_data), + write_char_data, + ESP_GATT_WRITE_TYPE_RSP, + ESP_GATT_AUTH_REQ_NONE); + break; + case ESP_GATTC_SRVC_CHG_EVT: { + esp_bd_addr_t bda; + memcpy(bda, p_data->srvc_chg.remote_bda, sizeof(esp_bd_addr_t)); + ESP_LOGI(COEX_TAG, "ESP_GATTC_SRVC_CHG_EVT, bd_addr:"); + esp_log_buffer_hex(COEX_TAG, bda, sizeof(esp_bd_addr_t)); + break; + } + case ESP_GATTC_WRITE_CHAR_EVT: + if (p_data->write.status != ESP_GATT_OK) { + ESP_LOGE(COEX_TAG, "write char failed, error status = %x\n", p_data->write.status); + break; + } + ESP_LOGI(COEX_TAG, "write char success \n"); + break; + case ESP_GATTC_DISCONNECT_EVT: { + connect = false; + get_server = false; + ESP_LOGI(COEX_TAG, "ESP_GATTC_DISCONNECT_EVT, reason = %d\n", p_data->disconnect.reason); + break; + } + default: + break; + } +} + +static void example_write_event_env(esp_gatt_if_t gatts_if, prepare_type_env_t *prepare_write_env, esp_ble_gatts_cb_param_t *param) { + esp_gatt_status_t status = ESP_GATT_OK; + if (param->write.need_rsp) { + if (param->write.is_prep) { + if (prepare_write_env->prepare_buf == NULL) { + prepare_write_env->prepare_buf = (uint8_t *)malloc(PREPARE_BUF_MAX_SIZE*sizeof(uint8_t)); + prepare_write_env->prepare_len = 0; + if (prepare_write_env->prepare_buf == NULL) { + ESP_LOGE(COEX_TAG, "Gatt_server prep no mem\n"); + status = ESP_GATT_NO_RESOURCES; + } + } else { + if(param->write.offset > PREPARE_BUF_MAX_SIZE) { + status = ESP_GATT_INVALID_OFFSET; + } else if ((param->write.offset + param->write.len) > PREPARE_BUF_MAX_SIZE) { + status = ESP_GATT_INVALID_ATTR_LEN; + } + } + + esp_gatt_rsp_t *gatt_rsp = (esp_gatt_rsp_t *)malloc(sizeof(esp_gatt_rsp_t)); + gatt_rsp->attr_value.len = param->write.len; + gatt_rsp->attr_value.handle = param->write.handle; + gatt_rsp->attr_value.offset = param->write.offset; + gatt_rsp->attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE; + memcpy(gatt_rsp->attr_value.value, param->write.value, param->write.len); + esp_err_t response_err = esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, status, gatt_rsp); + if (response_err != ESP_OK) { + ESP_LOGE(COEX_TAG, "Send response error\n"); + } + free(gatt_rsp); + if (status != ESP_GATT_OK) { + return; + } + memcpy(prepare_write_env->prepare_buf + param->write.offset, + param->write.value, + param->write.len); + prepare_write_env->prepare_len += param->write.len; + + } else { + esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, status, NULL); + } + } +} + +static void example_exec_write_event_env(prepare_type_env_t *prepare_write_env, esp_ble_gatts_cb_param_t *param) { + if (param->exec_write.exec_write_flag == ESP_GATT_PREP_WRITE_EXEC) { + esp_log_buffer_hex(COEX_TAG, prepare_write_env->prepare_buf, prepare_write_env->prepare_len); + } else { + ESP_LOGI(COEX_TAG,"ESP_GATT_PREP_WRITE_CANCEL\n"); + } + if (prepare_write_env->prepare_buf) { + free(prepare_write_env->prepare_buf); + prepare_write_env->prepare_buf = NULL; + } + prepare_write_env->prepare_len = 0; +} + +static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) { + switch (event) { + case ESP_GATTS_REG_EVT: + ESP_LOGI(COEX_TAG, "REGISTER_APP_EVT, status %d, app_id %d\n", param->reg.status, param->reg.app_id); + gatts_profile_tab[GATTS_PROFILE_A_APP_ID].service_id.is_primary = true; + gatts_profile_tab[GATTS_PROFILE_A_APP_ID].service_id.id.inst_id = 0x00; + gatts_profile_tab[GATTS_PROFILE_A_APP_ID].service_id.id.uuid.len = ESP_UUID_LEN_16; + gatts_profile_tab[GATTS_PROFILE_A_APP_ID].service_id.id.uuid.uuid.uuid16 = GATTS_SERVICE_UUID_TEST_A; + + esp_err_t set_dev_name_ret = esp_ble_gap_set_device_name(GATTS_ADV_NAME); + if (set_dev_name_ret) { + ESP_LOGE(COEX_TAG, "set device name failed, error code = %x\n", set_dev_name_ret); + } +#ifdef CONFIG_SET_RAW_ADV_DATA + esp_err_t raw_adv_ret = esp_ble_gap_config_adv_data_raw(raw_adv_data, sizeof(raw_adv_data)); + if (raw_adv_ret) { + ESP_LOGE(COEX_TAG, "config raw adv data failed, error code = %x \n", raw_adv_ret); + } + adv_config_done |= adv_config_flag; + esp_err_t raw_scan_ret = esp_ble_gap_config_scan_rsp_data_raw(raw_scan_rsp_data, sizeof(raw_scan_rsp_data)); + if (raw_scan_ret) { + ESP_LOGE(COEX_TAG, "config raw scan rsp data failed, error code = %x\n", raw_scan_ret); + } + adv_config_done |= scan_rsp_config_flag; +#else + //config adv data + esp_err_t ret = esp_ble_gap_config_adv_data(&adv_data); + if (ret) { + ESP_LOGE(COEX_TAG, "config adv data failed, error code = %x\n", ret); + } + adv_config_done |= adv_config_flag; + //config scan response data + ret = esp_ble_gap_config_adv_data(&scan_rsp_data); + if (ret) { + ESP_LOGE(COEX_TAG, "config scan response data failed, error code = %x\n", ret); + } + adv_config_done |= scan_rsp_config_flag; + +#endif + esp_ble_gatts_create_service(gatts_if, &gatts_profile_tab[GATTS_PROFILE_A_APP_ID].service_id, GATTS_NUM_HANDLE_TEST_A); + break; + case ESP_GATTS_READ_EVT: { + ESP_LOGI(COEX_TAG, "GATT_READ_EVT, conn_id %d, trans_id %d, handle %d\n", param->read.conn_id, param->read.trans_id, param->read.handle); + esp_gatt_rsp_t rsp; + memset(&rsp, 0, sizeof(esp_gatt_rsp_t)); + rsp.attr_value.handle = param->read.handle; + rsp.attr_value.len = 4; + rsp.attr_value.value[0] = 0xde; + rsp.attr_value.value[1] = 0xed; + rsp.attr_value.value[2] = 0xbe; + rsp.attr_value.value[3] = 0xef; + esp_ble_gatts_send_response(gatts_if, param->read.conn_id, param->read.trans_id, + ESP_GATT_OK, &rsp); + break; + } + case ESP_GATTS_WRITE_EVT: { + ESP_LOGI(COEX_TAG, "GATT_WRITE_EVT, conn_id %d, trans_id %d, handle %d\n", param->write.conn_id, param->write.trans_id, param->write.handle); + if (!param->write.is_prep) { + ESP_LOGI(COEX_TAG, "GATT_WRITE_EVT, value len %d, value :", param->write.len); + esp_log_buffer_hex(COEX_TAG, param->write.value, param->write.len); + if (gatts_profile_tab[GATTS_PROFILE_A_APP_ID].descr_handle == param->write.handle && param->write.len == 2) { + uint16_t descr_value = param->write.value[1]<<8 | param->write.value[0]; + if (descr_value == NOTIFY_ENABLE) { + if (a_property & ESP_GATT_CHAR_PROP_BIT_NOTIFY) { + ESP_LOGI(COEX_TAG, "notify enable\n"); + uint8_t notify_data[15]; + for (int i = 0; i < sizeof(notify_data); ++ i) { + notify_data[i] = i%0xff; + } + //the size of notify_data[] need less than MTU size + esp_ble_gatts_send_indicate(gatts_if, param->write.conn_id, gatts_profile_tab[GATTS_PROFILE_A_APP_ID].char_handle, + sizeof(notify_data), notify_data, false); + } + } else if (descr_value == INDICATE_ENABLE) { + if (a_property & ESP_GATT_CHAR_PROP_BIT_INDICATE) { + ESP_LOGI(COEX_TAG, "indicate enable\n"); + uint8_t indicate_data[15]; + for (int i = 0; i < sizeof(indicate_data); ++ i) { + indicate_data[i] = i%0xff; + } + //the size of indicate_data[] need less than MTU size + esp_ble_gatts_send_indicate(gatts_if, param->write.conn_id, gatts_profile_tab[GATTS_PROFILE_A_APP_ID].char_handle, + sizeof(indicate_data), indicate_data, true); + } + } else if (descr_value == NOTIFY_INDICATE_DISABLE) { + ESP_LOGI(COEX_TAG, "notify/indicate disable \n"); + } else { + ESP_LOGE(COEX_TAG, "unknown descr value\n"); + esp_log_buffer_hex(COEX_TAG, param->write.value, param->write.len); + } + + } + } + example_write_event_env(gatts_if, &a_prepare_write_env, param); + break; + } + case ESP_GATTS_EXEC_WRITE_EVT: + ESP_LOGI(COEX_TAG,"ESP_GATTS_EXEC_WRITE_EVT\n"); + esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, ESP_GATT_OK, NULL); + example_exec_write_event_env(&a_prepare_write_env, param); + break; + case ESP_GATTS_MTU_EVT: + ESP_LOGI(COEX_TAG, "ESP_GATTS_MTU_EVT, MTU %d\n", param->mtu.mtu); + break; + case ESP_GATTS_CREATE_EVT: + ESP_LOGI(COEX_TAG, "CREATE_SERVICE_EVT, status %d, service_handle %d\n", param->create.status, param->create.service_handle); + gatts_profile_tab[GATTS_PROFILE_A_APP_ID].service_handle = param->create.service_handle; + gatts_profile_tab[GATTS_PROFILE_A_APP_ID].char_uuid.len = ESP_UUID_LEN_16; + gatts_profile_tab[GATTS_PROFILE_A_APP_ID].char_uuid.uuid.uuid16 = GATTS_CHAR_UUID_TEST_A; + + esp_ble_gatts_start_service(gatts_profile_tab[GATTS_PROFILE_A_APP_ID].service_handle); + a_property = ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_WRITE | ESP_GATT_CHAR_PROP_BIT_NOTIFY; + esp_err_t add_char_ret = esp_ble_gatts_add_char(gatts_profile_tab[GATTS_PROFILE_A_APP_ID].service_handle, &gatts_profile_tab[GATTS_PROFILE_A_APP_ID].char_uuid, + ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, + a_property, + &gatts_demo_char1_val, NULL); + if (add_char_ret) { + ESP_LOGE(COEX_TAG, "add char failed, error code =%x\n",add_char_ret); + } + break; + case ESP_GATTS_ADD_CHAR_EVT: { + ESP_LOGI(COEX_TAG, "ADD_CHAR_EVT, status %d, attr_handle %d, service_handle %d\n", + param->add_char.status, param->add_char.attr_handle, param->add_char.service_handle); + gatts_profile_tab[GATTS_PROFILE_A_APP_ID].char_handle = param->add_char.attr_handle; + gatts_profile_tab[GATTS_PROFILE_A_APP_ID].descr_uuid.len = ESP_UUID_LEN_16; + gatts_profile_tab[GATTS_PROFILE_A_APP_ID].descr_uuid.uuid.uuid16 = ESP_GATT_UUID_CHAR_CLIENT_CONFIG; + + esp_err_t add_descr_ret = esp_ble_gatts_add_char_descr(gatts_profile_tab[GATTS_PROFILE_A_APP_ID].service_handle, &gatts_profile_tab[GATTS_PROFILE_A_APP_ID].descr_uuid, + ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, NULL, NULL); + if (add_descr_ret) { + ESP_LOGE(COEX_TAG, "add char descr failed, error code =%x\n", add_descr_ret); + } + break; + } + case ESP_GATTS_ADD_CHAR_DESCR_EVT: + gatts_profile_tab[GATTS_PROFILE_A_APP_ID].descr_handle = param->add_char_descr.attr_handle; + ESP_LOGI(COEX_TAG, "ADD_DESCR_EVT, status %d, attr_handle %d, service_handle %d\n", + param->add_char_descr.status, param->add_char_descr.attr_handle, param->add_char_descr.service_handle); + break; + case ESP_GATTS_START_EVT: + ESP_LOGI(COEX_TAG, "SERVICE_START_EVT, status %d, service_handle %d\n", + param->start.status, param->start.service_handle); + break; + case ESP_GATTS_CONNECT_EVT: { + + ESP_LOGI(COEX_TAG, "ESP_GATTS_CONNECT_EVT, conn_id %d, remote %02x:%02x:%02x:%02x:%02x:%02x\n", + param->connect.conn_id, + param->connect.remote_bda[0], param->connect.remote_bda[1], param->connect.remote_bda[2], + param->connect.remote_bda[3], param->connect.remote_bda[4], param->connect.remote_bda[5]); + gatts_profile_tab[GATTS_PROFILE_A_APP_ID].conn_id = param->connect.conn_id; + break; + } + case ESP_GATTS_DISCONNECT_EVT: + ESP_LOGI(COEX_TAG, "ESP_GATTS_DISCONNECT_EVT, disconnect reason 0x%x\n", param->disconnect.reason); + esp_ble_gap_start_advertising(&adv_params); + break; + case ESP_GATTS_CONF_EVT: + ESP_LOGI(COEX_TAG, "ESP_GATTS_CONF_EVT, status %d attr_handle %d\n", param->conf.status, param->conf.handle); + if (param->conf.status != ESP_GATT_OK) { + esp_log_buffer_hex(COEX_TAG, param->conf.value, param->conf.len); + } + break; + case ESP_GATTS_OPEN_EVT: + default: + break; + } +} + +static void gatts_profile_b_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) { + switch (event) { + case ESP_GATTS_REG_EVT: + ESP_LOGI(COEX_TAG, "REGISTER_APP_EVT, status %d, app_id %d\n", param->reg.status, param->reg.app_id); + gatts_profile_tab[GATTS_PROFILE_B_APP_ID].service_id.is_primary = true; + gatts_profile_tab[GATTS_PROFILE_B_APP_ID].service_id.id.inst_id = 0x00; + gatts_profile_tab[GATTS_PROFILE_B_APP_ID].service_id.id.uuid.len = ESP_UUID_LEN_16; + gatts_profile_tab[GATTS_PROFILE_B_APP_ID].service_id.id.uuid.uuid.uuid16 = GATTS_SERVICE_UUID_TEST_B; + + esp_ble_gatts_create_service(gatts_if, &gatts_profile_tab[GATTS_PROFILE_B_APP_ID].service_id, GATTS_NUM_HANDLE_TEST_B); + break; + case ESP_GATTS_READ_EVT: { + ESP_LOGI(COEX_TAG, "GATT_READ_EVT, conn_id %d, trans_id %d, handle %d\n", param->read.conn_id, param->read.trans_id, param->read.handle); + esp_gatt_rsp_t rsp; + memset(&rsp, 0, sizeof(esp_gatt_rsp_t)); + rsp.attr_value.handle = param->read.handle; + rsp.attr_value.len = 4; + rsp.attr_value.value[0] = 0xde; + rsp.attr_value.value[1] = 0xed; + rsp.attr_value.value[2] = 0xbe; + rsp.attr_value.value[3] = 0xef; + esp_ble_gatts_send_response(gatts_if, param->read.conn_id, param->read.trans_id, + ESP_GATT_OK, &rsp); + break; + } + case ESP_GATTS_WRITE_EVT: { + ESP_LOGI(COEX_TAG, "GATT_WRITE_EVT, conn_id %d, trans_id %d, handle %d\n", param->write.conn_id, param->write.trans_id, param->write.handle); + if (!param->write.is_prep) { + ESP_LOGI(COEX_TAG, "GATT_WRITE_EVT, value len %d, value :", param->write.len); + esp_log_buffer_hex(COEX_TAG, param->write.value, param->write.len); + if (gatts_profile_tab[GATTS_PROFILE_B_APP_ID].descr_handle == param->write.handle && param->write.len == 2) { + uint16_t descr_value= param->write.value[1]<<8 | param->write.value[0]; + if (descr_value == NOTIFY_ENABLE) { + if (b_property & ESP_GATT_CHAR_PROP_BIT_NOTIFY) { + ESP_LOGI(COEX_TAG, "notify enable\n"); + uint8_t notify_data[15]; + for (int i = 0; i < sizeof(notify_data); ++ i) { + notify_data[i] = i%0xff; + } + //the size of notify_data[] need less than MTU size + esp_ble_gatts_send_indicate(gatts_if, param->write.conn_id, gatts_profile_tab[GATTS_PROFILE_B_APP_ID].char_handle, + sizeof(notify_data), notify_data, false); + } + } else if (descr_value == INDICATE_ENABLE) { + if (b_property & ESP_GATT_CHAR_PROP_BIT_INDICATE) { + ESP_LOGI(COEX_TAG, "indicate enable\n"); + uint8_t indicate_data[15]; + for (int i = 0; i < sizeof(indicate_data); ++ i) { + indicate_data[i] = i%0xff; + } + //the size of indicate_data[] need less than MTU size + esp_ble_gatts_send_indicate(gatts_if, param->write.conn_id, gatts_profile_tab[GATTS_PROFILE_B_APP_ID].char_handle, + sizeof(indicate_data), indicate_data, true); + } + } else if (descr_value == NOTIFY_INDICATE_DISABLE) { + ESP_LOGI(COEX_TAG, "notify/indicate disable \n"); + } else { + ESP_LOGE(COEX_TAG, "unknown value\n"); + } + + } + } + example_write_event_env(gatts_if, &b_prepare_write_env, param); + break; + } + case ESP_GATTS_EXEC_WRITE_EVT: + ESP_LOGI(COEX_TAG,"ESP_GATTS_EXEC_WRITE_EVT\n"); + esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, ESP_GATT_OK, NULL); + example_exec_write_event_env(&b_prepare_write_env, param); + break; + case ESP_GATTS_MTU_EVT: + ESP_LOGI(COEX_TAG, "ESP_GATTS_MTU_EVT, MTU %d\n", param->mtu.mtu); + break; + case ESP_GATTS_UNREG_EVT: + break; + case ESP_GATTS_CREATE_EVT: + ESP_LOGI(COEX_TAG, "CREATE_SERVICE_EVT, status %d, service_handle %d\n", param->create.status, param->create.service_handle); + gatts_profile_tab[GATTS_PROFILE_B_APP_ID].service_handle = param->create.service_handle; + gatts_profile_tab[GATTS_PROFILE_B_APP_ID].char_uuid.len = ESP_UUID_LEN_16; + gatts_profile_tab[GATTS_PROFILE_B_APP_ID].char_uuid.uuid.uuid16 = GATTS_CHAR_UUID_TEST_B; + + esp_ble_gatts_start_service(gatts_profile_tab[GATTS_PROFILE_B_APP_ID].service_handle); + b_property = ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_WRITE | ESP_GATT_CHAR_PROP_BIT_NOTIFY; + esp_err_t add_char_ret =esp_ble_gatts_add_char( gatts_profile_tab[GATTS_PROFILE_B_APP_ID].service_handle, &gatts_profile_tab[GATTS_PROFILE_B_APP_ID].char_uuid, + ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, + b_property, + NULL, NULL); + if (add_char_ret) { + ESP_LOGE(COEX_TAG, "add char failed, error code =%x\n",add_char_ret); + } + break; + case ESP_GATTS_ADD_INCL_SRVC_EVT: + break; + case ESP_GATTS_ADD_CHAR_EVT: + ESP_LOGI(COEX_TAG, "ADD_CHAR_EVT, status %d, attr_handle %d, service_handle %d\n", + param->add_char.status, param->add_char.attr_handle, param->add_char.service_handle); + + gatts_profile_tab[GATTS_PROFILE_B_APP_ID].char_handle = param->add_char.attr_handle; + gatts_profile_tab[GATTS_PROFILE_B_APP_ID].descr_uuid.len = ESP_UUID_LEN_16; + gatts_profile_tab[GATTS_PROFILE_B_APP_ID].descr_uuid.uuid.uuid16 = ESP_GATT_UUID_CHAR_CLIENT_CONFIG; + esp_ble_gatts_add_char_descr(gatts_profile_tab[GATTS_PROFILE_B_APP_ID].service_handle, &gatts_profile_tab[GATTS_PROFILE_B_APP_ID].descr_uuid, + ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, + NULL, NULL); + break; + case ESP_GATTS_ADD_CHAR_DESCR_EVT: + gatts_profile_tab[GATTS_PROFILE_B_APP_ID].descr_handle = param->add_char_descr.attr_handle; + ESP_LOGI(COEX_TAG, "ADD_DESCR_EVT, status %d, attr_handle %d, service_handle %d\n", + param->add_char_descr.status, param->add_char_descr.attr_handle, param->add_char_descr.service_handle); + break; + case ESP_GATTS_DELETE_EVT: + break; + case ESP_GATTS_START_EVT: + ESP_LOGI(COEX_TAG, "SERVICE_START_EVT, status %d, service_handle %d\n", + param->start.status, param->start.service_handle); + break; + case ESP_GATTS_STOP_EVT: + break; + case ESP_GATTS_CONNECT_EVT: + ESP_LOGI(COEX_TAG, "CONNECT_EVT, conn_id %d, remote %02x:%02x:%02x:%02x:%02x:%02x\n", + param->connect.conn_id, + param->connect.remote_bda[0], param->connect.remote_bda[1], param->connect.remote_bda[2], + param->connect.remote_bda[3], param->connect.remote_bda[4], param->connect.remote_bda[5]); + gatts_profile_tab[GATTS_PROFILE_B_APP_ID].conn_id = param->connect.conn_id; + break; + case ESP_GATTS_CONF_EVT: + ESP_LOGI(COEX_TAG, "ESP_GATTS_CONF_EVT status %d attr_handle %d\n", param->conf.status, param->conf.handle); + if (param->conf.status != ESP_GATT_OK) { + esp_log_buffer_hex(COEX_TAG, param->conf.value, param->conf.len); + } + break; + case ESP_GATTS_DISCONNECT_EVT: + case ESP_GATTS_OPEN_EVT: + default: + break; + } +} + +static void esp_gattc_cb(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) +{ + /* If event is register event, store the gattc_if for each profile */ + if (event == ESP_GATTC_REG_EVT) { + if (param->reg.status == ESP_GATT_OK) { + gattc_profile_tab[param->reg.app_id].gattc_if = gattc_if; + } else { + ESP_LOGI(COEX_TAG, "reg app failed, app_id %04x, status %d\n", + param->reg.app_id, + param->reg.status); + return; + } + } + + /* If the gattc_if equal to profile A, call profile A cb handler, + * so here call each profile's callback */ + do { + int idx; + for (idx = 0; idx < GATTC_PROFILE_NUM; idx ++) { + if (gattc_if == ESP_GATT_IF_NONE || /* ESP_GATT_IF_NONE, not specify a certain gatt_if, need to call every profile cb function */ + gattc_if == gattc_profile_tab[idx].gattc_if) { + if (gattc_profile_tab[idx].gattc_cb) { + gattc_profile_tab[idx].gattc_cb(event, gattc_if, param); + } + } + } + } while (0); +} + +static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) +{ + /* If event is register event, store the gatts_if for each profile */ + if (event == ESP_GATTS_REG_EVT) { + if (param->reg.status == ESP_GATT_OK) { + gatts_profile_tab[param->reg.app_id].gatts_if = gatts_if; + } else { + ESP_LOGI(COEX_TAG, "Reg app failed, app_id %04x, status %d\n", + param->reg.app_id, + param->reg.status); + return; + } + } + + /* If the gatts_if equal to profile A, call profile A cb handler, + * so here call each profile's callback */ + do { + int idx; + for (idx = 0; idx < GATTS_PROFILE_NUM; idx ++) { + if (gatts_if == ESP_GATT_IF_NONE || /* ESP_GATT_IF_NONE, not specify a certain gatt_if, need to call every profile cb function */ + gatts_if == gatts_profile_tab[idx].gatts_if) { + if (gatts_profile_tab[idx].gatts_cb) { + gatts_profile_tab[idx].gatts_cb(event, gatts_if, param); + } + } + } + } while (0); +} + +void app_main() +{ + esp_err_t ret; + + // Initialize NVS. + ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK( ret ); + + ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT)); + + esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + ret = esp_bt_controller_init(&bt_cfg); + if (ret) { + ESP_LOGE(COEX_TAG, "%s initialize controller failed: %s\n", __func__, esp_err_to_name(ret)); + return; + } + + ret = esp_bt_controller_enable(ESP_BT_MODE_BLE); + if (ret) { + ESP_LOGE(COEX_TAG, "%s enable controller failed: %s\n", __func__, esp_err_to_name(ret)); + return; + } + + ret = esp_bluedroid_init(); + if (ret) { + ESP_LOGE(COEX_TAG, "%s init bluetooth failed: %s\n", __func__, esp_err_to_name(ret)); + return; + } + + ret = esp_bluedroid_enable(); + if (ret) { + ESP_LOGE(COEX_TAG, "%s enable bluetooth failed: %s\n", __func__, esp_err_to_name(ret)); + return; + } + + ret = esp_ble_gap_register_callback(gap_event_handler); + if (ret) { + ESP_LOGE(COEX_TAG, "gap register error, error code = %x", ret); + return; + } + + // gatts register + ret = esp_ble_gatts_register_callback(gatts_event_handler); + if (ret) { + ESP_LOGE(COEX_TAG, "gatts register error, error code = %x", ret); + return; + } + + ret = esp_ble_gatts_app_register(GATTS_PROFILE_A_APP_ID); + if (ret) { + ESP_LOGE(COEX_TAG, "gatts app register error, error code = %x", ret); + return; + } + + ret = esp_ble_gatts_app_register(GATTS_PROFILE_B_APP_ID); + if (ret) { + ESP_LOGE(COEX_TAG, "gatts app register error, error code = %x", ret); + return; + } + + // gattc regisrter + ret = esp_ble_gattc_register_callback(esp_gattc_cb); + if(ret) { + ESP_LOGE(COEX_TAG, "%s gattc register failed, error code = %x\n", __func__, ret); + return; + } + + ret = esp_ble_gattc_app_register(GATTC_PROFILE_C_APP_ID); + if (ret) { + ESP_LOGE(COEX_TAG, "%s gattc app register failed, error code = %x\n", __func__, ret); + } + + esp_err_t local_mtu_ret = esp_ble_gatt_set_local_mtu(500); + if (local_mtu_ret) { + ESP_LOGE(COEX_TAG, "set local MTU failed, error code = %x", local_mtu_ret); + } + + return; +} + diff --git a/examples/bluetooth/gattc_gatts_coex/sdkconfig.defaults b/examples/bluetooth/gattc_gatts_coex/sdkconfig.defaults new file mode 100644 index 0000000000..8dbe56f4f4 --- /dev/null +++ b/examples/bluetooth/gattc_gatts_coex/sdkconfig.defaults @@ -0,0 +1,6 @@ +# Override some defaults so BT stack is enabled +# by default in this example +CONFIG_BT_ENABLED=y +CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y +CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY= +CONFIG_BTDM_CTRL_MODE_BTDM= From 037c079e9a86a54d95b0daa934cd2e8200a7d79b Mon Sep 17 00:00:00 2001 From: Darian Leung Date: Thu, 29 Nov 2018 17:06:21 +0800 Subject: [PATCH 073/486] esp32: Refactor backtrace and add esp_backtrace_print() This commit refactors backtracing within the panic handler so that a common function esp_backtrace_get_next_frame() is used iteratively to traverse a callstack. A esp_backtrace_print() function has also be added that allows the printing of a backtrace at runtime. The esp_backtrace_print() function allows unity to print the backtrace of failed test cases and jump back to the main test menu without the need reset the chip. esp_backtrace_print() can also be used as a debugging function by users. - esp_stack_ptr_is_sane() moved to soc_memory_layout.h - removed uncessary includes of "esp_debug_helpers.h" --- components/esp32/cpu_start.c | 1 - components/esp32/gdbstub.c | 2 +- components/esp32/panic.c | 42 +++++----- components/espcoredump/src/core_dump_port.c | 2 +- components/freertos/tasks.c | 1 - components/heap/test/test_malloc.c | 1 - components/soc/esp32/include/soc/cpu.h | 21 +++++ .../soc/include/soc/soc_memory_layout.h | 7 ++ components/unity/CMakeLists.txt | 4 + components/unity/Kconfig | 10 ++- components/unity/component.mk | 4 + components/unity/include/priv/setjmp.h | 14 ++++ components/xtensa/CMakeLists.txt | 2 +- components/xtensa/debug_helpers.c | 72 ++++++++++++++++ components/xtensa/debug_helpers_asm.S | 57 +++++++++++++ components/xtensa/include/esp_debug_helpers.h | 84 +++++++++++++++++-- tools/unit-test-app/sdkconfig.defaults | 1 + 17 files changed, 293 insertions(+), 32 deletions(-) create mode 100644 components/unity/include/priv/setjmp.h create mode 100644 components/xtensa/debug_helpers.c create mode 100644 components/xtensa/debug_helpers_asm.S diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index 3a782f2985..7e5a1c93e2 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -59,7 +59,6 @@ #include "esp_phy_init.h" #include "esp32/cache_err_int.h" #include "esp_coexist_internal.h" -#include "esp_debug_helpers.h" #include "esp_core_dump.h" #include "esp_app_trace.h" #include "esp_private/dbg_stubs.h" diff --git a/components/esp32/gdbstub.c b/components/esp32/gdbstub.c index 08623dce4c..c690e42b59 100644 --- a/components/esp32/gdbstub.c +++ b/components/esp32/gdbstub.c @@ -22,8 +22,8 @@ #include "esp32/rom/ets_sys.h" #include "soc/uart_periph.h" #include "soc/gpio_periph.h" +#include "soc/soc_memory_layout.h" #include "esp_private/gdbstub.h" -#include "esp_debug_helpers.h" #include "driver/gpio.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" diff --git a/components/esp32/panic.c b/components/esp32/panic.c index b66ba9437a..e7ed6540e2 100644 --- a/components/esp32/panic.c +++ b/components/esp32/panic.c @@ -30,6 +30,7 @@ #include "soc/cpu.h" #include "soc/rtc.h" #include "soc/rtc_wdt.h" +#include "soc/soc_memory_layout.h" #include "esp_private/gdbstub.h" #include "esp_debug_helpers.h" @@ -446,33 +447,36 @@ static void esp_panic_dig_reset() static void putEntry(uint32_t pc, uint32_t sp) { - if (pc & 0x80000000) { - pc = (pc & 0x3fffffff) | 0x40000000; - } panicPutStr(" 0x"); panicPutHex(pc); panicPutStr(":0x"); panicPutHex(sp); } -static void doBacktrace(XtExcFrame *frame) +static void doBacktrace(XtExcFrame *exc_frame, int depth) { - uint32_t i = 0, pc = frame->pc, sp = frame->a1; + //Initialize stk_frame with first frame of stack + esp_backtrace_frame_t stk_frame = {.pc = exc_frame->pc, .sp = exc_frame->a1, .next_pc = exc_frame->a0}; panicPutStr("\r\nBacktrace:"); - /* Do not check sanity on first entry, PC could be smashed. */ - putEntry(pc, sp); - pc = frame->a0; - while (i++ < 100) { - uint32_t psp = sp; - if (!esp_stack_ptr_is_sane(sp) || i++ > 100) { - break; - } - sp = *((uint32_t *) (sp - 0x10 + 4)); - putEntry(pc - 3, sp); // stack frame addresses are return addresses, so subtract 3 to get the CALL address - pc = *((uint32_t *) (psp - 0x10)); - if (pc < 0x40000000) { - break; + putEntry(esp_cpu_process_stack_pc(stk_frame.pc), stk_frame.sp); + + //Check if first frame is valid + bool corrupted = (esp_stack_ptr_is_sane(stk_frame.sp) && + esp_ptr_executable((void*)esp_cpu_process_stack_pc(stk_frame.pc))) ? + false : true; + uint32_t i = ((depth <= 0) ? INT32_MAX : depth) - 1; //Account for stack frame that's already printed + while (i-- > 0 && stk_frame.next_pc != 0 && !corrupted) { + if (!esp_backtrace_get_next_frame(&stk_frame)) { //Get next stack frame + corrupted = true; } + putEntry(esp_cpu_process_stack_pc(stk_frame.pc), stk_frame.sp); + } + + //Print backtrace termination marker + if (corrupted) { + panicPutStr(" |<-CORRUPTED"); + } else if (stk_frame.next_pc != 0) { //Backtrace continues + panicPutStr(" |<-CONTINUES"); } panicPutStr("\r\n"); } @@ -549,7 +553,7 @@ static void commonErrorHandler_dump(XtExcFrame *frame, int core_id) panicPutStr("\r\n"); /* With windowed ABI backtracing is easy, let's do it. */ - doBacktrace(frame); + doBacktrace(frame, 100); panicPutStr("\r\n"); } diff --git a/components/espcoredump/src/core_dump_port.c b/components/espcoredump/src/core_dump_port.c index 3e3414a4cc..2ac1b6482b 100644 --- a/components/espcoredump/src/core_dump_port.c +++ b/components/espcoredump/src/core_dump_port.c @@ -13,7 +13,7 @@ // limitations under the License. #include #include -#include "esp_debug_helpers.h" +#include "soc/soc_memory_layout.h" #include "esp_core_dump_priv.h" const static DRAM_ATTR char TAG[] __attribute__((unused)) = "esp_core_dump_port"; diff --git a/components/freertos/tasks.c b/components/freertos/tasks.c index 91c7616645..b19ba84a62 100644 --- a/components/freertos/tasks.c +++ b/components/freertos/tasks.c @@ -78,7 +78,6 @@ task.h is included from an application file. */ #include "esp32/rom/ets_sys.h" #include "esp_newlib.h" -#include "esp_debug_helpers.h" /* FreeRTOS includes. */ #include "FreeRTOS.h" diff --git a/components/heap/test/test_malloc.c b/components/heap/test/test_malloc.c index 1ed67ce50c..e2916e2bc3 100644 --- a/components/heap/test/test_malloc.c +++ b/components/heap/test/test_malloc.c @@ -14,7 +14,6 @@ #include "unity.h" #include "esp_heap_caps.h" -#include "esp_debug_helpers.h" #include "sdkconfig.h" diff --git a/components/soc/esp32/include/soc/cpu.h b/components/soc/esp32/include/soc/cpu.h index f28feb59fa..190786a433 100644 --- a/components/soc/esp32/include/soc/cpu.h +++ b/components/soc/esp32/include/soc/cpu.h @@ -110,4 +110,25 @@ void esp_cpu_reset(int cpu_id); */ bool esp_cpu_in_ocd_debug_mode(); +/** + * @brief Convert the PC register value to its true address + * + * The address of the current instruction is not stored as an exact uint32_t + * representation in PC register. This function will convert the value stored in + * the PC register to a uint32_t address. + * + * @param pc_raw The PC as stored in register format. + * + * @return Address in uint32_t format + */ +static inline uint32_t esp_cpu_process_stack_pc(uint32_t pc) +{ + if (pc & 0x80000000) { + //Top two bits of a0 (return address) specify window increment. Overwrite to map to address space. + pc = (pc & 0x3fffffff) | 0x40000000; + } + //Minus 3 to get PC of previous instruction (i.e. instruction executed before return address) + return pc - 3; +} + #endif diff --git a/components/soc/include/soc/soc_memory_layout.h b/components/soc/include/soc/soc_memory_layout.h index 6fb8c6519b..de7c449d51 100644 --- a/components/soc/include/soc/soc_memory_layout.h +++ b/components/soc/include/soc/soc_memory_layout.h @@ -203,3 +203,10 @@ inline static bool IRAM_ATTR esp_ptr_in_diram_dram(const void *p) { inline static bool IRAM_ATTR esp_ptr_in_diram_iram(const void *p) { return ((intptr_t)p >= SOC_DIRAM_IRAM_LOW && (intptr_t)p < SOC_DIRAM_IRAM_HIGH); } + + +inline static bool IRAM_ATTR esp_stack_ptr_is_sane(uint32_t sp) +{ + //Check if stack ptr is in between SOC_DRAM_LOW and SOC_DRAM_HIGH, and 16 byte aligned. + return !(sp < SOC_DRAM_LOW + 0x10 || sp > SOC_DRAM_HIGH - 0x10 || ((sp & 0xF) != 0)); +} diff --git a/components/unity/CMakeLists.txt b/components/unity/CMakeLists.txt index 3f8ecac230..53494ae0f0 100644 --- a/components/unity/CMakeLists.txt +++ b/components/unity/CMakeLists.txt @@ -4,6 +4,10 @@ set(COMPONENT_SRCS "unity/src/unity.c" set(COMPONENT_ADD_INCLUDEDIRS "include" "unity/src") +if(CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL) + list(APPEND COMPONENT_PRIV_INCLUDEDIRS "include/priv") +endif() + if(CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER) list(APPEND COMPONENT_SRCS "unity_runner.c") endif() diff --git a/components/unity/Kconfig b/components/unity/Kconfig index 4cf2a30731..e9efc6381e 100644 --- a/components/unity/Kconfig +++ b/components/unity/Kconfig @@ -42,4 +42,12 @@ menu "Unity unit testing library" the build. These provide an optional set of macros and functions to implement test groups. -endmenu # "Unity unit testing library" + config UNITY_ENABLE_BACKTRACE_ON_FAIL + bool "Print a backtrace when a unit test fails" + default n + help + If set, the unity framework will print the backtrace information before + jumping back to the test menu. The jumping is usually occurs in assert + functions such as TEST_ASSERT, TEST_FAIL etc. + +endmenu # "Unity unit testing library" \ No newline at end of file diff --git a/components/unity/component.mk b/components/unity/component.mk index 3e1501c79a..2d2a106d7c 100644 --- a/components/unity/component.mk +++ b/components/unity/component.mk @@ -9,6 +9,10 @@ endif COMPONENT_ADD_INCLUDEDIRS = include unity/src COMPONENT_SRCDIRS = unity/src . +ifdef CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL +COMPONENT_PRIV_INCLUDEDIRS += include/priv +endif + ifndef CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER COMPONENT_OBJEXCLUDE += unity_runner.o endif diff --git a/components/unity/include/priv/setjmp.h b/components/unity/include/priv/setjmp.h new file mode 100644 index 0000000000..c467cbd94a --- /dev/null +++ b/components/unity/include/priv/setjmp.h @@ -0,0 +1,14 @@ +#include_next +#include "esp_debug_helpers.h" + +/* + * This is the middle layer of setjmp to be used with the unity. + */ + +/** Insert backtrace before longjmp (TEST_ABORT). + * + * Currently we only do long jump before test is ignored or failed. + * If this is also called when test pass, we may need to add some check before + * backtrace is called. + */ +#define longjmp(buf, val) do {esp_backtrace_print(100); longjmp(buf, val);} while(0) diff --git a/components/xtensa/CMakeLists.txt b/components/xtensa/CMakeLists.txt index cb9c9ce49c..1bb997d673 100644 --- a/components/xtensa/CMakeLists.txt +++ b/components/xtensa/CMakeLists.txt @@ -1,4 +1,4 @@ -set(COMPONENT_SRCS "eri.c" "trax.c") +set(COMPONENT_SRCS "eri.c" "trax.c" "debug_helpers.c" "debug_helpers_asm.S") set(COMPONENT_ADD_INCLUDEDIRS "include" "${IDF_TARGET}/include") diff --git a/components/xtensa/debug_helpers.c b/components/xtensa/debug_helpers.c new file mode 100644 index 0000000000..564e6eb159 --- /dev/null +++ b/components/xtensa/debug_helpers.c @@ -0,0 +1,72 @@ +// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// +// 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 "esp_types.h" +#include "esp_attr.h" +#include "esp_err.h" +#include "esp_debug_helpers.h" +#include "esp32/rom/ets_sys.h" +#include "soc/soc_memory_layout.h" +#include "soc/cpu.h" + +bool IRAM_ATTR esp_backtrace_get_next_frame(esp_backtrace_frame_t *frame) +{ + //Use frame(i-1)'s BS area located below frame(i)'s sp to get frame(i-1)'s sp and frame(i-2)'s pc + void *base_save = (void *)frame->sp; //Base save area consists of 4 words under SP + frame->pc = frame->next_pc; + frame->next_pc = *((uint32_t *)(base_save - 16)); //If next_pc = 0, indicates frame(i-1) is the last frame on the stack + frame->sp = *((uint32_t *)(base_save - 12)); + + //Return true if both sp and pc of frame(i-1) are sane, false otherwise + return (esp_stack_ptr_is_sane(frame->sp) && esp_ptr_executable((void*)esp_cpu_process_stack_pc(frame->pc))); +} + +esp_err_t IRAM_ATTR esp_backtrace_print(int depth) +{ + //Check arguments + if (depth <= 0) { + return ESP_ERR_INVALID_ARG; + } + + //Initialize stk_frame with first frame of stack + esp_backtrace_frame_t stk_frame; + esp_backtrace_get_start(&(stk_frame.pc), &(stk_frame.sp), &(stk_frame.next_pc)); + //esp_cpu_get_backtrace_start(&stk_frame); + ets_printf("\r\n\r\nBacktrace:"); + ets_printf("0x%08X:0x%08X ", esp_cpu_process_stack_pc(stk_frame.pc), stk_frame.sp); + + //Check if first frame is valid + bool corrupted = (esp_stack_ptr_is_sane(stk_frame.sp) && + esp_ptr_executable((void*)esp_cpu_process_stack_pc(stk_frame.pc))) ? + false : true; + + uint32_t i = (depth <= 0) ? INT32_MAX : depth; + while (i-- > 0 && stk_frame.next_pc != 0 && !corrupted) { + if (!esp_backtrace_get_next_frame(&stk_frame)) { //Get previous stack frame + corrupted = true; + } + ets_printf("0x%08X:0x%08X ", esp_cpu_process_stack_pc(stk_frame.pc), stk_frame.sp); + } + + //Print backtrace termination marker + esp_err_t ret = ESP_OK; + if (corrupted) { + ets_printf(" |<-CORRUPTED"); + ret = ESP_FAIL; + } else if (stk_frame.next_pc != 0) { //Backtrace continues + ets_printf(" |<-CONTINUES"); + } + ets_printf("\r\n\r\n"); + return ret; +} diff --git a/components/xtensa/debug_helpers_asm.S b/components/xtensa/debug_helpers_asm.S new file mode 100644 index 0000000000..0df70df863 --- /dev/null +++ b/components/xtensa/debug_helpers_asm.S @@ -0,0 +1,57 @@ +// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// +// 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 +#include +#include + +/* + * esp_backtrace_get_start(uint32_t *pc, uint32_t *sp, uint32_t *next_pc) + * + * High Addr + * .................. + * | i-3 BS | + * | i-1 locals | Function B + * .................. i-1 SP + * | i-2 BS | + * | i locals | Function A (Start of backtrace) + * ------------------ i SP + * | i-1 BS | + * | i+1 locals | Backtracing function (e.g. esp_backtrace_print()) + * ------------------ i+1 SP + * | i BS | + * | i+2 locals | esp_backtrace_get_start() <- This function + * ------------------ i+2 SP + * | i+1 BS | + * | i+3 locals | xthal_window_spill() + * ------------------ i+3 SP + * .................. Low Addr + */ + .section .iram1, "ax" + .align 4 + .global esp_backtrace_get_start + .type esp_backtrace_get_start, @function +esp_backtrace_get_start: + entry a1, 32 + call8 xthal_window_spill //Spill registers onto stack (excluding this function) + //a2, a3, a4 should be out arguments for i SP, i PC, i-1 PC respectively. Use a5 and a6 as scratch + l32e a5, sp, -16 //Get i PC, which is ret addres of i+1 + s32i a5, a2, 0 //Store i PC to arg *pc + l32e a6, sp, -12 //Get i+1 SP. Used to access i BS + l32e a5, a6, -12 //Get i SP + s32i a5, a3, 0 //Store i SP to arg *sp + l32e a5, a6, -16 //Get i-1 PC, which is ret address of i + s32i a5, a4, 0 //Store i-1 PC to arg *next_pc + retw diff --git a/components/xtensa/include/esp_debug_helpers.h b/components/xtensa/include/esp_debug_helpers.h index 981fa34a53..40167780c5 100644 --- a/components/xtensa/include/esp_debug_helpers.h +++ b/components/xtensa/include/esp_debug_helpers.h @@ -1,3 +1,17 @@ +// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// +// 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 #ifdef __cplusplus @@ -12,6 +26,24 @@ extern "C" { #define ESP_WATCHPOINT_STORE 0x80000000 #define ESP_WATCHPOINT_ACCESS 0xC0000000 +/* + * @brief Structure used for backtracing + * + * This structure stores the backtrace information of a particular stack frame + * (i.e. the PC and SP). This structure is used iteratively with the + * esp_cpu_get_next_backtrace_frame() function to traverse each frame within a + * single stack. The next_pc represents the PC of the current frame's caller, thus + * a next_pc of 0 indicates that the current frame is the last frame on the stack. + * + * @note Call esp_backtrace_get_start() to obtain initialization values for + * this structure + */ +typedef struct { + uint32_t pc; /* PC of the current frame */ + uint32_t sp; /* SP of the current frame */ + uint32_t next_pc; /* PC of the current frame's caller */ +} esp_backtrace_frame_t; + /** * @brief If an OCD is connected over JTAG. set breakpoint 0 to the given function * address. Do nothing otherwise. @@ -37,7 +69,6 @@ void esp_set_breakpoint_if_jtag(void *fn); */ esp_err_t esp_set_watchpoint(int no, void *adr, int size, int flags); - /** * @brief Clear a watchpoint * @@ -47,12 +78,53 @@ esp_err_t esp_set_watchpoint(int no, void *adr, int size, int flags); void esp_clear_watchpoint(int no); /** - * @brief Checks stack pointer + * Get the first frame of the current stack's backtrace + * + * Given the following function call flow (B -> A -> X -> esp_backtrace_get_start), + * this function will do the following. + * - Flush CPU registers and window frames onto the current stack + * - Return PC and SP of function A (i.e. start of the stack's backtrace) + * - Return PC of function B (i.e. next_pc) + * + * @note This function is implemented in assembly + * + * @param[out] pc PC of the first frame in the backtrace + * @param[out] sp SP of the first frame in the backtrace + * @param[out] next_pc PC of the first frame's caller */ -static inline bool esp_stack_ptr_is_sane(uint32_t sp) -{ - return !(sp < 0x3ffae010UL || sp > 0x3ffffff0UL || ((sp & 0xf) != 0)); -} +extern void esp_backtrace_get_start(uint32_t *pc, uint32_t *sp, uint32_t *next_pc); + +/** + * Get the next frame on a stack for backtracing + * + * Given a stack frame(i), this function will obtain the next stack frame(i-1) + * on the same call stack (i.e. the caller of frame(i)). This function is meant to be + * called iteratively when doing a backtrace. + * + * Entry Conditions: Frame structure containing valid SP and next_pc + * Exit Conditions: + * - Frame structure updated with SP and PC of frame(i-1). next_pc now points to frame(i-2). + * - If a next_pc of 0 is returned, it indicates that frame(i-1) is last frame on the stack + * + * @param[inout] frame Pointer to frame structure + * + * @return + * - True if the SP and PC of the next frame(i-1) are sane + * - False otherwise + */ +bool esp_backtrace_get_next_frame(esp_backtrace_frame_t *frame); + +/** + * @brief Print the backtrace of the current stack + * + * @param depth The maximum number of stack frames to print (should be > 0) + * + * @return + * - ESP_OK Backtrace successfully printed to completion or to depth limit + * - ESP_FAIL Backtrace is corrupted + */ +esp_err_t esp_backtrace_print(int depth); + #endif #ifdef __cplusplus diff --git a/tools/unit-test-app/sdkconfig.defaults b/tools/unit-test-app/sdkconfig.defaults index ca30ad526d..947c232873 100644 --- a/tools/unit-test-app/sdkconfig.defaults +++ b/tools/unit-test-app/sdkconfig.defaults @@ -31,3 +31,4 @@ CONFIG_SPI_MASTER_IN_IRAM=y CONFIG_EFUSE_VIRTUAL=y CONFIG_SPIRAM_BANKSWITCH_ENABLE=n CONFIG_FATFS_ALLOC_EXTRAM_FIRST=y +CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL=y From 71eece07a0f9fd22a678c0529955daef93bf8684 Mon Sep 17 00:00:00 2001 From: zhiweijian Date: Wed, 3 Apr 2019 20:11:17 +0800 Subject: [PATCH 074/486] Component/bt: add ble ancs demo --- examples/bluetooth/ble_ancs/CMakeLists.txt | 6 + examples/bluetooth/ble_ancs/Makefile | 10 + examples/bluetooth/ble_ancs/README.md | 18 + .../bluetooth/ble_ancs/main/CMakeLists.txt | 5 + examples/bluetooth/ble_ancs/main/ble_ancs.c | 228 ++++++ examples/bluetooth/ble_ancs/main/ble_ancs.h | 124 ++++ .../bluetooth/ble_ancs/main/ble_ancs_demo.c | 688 ++++++++++++++++++ examples/bluetooth/ble_ancs/main/component.mk | 9 + .../bluetooth/ble_ancs/sdkconfig.defaults | 6 + 9 files changed, 1094 insertions(+) create mode 100644 examples/bluetooth/ble_ancs/CMakeLists.txt create mode 100644 examples/bluetooth/ble_ancs/Makefile create mode 100644 examples/bluetooth/ble_ancs/README.md create mode 100644 examples/bluetooth/ble_ancs/main/CMakeLists.txt create mode 100644 examples/bluetooth/ble_ancs/main/ble_ancs.c create mode 100644 examples/bluetooth/ble_ancs/main/ble_ancs.h create mode 100644 examples/bluetooth/ble_ancs/main/ble_ancs_demo.c create mode 100644 examples/bluetooth/ble_ancs/main/component.mk create mode 100644 examples/bluetooth/ble_ancs/sdkconfig.defaults diff --git a/examples/bluetooth/ble_ancs/CMakeLists.txt b/examples/bluetooth/ble_ancs/CMakeLists.txt new file mode 100644 index 0000000000..84faba94ed --- /dev/null +++ b/examples/bluetooth/ble_ancs/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(ble_ancs) diff --git a/examples/bluetooth/ble_ancs/Makefile b/examples/bluetooth/ble_ancs/Makefile new file mode 100644 index 0000000000..a5208ef071 --- /dev/null +++ b/examples/bluetooth/ble_ancs/Makefile @@ -0,0 +1,10 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := ble_ancs + +COMPONENT_ADD_INCLUDEDIRS := components/include + +include $(IDF_PATH)/make/project.mk diff --git a/examples/bluetooth/ble_ancs/README.md b/examples/bluetooth/ble_ancs/README.md new file mode 100644 index 0000000000..06a7f4625e --- /dev/null +++ b/examples/bluetooth/ble_ancs/README.md @@ -0,0 +1,18 @@ +ESP-IDF BLE ANCS Example +========================== + +The purpose of the Apple Notification Center Service (ANCS) is to give Bluetooth accessories (that connect to iOS devices through a Bluetooth low-energy link) a simple and convenient way to access many kinds of notifications that are generated on iOS devices. + +The Apple Notification Center Service is a primary service whose service UUID is 7905F431-B5CE-4E99-A40F-4B1E122D00D0. + +Only one instance of the ANCS may be present on an NP. Due to the nature of iOS, the ANCS is not guaranteed to always be present. As a result, the NC should look for and subscribe to the Service Changed characteristic of the GATT service in order to monitor for the potential publishing and unpublishing of the ANCS at any time. + +In its basic form, the ANCS exposes three characteristics: +Notification Source: UUID 9FBF120D-6301-42D9-8C58-25E699A21DBD (notifiable) +Control Point: UUID 69D1D8F3-45E1-49A8-9821-9BBDFDAAD9D9 (writeable with response) +Data Source: UUID 22EAC6E9-24D6-4BB5-BE44-B36ACE7C7BFB (notifiable) +All these characteristics require authorization for access. + + + + diff --git a/examples/bluetooth/ble_ancs/main/CMakeLists.txt b/examples/bluetooth/ble_ancs/main/CMakeLists.txt new file mode 100644 index 0000000000..7e64fd8041 --- /dev/null +++ b/examples/bluetooth/ble_ancs/main/CMakeLists.txt @@ -0,0 +1,5 @@ +set(COMPONENT_SRCS "ble_ancs_demo.c" + "ble_ancs.c") +set(COMPONENT_ADD_INCLUDEDIRS ".") + +register_component() diff --git a/examples/bluetooth/ble_ancs/main/ble_ancs.c b/examples/bluetooth/ble_ancs/main/ble_ancs.c new file mode 100644 index 0000000000..2035b67516 --- /dev/null +++ b/examples/bluetooth/ble_ancs/main/ble_ancs.c @@ -0,0 +1,228 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 +#include "esp_log.h" +#include "ble_ancs.h" + +#define BLE_ANCS_TAG "BLE_ANCS" + +/* +| EventID(1 Byte) | EventFlags(1 Byte) | CategoryID(1 Byte) | CategoryCount(1 Byte) | NotificationUID(4 Bytes) | + +A GATT notification delivered through the Notification Source characteristic contains the following information: +* EventID: This field informs the accessory whether the given iOS notification was added, modified, or removed. The enumerated values for this field are defined + in EventID Values. +* EventFlags: A bitmask whose set bits inform an NC of specificities with the iOS notification. For example, if an iOS notification is considered “important”, + the NC may want to display a more aggressive user interface (UI) to make sure the user is properly alerted. The enumerated bits for this field + are defined in EventFlags. +* CategoryID: A numerical value providing a category in which the iOS notification can be classified. The NP will make a best effort to provide an accurate category + for each iOS notification. The enumerated values for this field are defined in CategoryID Values. +* CategoryCount: The current number of active iOS notifications in the given category. For example, if two unread emails are sitting in a user’s email inbox, and a new + email is pushed to the user’s iOS device, the value of CategoryCount is 3. +* NotificationUID: A 32-bit numerical value that is the unique identifier (UID) for the iOS notification. This value can be used as a handle in commands sent to the + Control Point characteristic to interact with the iOS notification. +*/ + +char *EventID_to_String(uint8_t EventID) +{ + char *str = NULL; + switch (EventID) + { + case EventIDNotificationAdded: + str = "New message"; + break; + case EventIDNotificationModified: + str = "Modified message"; + break; + case EventIDNotificationRemoved: + str = "Removed message"; + break; + default: + str = "unknown EventID"; + break; + } + return str; +} + +char *CategoryID_to_String(uint8_t CategoryID) +{ + char *Cidstr = NULL; + switch(CategoryID) { + case CategoryIDOther: + Cidstr = "Other"; + break; + case CategoryIDIncomingCall: + Cidstr = "IncomingCall"; + break; + case CategoryIDMissedCall: + Cidstr = "MissedCall"; + break; + case CategoryIDVoicemail: + Cidstr = "Voicemail"; + break; + case CategoryIDSocial: + Cidstr = "Social"; + break; + case CategoryIDSchedule: + Cidstr = "Schedule"; + break; + case CategoryIDEmail: + Cidstr = "Email"; + break; + case CategoryIDNews: + Cidstr = "News"; + break; + case CategoryIDHealthAndFitness: + Cidstr = "HealthAndFitness"; + break; + case CategoryIDBusinessAndFinance: + Cidstr = "BusinessAndFinance"; + break; + case CategoryIDLocation: + Cidstr = "Location"; + break; + case CategoryIDEntertainment: + Cidstr = "Entertainment"; + break; + default: + Cidstr = "Unknown CategoryID"; + break; + } + return Cidstr; +} + +/* +| EventID(1 Byte) | EventFlags(1 Byte) | CategoryID(1 Byte) | CategoryCount(1 Byte) | NotificationUID(4 Bytes) | +*/ + +void esp_receive_apple_notification_source(uint8_t *message, uint16_t message_len) +{ + if (!message || message_len < 5) { + return; + } + + uint8_t EventID = message[0]; + char *EventIDS = EventID_to_String(EventID); + uint8_t EventFlags = message[1]; + uint8_t CategoryID = message[2]; + char *Cidstr = CategoryID_to_String(CategoryID); + uint8_t CategoryCount = message[3]; + uint32_t NotificationUID = (message[4]) | (message[5]<< 8) | (message[6]<< 16) | (message[7] << 24); + ESP_LOGI(BLE_ANCS_TAG, "EventID:%s EventFlags:0x%x CategoryID:%s CategoryCount:%d NotificationUID:%d", EventIDS, EventFlags, Cidstr, CategoryCount, NotificationUID); +} + +void esp_receive_apple_data_source(uint8_t *message, uint16_t message_len) +{ + //esp_log_buffer_hex("data source", message, message_len); + if (!message || message_len == 0) { + return; + } + uint8_t Command_id = message[0]; + switch (Command_id) + { + case CommandIDGetNotificationAttributes: { + uint32_t NotificationUID = (message[1]) | (message[2]<< 8) | (message[3]<< 16) | (message[4] << 24); + uint32_t remian_attr_len = message_len - 5; + uint8_t *attrs = &message[5]; + ESP_LOGI(BLE_ANCS_TAG, "recevice Notification Attributes response Command_id %d NotificationUID %d", Command_id, NotificationUID); + while(remian_attr_len > 0) { + uint8_t AttributeID = attrs[0]; + uint16_t len = attrs[1] | (attrs[2] << 8); + if(len > (remian_attr_len -3)) { + ESP_LOGE(BLE_ANCS_TAG, "data error"); + break; + } + switch (AttributeID) + { + case NotificationAttributeIDAppIdentifier: + esp_log_buffer_char("Identifier", &attrs[3], len); + break; + case NotificationAttributeIDTitle: + esp_log_buffer_char("Title", &attrs[3], len); + break; + case NotificationAttributeIDSubtitle: + esp_log_buffer_char("Subtitle", &attrs[3], len); + break; + case NotificationAttributeIDMessage: + esp_log_buffer_char("Message", &attrs[3], len); + break; + case NotificationAttributeIDMessageSize: + esp_log_buffer_char("MessageSize", &attrs[3], len); + break; + case NotificationAttributeIDDate: + //yyyyMMdd'T'HHmmSS + esp_log_buffer_char("Date", &attrs[3], len); + break; + case NotificationAttributeIDPositiveActionLabel: + esp_log_buffer_hex("PActionLabel", &attrs[3], len); + break; + case NotificationAttributeIDNegativeActionLabel: + esp_log_buffer_hex("NActionLabel", &attrs[3], len); + break; + default: + esp_log_buffer_hex("unknownAttributeID", &attrs[3], len); + break; + } + + attrs += (1 + 2 + len); + remian_attr_len -= (1 + 2 + len); + } + + break; + } + case CommandIDGetAppAttributes: + ESP_LOGI(BLE_ANCS_TAG, "recevice APP Attributes response"); + break; + case CommandIDPerformNotificationAction: + ESP_LOGI(BLE_ANCS_TAG, "recevice Perform Notification Action"); + break; + default: + ESP_LOGI(BLE_ANCS_TAG, "unknown Command ID"); + break; + } +} + +char *Errcode_to_String(uint16_t status) +{ + char *Errstr = NULL; + switch (status) { + case Unknown_command: + Errstr = "Unknown_command"; + break; + case Invalid_command: + Errstr = "Invalid_command"; + break; + case Invalid_parameter: + Errstr = "Invalid_parameter"; + break; + case Action_failed: + Errstr = "Action_failed"; + break; + default: + Errstr = "unknown_failed"; + break; + } + return Errstr; + +} + + + + + + + + diff --git a/examples/bluetooth/ble_ancs/main/ble_ancs.h b/examples/bluetooth/ble_ancs/main/ble_ancs.h new file mode 100644 index 0000000000..64608ea029 --- /dev/null +++ b/examples/bluetooth/ble_ancs/main/ble_ancs.h @@ -0,0 +1,124 @@ +/* + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + + +#include +#include +#include + +//EventID values +typedef enum { + EventIDNotificationAdded = 0, + EventIDNotificationModified = 1, + EventIDNotificationRemoved = 2, + //Reserved EventID values = 3–255 +} esp_EventID; + +//EventFlags +typedef enum { + EventFlagSilent = (1 << 0), + EventFlagImportant = (1 << 1), + EventFlagPreExisting = (1 << 2), + EventFlagPositiveAction = (1 << 3), + EventFlagNegativeAction = (1 << 4), + //Reserved EventFlags = (1 << 5)–(1 << 7 +}esp_EventFlags; + +// CategoryID values +typedef enum { + CategoryIDOther = 0, + CategoryIDIncomingCall = 1, + CategoryIDMissedCall = 2, + CategoryIDVoicemail = 3, + CategoryIDSocial = 4, + CategoryIDSchedule = 5, + CategoryIDEmail = 6, + CategoryIDNews = 7, + CategoryIDHealthAndFitness = 8, + CategoryIDBusinessAndFinance = 9, + CategoryIDLocation = 10, + CategoryIDEntertainment = 11, + //Reserved CategoryID values = 12–255 +} esp_CategoryID; + +//CommandID values +typedef enum { + CommandIDGetNotificationAttributes = 0, + CommandIDGetAppAttributes = 1, + CommandIDPerformNotificationAction = 2, + //Reserved CommandID values = 3–255 +} esp_CommandID; + +//NotificationAttributeID +typedef enum { + NotificationAttributeIDAppIdentifier = 0, + NotificationAttributeIDTitle = 1, //(Needs to be followed by a 2-bytes max length parameter) + NotificationAttributeIDSubtitle = 2, //(Needs to be followed by a 2-bytes max length parameter) + NotificationAttributeIDMessage = 3, //(Needs to be followed by a 2-bytes max length parameter) + NotificationAttributeIDMessageSize = 4, + NotificationAttributeIDDate = 5, + NotificationAttributeIDPositiveActionLabel = 6, + NotificationAttributeIDNegativeActionLabel = 7, + //Reserved NotificationAttributeID values = 8–255 +} esp_NotificationAttributeID; + +/* +Note: The format of the NotificationAttributeIDMessageSize constant is a string that represents the integral value +of the message size. The format of the NotificationAttributeIDDate constant is a string that uses the Unicode Technical +Standard (UTS) #35 date format pattern yyyyMMdd'T'HHmmSS. The format of all the other constants in Table 3-5 are UTF-8 +strings. +*/ + +//ActionID values +typedef enum { + ActionIDPositive = 0, + ActionIDNegative = 1, + //Reserved ActionID values = 2–255 +} esp_ActionID; + +//AppAttributeID Values +typedef enum { + AppAttributeIDDisplayName = 0, + //Reserved AppAttributeID values = 1–255 +} esp_AppAttributeID; + +typedef struct { + uint8_t noti_attribute_id; + uint16_t attribute_len; +} esp_noti_attr_list_t; + +typedef enum { + Unknown_command = (0xA0), //The commandID was not recognized by the NP. + Invalid_command = (0xA1), //The command was improperly formatted. + Invalid_parameter = (0xA2), // One of the parameters (for example, the NotificationUID) does not refer to an existing object on the NP. + Action_failed = (0xA3), //The action was not performed +} esp_error_code; + +typedef enum { + attr_appidentifier_index = 0, //The commandID was not recognized by the NP. + attr_title_index, + attr_subtitle_index, + attr_message_index, + attr_messagesize_index, + attr_date_index, + attr_positiveactionlabel_index, + attr_negativeactionlabel_index, +} esp_attr_index; + +#define ESP_NOTIFICATIONUID_LEN 4 + + +char *EventID_to_String(uint8_t EventID); +char *CategoryID_to_String(uint8_t CategoryID); +void esp_receive_apple_notification_source(uint8_t *message, uint16_t message_len); +void esp_receive_apple_data_source(uint8_t *message, uint16_t message_len); +char *Errcode_to_String(uint16_t status); + + + + diff --git a/examples/bluetooth/ble_ancs/main/ble_ancs_demo.c b/examples/bluetooth/ble_ancs/main/ble_ancs_demo.c new file mode 100644 index 0000000000..28ca4629fc --- /dev/null +++ b/examples/bluetooth/ble_ancs/main/ble_ancs_demo.c @@ -0,0 +1,688 @@ +/* + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "esp_system.h" +#include "esp_log.h" +#include "nvs_flash.h" +#include "esp_bt.h" + +#include "esp_gap_ble_api.h" +#include "esp_gatts_api.h" +#include "esp_bt_defs.h" +#include "esp_bt_main.h" +#include "esp_gattc_api.h" +#include "esp_gatt_defs.h" +#include "esp_gatt_common_api.h" +#include "ble_ancs.h" + +#define BLE_ANCS_TAG "BLE_ANCS" +#define EXCAMPLE_DEVICE_NAME "ESP_BLE_ANCS" +#define PROFILE_A_APP_ID 0 +#define PROFILE_NUM 1 +#define ADV_CONFIG_FLAG (1 << 0) +#define SCAN_RSP_CONFIG_FLAG (1 << 1) +#define INVALID_HANDLE 0 +static uint8_t adv_config_done = 0; +static bool get_service = false; +static esp_gattc_char_elem_t *char_elem_result = NULL; +static esp_gattc_descr_elem_t *descr_elem_result = NULL; +static void periodic_timer_callback(void* arg); +esp_timer_handle_t periodic_timer; + +const esp_timer_create_args_t periodic_timer_args = { + .callback = &periodic_timer_callback, + /* name is optional, but may help identify the timer when debugging */ + .name = "periodic" +}; + +struct data_source_buffer { + uint8_t buffer[1024]; + uint16_t len; +}; + +static struct data_source_buffer data_buffer = {0}; + +//In its basic form, the ANCS exposes three characteristics: +// service UUID: 7905F431-B5CE-4E99-A40F-4B1E122D00D0 +uint8_t Apple_NC_UUID[16] = {0xD0, 0x00, 0x2D, 0x12, 0x1E, 0x4B, 0x0F, 0xA4, 0x99, 0x4E, 0xCE, 0xB5, 0x31, 0xF4, 0x05, 0x79}; +// Notification Source UUID: 9FBF120D-6301-42D9-8C58-25E699A21DBD(notifiable) +uint8_t notification_source[16] = {0xbd, 0x1d, 0xa2, 0x99, 0xe6, 0x25, 0x58, 0x8c, 0xd9, 0x42, 0x01, 0x63, 0x0d, 0x12, 0xbf, 0x9f}; +// Control Point UUID:69D1D8F3-45E1-49A8-9821-9BBDFDAAD9D9(writeable with response) +uint8_t control_point[16] = {0xd9, 0xd9, 0xaa, 0xfd, 0xbd, 0x9b, 0x21, 0x98, 0xa8, 0x49, 0xe1, 0x45, 0xf3, 0xd8, 0xd1, 0x69}; +// Data Source UUID:22EAC6E9-24D6-4BB5-BE44-B36ACE7C7BFB(notifiable) +uint8_t data_source[16] = {0xfb, 0x7b, 0x7c, 0xce, 0x6a, 0xb3, 0x44, 0xbe, 0xb5, 0x4b, 0xd6, 0x24, 0xe9, 0xc6, 0xea, 0x22}; + +/* +Note: There may be more characteristics present in the ANCS than the three listed above. That said, an NC may ignore any characteristic it does not recognize. +*/ + +static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param); + +static esp_bt_uuid_t apple_nc_uuid = { + .len = ESP_UUID_LEN_128, +}; + +static uint8_t hidd_service_uuid128[] = { + /* LSB <--------------------------------------------------------------------------------> MSB */ + //first uuid, 16bit, [12],[13] is the value + 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x12, 0x18, 0x00, 0x00, +}; + +// config adv data +static esp_ble_adv_data_t adv_config = { + .set_scan_rsp = false, + .include_txpower = false, + .min_interval = 0x0006, //slave connection min interval, Time = min_interval * 1.25 msec + .max_interval = 0x0010, //slave connection max interval, Time = max_interval * 1.25 msec + .appearance = ESP_BLE_APPEARANCE_GENERIC_HID, + .service_uuid_len = sizeof(hidd_service_uuid128), + .p_service_uuid = hidd_service_uuid128, + .flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT), +}; +// config scan response data +static esp_ble_adv_data_t scan_rsp_config = { + .set_scan_rsp = true, + .include_name = true, + .manufacturer_len = 0, + .p_manufacturer_data = NULL, +}; + +static esp_ble_adv_params_t adv_params = { + .adv_int_min = 0x100, + .adv_int_max = 0x100, + .adv_type = ADV_TYPE_IND, + .own_addr_type = BLE_ADDR_TYPE_RANDOM, + .channel_map = ADV_CHNL_ALL, + .adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY, +}; + +struct gattc_profile_inst { + esp_gattc_cb_t gattc_cb; + uint16_t gattc_if; + uint16_t app_id; + uint16_t conn_id; + uint16_t service_start_handle; + uint16_t service_end_handle; + uint16_t notification_source_handle; + uint16_t data_source_handle; + uint16_t contol_point_handle; + esp_bd_addr_t remote_bda; + uint16_t MTU_size; +}; + +static struct gattc_profile_inst gl_profile_tab[PROFILE_NUM] = { + [PROFILE_A_APP_ID] = { + .gattc_cb = gattc_profile_event_handler, + .gattc_if = ESP_GATT_IF_NONE, /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */ + }, +}; + +esp_noti_attr_list_t p_attr[8] = { + [attr_appidentifier_index] = { + .noti_attribute_id = NotificationAttributeIDAppIdentifier, + .attribute_len = 0, + }, + [attr_title_index] = { + .noti_attribute_id = NotificationAttributeIDTitle, + .attribute_len = 0xFFFF, + }, + [attr_subtitle_index] = { + .noti_attribute_id = NotificationAttributeIDSubtitle, + .attribute_len = 0xFFFF, + }, + [attr_message_index] = { + .noti_attribute_id = NotificationAttributeIDMessage, + .attribute_len = 0xFFFF, + }, + [attr_messagesize_index] = { + .noti_attribute_id = NotificationAttributeIDMessageSize, + .attribute_len = 0, + }, + [attr_date_index] = { + .noti_attribute_id = NotificationAttributeIDDate, + .attribute_len = 0, + }, + [attr_positiveactionlabel_index] = { + .noti_attribute_id = NotificationAttributeIDPositiveActionLabel, + .attribute_len = 0, + }, + [attr_negativeactionlabel_index] = { + .noti_attribute_id = NotificationAttributeIDNegativeActionLabel, + .attribute_len = 0, + }, + +}; + +/* + | CommandID(1 Byte) | NotificationUID(4 Bytes) | AttributeIDs | +*/ + +void esp_get_notification_attributes(uint8_t *notificationUID, uint8_t num_attr, esp_noti_attr_list_t *p_attr) +{ + uint8_t cmd[600] = {0}; + uint32_t index = 0; + cmd[0] = CommandIDGetNotificationAttributes; + index ++; + memcpy(&cmd[index], notificationUID, ESP_NOTIFICATIONUID_LEN); + index += ESP_NOTIFICATIONUID_LEN; + while(num_attr > 0) { + cmd[index ++] = p_attr->noti_attribute_id; + if (p_attr->attribute_len > 0) { + cmd[index ++] = p_attr->attribute_len; + cmd[index ++] = (p_attr->attribute_len << 8); + } + p_attr ++; + num_attr --; + } + + esp_ble_gattc_write_char( gl_profile_tab[PROFILE_A_APP_ID].gattc_if, + gl_profile_tab[PROFILE_A_APP_ID].conn_id, + gl_profile_tab[PROFILE_A_APP_ID].contol_point_handle, + index, + cmd, + ESP_GATT_WRITE_TYPE_RSP, + ESP_GATT_AUTH_REQ_NONE); +} + +void esp_get_app_attributes(uint8_t *appidentifier, uint16_t appidentifier_len, uint8_t num_attr, uint8_t *p_app_attrs) +{ + uint8_t buffer[600] = {0}; + uint32_t index = 0; + buffer[0] = CommandIDGetAppAttributes; + index ++; + memcpy(&buffer[index], appidentifier, appidentifier_len); + index += appidentifier_len; + memcpy(&buffer[index], p_app_attrs, num_attr); + index += num_attr; + + esp_ble_gattc_write_char( gl_profile_tab[PROFILE_A_APP_ID].gattc_if, + gl_profile_tab[PROFILE_A_APP_ID].conn_id, + gl_profile_tab[PROFILE_A_APP_ID].contol_point_handle, + index, + buffer, + ESP_GATT_WRITE_TYPE_RSP, + ESP_GATT_AUTH_REQ_NONE); +} + +void esp_perform_notification_action(uint8_t *notificationUID, uint8_t ActionID) +{ + uint8_t buffer[600] = {0}; + uint32_t index = 0; + buffer[0] = CommandIDPerformNotificationAction; + index ++; + memcpy(&buffer[index], notificationUID, ESP_NOTIFICATIONUID_LEN); + index += ESP_NOTIFICATIONUID_LEN; + buffer[index] = ActionID; + index ++; + esp_ble_gattc_write_char( gl_profile_tab[PROFILE_A_APP_ID].gattc_if, + gl_profile_tab[PROFILE_A_APP_ID].conn_id, + gl_profile_tab[PROFILE_A_APP_ID].contol_point_handle, + index, + buffer, + ESP_GATT_WRITE_TYPE_RSP, + ESP_GATT_AUTH_REQ_NONE); +} + +static void periodic_timer_callback(void* arg) +{ + esp_timer_stop(periodic_timer); + if (data_buffer.len > 0) { + esp_receive_apple_data_source(data_buffer.buffer, data_buffer.len); + memset(data_buffer.buffer, 0, data_buffer.len); + data_buffer.len = 0; + } +} + +static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) +{ + ESP_LOGV(BLE_ANCS_TAG, "GAP_EVT, event %d\n", event); + + switch (event) { + case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT: + adv_config_done &= (~SCAN_RSP_CONFIG_FLAG); + if (adv_config_done == 0) { + esp_ble_gap_start_advertising(&adv_params); + } + break; + case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: + adv_config_done &= (~ADV_CONFIG_FLAG); + if (adv_config_done == 0) { + esp_ble_gap_start_advertising(&adv_params); + } + break; + case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: + //advertising start complete event to indicate advertising start successfully or failed + if (param->adv_start_cmpl.status != ESP_BT_STATUS_SUCCESS) { + ESP_LOGE(BLE_ANCS_TAG, "advertising start failed, error status = %x", param->adv_start_cmpl.status); + break; + } + ESP_LOGI(BLE_ANCS_TAG, "advertising start success"); + break; + case ESP_GAP_BLE_PASSKEY_REQ_EVT: /* passkey request event */ + ESP_LOGI(BLE_ANCS_TAG, "ESP_GAP_BLE_PASSKEY_REQ_EVT"); + /* Call the following function to input the passkey which is displayed on the remote device */ + //esp_ble_passkey_reply(heart_rate_profile_tab[HEART_PROFILE_APP_IDX].remote_bda, true, 0x00); + break; + case ESP_GAP_BLE_OOB_REQ_EVT: { + ESP_LOGI(BLE_ANCS_TAG, "ESP_GAP_BLE_OOB_REQ_EVT"); + uint8_t tk[16] = {1}; //If you paired with OOB, both devices need to use the same tk + esp_ble_oob_req_reply(param->ble_security.ble_req.bd_addr, tk, sizeof(tk)); + break; + } + case ESP_GAP_BLE_NC_REQ_EVT: + /* The app will receive this evt when the IO has DisplayYesNO capability and the peer device IO also has DisplayYesNo capability. + show the passkey number to the user to confirm it with the number displayed by peer device. */ + esp_ble_confirm_reply(param->ble_security.ble_req.bd_addr, true); + ESP_LOGI(BLE_ANCS_TAG, "ESP_GAP_BLE_NC_REQ_EVT, the passkey Notify number:%d", param->ble_security.key_notif.passkey); + break; + case ESP_GAP_BLE_SEC_REQ_EVT: + /* send the positive(true) security response to the peer device to accept the security request. + If not accept the security request, should send the security response with negative(false) accept value*/ + esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true); + break; + case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: ///the app will receive this evt when the IO has Output capability and the peer device IO has Input capability. + ///show the passkey number to the user to input it in the peer device. + ESP_LOGI(BLE_ANCS_TAG, "The passkey Notify number:%06d", param->ble_security.key_notif.passkey); + break; + case ESP_GAP_BLE_AUTH_CMPL_EVT: { + esp_log_buffer_hex("addr", param->ble_security.auth_cmpl.bd_addr, ESP_BD_ADDR_LEN); + ESP_LOGI(BLE_ANCS_TAG, "pair status = %s",param->ble_security.auth_cmpl.success ? "success" : "fail"); + if (!param->ble_security.auth_cmpl.success) { + ESP_LOGI(BLE_ANCS_TAG, "fail reason = 0x%x",param->ble_security.auth_cmpl.fail_reason); + } + break; + } + case ESP_GAP_BLE_SET_LOCAL_PRIVACY_COMPLETE_EVT: + if (param->local_privacy_cmpl.status != ESP_BT_STATUS_SUCCESS) { + ESP_LOGE(BLE_ANCS_TAG, "config local privacy failed, error status = %x", param->local_privacy_cmpl.status); + break; + } + + esp_err_t ret = esp_ble_gap_config_adv_data(&adv_config); + if (ret) { + ESP_LOGE(BLE_ANCS_TAG, "config adv data failed, error code = %x", ret); + } else { + adv_config_done |= ADV_CONFIG_FLAG; + } + + ret = esp_ble_gap_config_adv_data(&scan_rsp_config); + if (ret) { + ESP_LOGE(BLE_ANCS_TAG, "config adv data failed, error code = %x", ret); + } else { + adv_config_done |= SCAN_RSP_CONFIG_FLAG; + } + + break; + default: + break; + } +} + +static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) +{ + switch (event) { + case ESP_GATTC_REG_EVT: + ESP_LOGI(BLE_ANCS_TAG, "REG_EVT"); + esp_ble_gap_set_device_name(EXCAMPLE_DEVICE_NAME); + esp_ble_gap_config_local_icon (ESP_BLE_APPEARANCE_GENERIC_WATCH); + //generate a resolvable random address + esp_ble_gap_config_local_privacy(true); + break; + case ESP_GATTC_OPEN_EVT: + if (param->open.status != ESP_GATT_OK) { + ESP_LOGE(BLE_ANCS_TAG, "open failed, error status = %x", param->open.status); + break; + } + ESP_LOGI(BLE_ANCS_TAG, "ESP_GATTC_OPEN_EVT"); + gl_profile_tab[PROFILE_A_APP_ID].conn_id = param->open.conn_id; + esp_ble_set_encryption(param->open.remote_bda, ESP_BLE_SEC_ENCRYPT_MITM); + esp_err_t mtu_ret = esp_ble_gattc_send_mtu_req (gattc_if, param->open.conn_id); + if (mtu_ret) { + ESP_LOGE(BLE_ANCS_TAG, "config MTU error, error code = %x", mtu_ret); + } + break; + case ESP_GATTC_CFG_MTU_EVT: + if (param->cfg_mtu.status != ESP_GATT_OK) { + ESP_LOGE(BLE_ANCS_TAG,"config mtu failed, error status = %x", param->cfg_mtu.status); + } + ESP_LOGI(BLE_ANCS_TAG, "ESP_GATTC_CFG_MTU_EVT, Status %d, MTU %d, conn_id %d", param->cfg_mtu.status, param->cfg_mtu.mtu, param->cfg_mtu.conn_id); + gl_profile_tab[PROFILE_A_APP_ID].MTU_size = param->cfg_mtu.mtu; + memcpy(apple_nc_uuid.uuid.uuid128, Apple_NC_UUID,16); + esp_ble_gattc_search_service(gl_profile_tab[PROFILE_A_APP_ID].gattc_if, gl_profile_tab[PROFILE_A_APP_ID].conn_id, &apple_nc_uuid); + break; + case ESP_GATTC_SEARCH_RES_EVT: { + if (param->search_res.srvc_id.uuid.len == ESP_UUID_LEN_128) { + gl_profile_tab[PROFILE_A_APP_ID].service_start_handle = param->search_res.start_handle; + gl_profile_tab[PROFILE_A_APP_ID].service_end_handle = param->search_res.end_handle; + get_service = true; + } + break; + } + case ESP_GATTC_SEARCH_CMPL_EVT: + if (param->search_cmpl.status != ESP_GATT_OK) { + ESP_LOGE(BLE_ANCS_TAG, "search service failed, error status = %x", param->search_cmpl.status); + break; + } + ESP_LOGI(BLE_ANCS_TAG, "ESP_GATTC_SEARCH_CMPL_EVT"); + if (get_service) { + uint16_t count = 0; + uint16_t offset = 0; + esp_gatt_status_t ret_status = esp_ble_gattc_get_attr_count(gattc_if, + gl_profile_tab[PROFILE_A_APP_ID].conn_id, + ESP_GATT_DB_CHARACTERISTIC, + gl_profile_tab[PROFILE_A_APP_ID].service_start_handle, + gl_profile_tab[PROFILE_A_APP_ID].service_end_handle, + INVALID_HANDLE, + &count); + if (ret_status != ESP_GATT_OK) { + ESP_LOGE(BLE_ANCS_TAG, "esp_ble_gattc_get_attr_count error, %d", __LINE__); + } + if (count > 0) { + char_elem_result = (esp_gattc_char_elem_t *)malloc(sizeof(esp_gattc_char_elem_t) * count); + memset(char_elem_result, 0xff, sizeof(esp_gattc_char_elem_t) * count); + if (!char_elem_result) { + ESP_LOGE(BLE_ANCS_TAG, "gattc no mem"); + } else { + ret_status = esp_ble_gattc_get_all_char(gattc_if, + gl_profile_tab[PROFILE_A_APP_ID].conn_id, + gl_profile_tab[PROFILE_A_APP_ID].service_start_handle, + gl_profile_tab[PROFILE_A_APP_ID].service_end_handle, + char_elem_result, + &count, + offset); + if (ret_status != ESP_GATT_OK) { + ESP_LOGE(BLE_ANCS_TAG, "esp_ble_gattc_get_all_char error, %d", __LINE__); + } + if (count > 0) { + + for (int i = 0; i < count; i ++) { + if (char_elem_result[i].uuid.len == ESP_UUID_LEN_128) { + if (char_elem_result[i].properties & ESP_GATT_CHAR_PROP_BIT_NOTIFY + && memcmp(char_elem_result[i].uuid.uuid.uuid128, notification_source, 16) == 0) { + gl_profile_tab[PROFILE_A_APP_ID].notification_source_handle = char_elem_result[i].char_handle; + esp_ble_gattc_register_for_notify (gattc_if, + gl_profile_tab[PROFILE_A_APP_ID].remote_bda, + char_elem_result[i].char_handle); + ESP_LOGI(BLE_ANCS_TAG, "Find Apple noticification source char"); + + } else if (char_elem_result[i].properties & ESP_GATT_CHAR_PROP_BIT_NOTIFY + && memcmp(char_elem_result[i].uuid.uuid.uuid128, data_source, 16) == 0) { + gl_profile_tab[PROFILE_A_APP_ID].data_source_handle = char_elem_result[i].char_handle; + esp_ble_gattc_register_for_notify (gattc_if, + gl_profile_tab[PROFILE_A_APP_ID].remote_bda, + char_elem_result[i].char_handle); + ESP_LOGI(BLE_ANCS_TAG, "Find Apple data source char"); + + } else if (char_elem_result[i].properties & ESP_GATT_CHAR_PROP_BIT_WRITE + && memcmp(char_elem_result[i].uuid.uuid.uuid128, control_point, 16) == 0) { + gl_profile_tab[PROFILE_A_APP_ID].contol_point_handle = char_elem_result[i].char_handle; + ESP_LOGI(BLE_ANCS_TAG, "Find Apple control point char"); + + } + } + } + } + } + free(char_elem_result); + } + } else { + ESP_LOGE(BLE_ANCS_TAG, "No Apple Notification Service found"); + } + + break; + case ESP_GATTC_REG_FOR_NOTIFY_EVT: { + if (param->reg_for_notify.status != ESP_GATT_OK) { + ESP_LOGI(BLE_ANCS_TAG, "ESP_GATTC_REG_FOR_NOTIFY_EVT status %d", param->reg_for_notify.status); + break; + } + uint16_t count = 0; + uint16_t offset = 0; + //uint16_t notify_en = 1; + uint8_t notify_en[2] = {0x01, 0x00}; + esp_gatt_status_t ret_status = esp_ble_gattc_get_attr_count(gattc_if, + gl_profile_tab[PROFILE_A_APP_ID].conn_id, + ESP_GATT_DB_DESCRIPTOR, + gl_profile_tab[PROFILE_A_APP_ID].service_start_handle, + gl_profile_tab[PROFILE_A_APP_ID].service_end_handle, + param->reg_for_notify.handle, + &count); + if (ret_status != ESP_GATT_OK) { + ESP_LOGE(BLE_ANCS_TAG, "esp_ble_gattc_get_attr_count error, %d", __LINE__); + } + if (count > 0) { + descr_elem_result = malloc(sizeof(esp_gattc_descr_elem_t) * count); + if (!descr_elem_result) { + ESP_LOGE(BLE_ANCS_TAG, "malloc error, gattc no mem"); + } else { + ret_status = esp_ble_gattc_get_all_descr(gattc_if, + gl_profile_tab[PROFILE_A_APP_ID].conn_id, + param->reg_for_notify.handle, + descr_elem_result, + &count, + offset); + if (ret_status != ESP_GATT_OK) { + ESP_LOGE(BLE_ANCS_TAG, "esp_ble_gattc_get_all_descr error, %d", __LINE__); + } + + for (int i = 0; i < count; ++ i) { + if (descr_elem_result[i].uuid.len == ESP_UUID_LEN_16 && descr_elem_result[i].uuid.uuid.uuid16 == ESP_GATT_UUID_CHAR_CLIENT_CONFIG) { + esp_ble_gattc_write_char_descr (gattc_if, + gl_profile_tab[PROFILE_A_APP_ID].conn_id, + descr_elem_result[i].handle, + sizeof(notify_en), + (uint8_t *)¬ify_en, + ESP_GATT_WRITE_TYPE_RSP, + ESP_GATT_AUTH_REQ_NONE); + + break; + } + } + } + free(descr_elem_result); + } + break; + } + case ESP_GATTC_NOTIFY_EVT: + //esp_log_buffer_hex(BLE_ANCS_TAG, param->notify.value, param->notify.value_len); + if (param->notify.handle == gl_profile_tab[PROFILE_A_APP_ID].notification_source_handle) { + esp_receive_apple_notification_source(param->notify.value, param->notify.value_len); + uint8_t *notificationUID = ¶m->notify.value[4]; + if (param->notify.value[0] == EventIDNotificationAdded && param->notify.value[2] == CategoryIDIncomingCall) { + ESP_LOGI(BLE_ANCS_TAG, "IncomingCall, reject"); + //Call reject + esp_perform_notification_action(notificationUID, ActionIDNegative); + } else if (param->notify.value[0] == EventIDNotificationAdded) { + //get more information + ESP_LOGI(BLE_ANCS_TAG, "Get detailed information"); + esp_get_notification_attributes(notificationUID, sizeof(p_attr)/sizeof(esp_noti_attr_list_t), p_attr); + } + } else if (param->notify.handle == gl_profile_tab[PROFILE_A_APP_ID].data_source_handle) { + memcpy(&data_buffer.buffer[data_buffer.len], param->notify.value, param->notify.value_len); + data_buffer.len += param->notify.value_len; + if (param->notify.value_len == (gl_profile_tab[PROFILE_A_APP_ID].MTU_size - 3)) { + // cpoy and wait next packet, start timer 500ms + esp_timer_start_periodic(periodic_timer, 500000); + } else { + esp_timer_stop(periodic_timer); + esp_receive_apple_data_source(data_buffer.buffer, data_buffer.len); + memset(data_buffer.buffer, 0, data_buffer.len); + data_buffer.len = 0; + } + } else { + ESP_LOGI(BLE_ANCS_TAG, "unknown handle, receive notify value:"); + } + break; + case ESP_GATTC_WRITE_DESCR_EVT: + if (param->write.status != ESP_GATT_OK) { + ESP_LOGE(BLE_ANCS_TAG, "write descr failed, error status = %x", param->write.status); + break; + } + //ESP_LOGI(BLE_ANCS_TAG, "write descr successfully"); + break; + case ESP_GATTC_SRVC_CHG_EVT: { + ESP_LOGI(BLE_ANCS_TAG, "ESP_GATTC_SRVC_CHG_EVT, bd_addr:"); + esp_log_buffer_hex(BLE_ANCS_TAG, param->srvc_chg.remote_bda, 6); + break; + } + case ESP_GATTC_WRITE_CHAR_EVT: + if (param->write.status != ESP_GATT_OK) { + char *Errstr = Errcode_to_String(param->write.status); + if (Errstr) { + ESP_LOGE(BLE_ANCS_TAG, "write control point error %s", Errstr); + } + break; + } + //ESP_LOGI(BLE_ANCS_TAG, "Write char success "); + break; + case ESP_GATTC_DISCONNECT_EVT: + ESP_LOGI(BLE_ANCS_TAG, "ESP_GATTC_DISCONNECT_EVT, reason = 0x%x", param->disconnect.reason); + get_service = false; + esp_ble_gap_start_advertising(&adv_params); + break; + case ESP_GATTC_CONNECT_EVT: + //ESP_LOGI(BLE_ANCS_TAG, "ESP_GATTC_CONNECT_EVT"); + //esp_log_buffer_hex("bda", param->connect.remote_bda, 6); + memcpy(gl_profile_tab[PROFILE_A_APP_ID].remote_bda, param->connect.remote_bda, 6); + // create gattc virtual connection + esp_ble_gattc_open(gl_profile_tab[PROFILE_A_APP_ID].gattc_if, gl_profile_tab[PROFILE_A_APP_ID].remote_bda, BLE_ADDR_TYPE_RANDOM, true); + break; + case ESP_GATTC_DIS_SRVC_CMPL_EVT: + ESP_LOGI(BLE_ANCS_TAG, "ESP_GATTC_DIS_SRVC_CMPL_EVT"); + break; + default: + break; + } +} + +static void esp_gattc_cb(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) +{ + /* If event is register event, store the gattc_if for each profile */ + if (event == ESP_GATTC_REG_EVT) { + if (param->reg.status == ESP_GATT_OK) { + gl_profile_tab[param->reg.app_id].gattc_if = gattc_if; + } else { + ESP_LOGI(BLE_ANCS_TAG, "Reg app failed, app_id %04x, status %d", + param->reg.app_id, + param->reg.status); + return; + } + } + + /* If the gattc_if equal to profile A, call profile A cb handler, + * so here call each profile's callback */ + do { + int idx; + for (idx = 0; idx < PROFILE_NUM; idx++) { + if (gattc_if == ESP_GATT_IF_NONE || /* ESP_GATT_IF_NONE, not specify a certain gatt_if, need to call every profile cb function */ + gattc_if == gl_profile_tab[idx].gattc_if) { + if (gl_profile_tab[idx].gattc_cb) { + gl_profile_tab[idx].gattc_cb(event, gattc_if, param); + } + } + } + } while (0); +} + +void init_timer(void) +{ + ESP_ERROR_CHECK(esp_timer_create(&periodic_timer_args, &periodic_timer)); +} + +void app_main() +{ + esp_err_t ret; + + // Initialize NVS. + ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK( ret ); + + // init timer + init_timer(); + + ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT)); + + esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + ret = esp_bt_controller_init(&bt_cfg); + if (ret) { + ESP_LOGE(BLE_ANCS_TAG, "%s init controller failed: %s", __func__, esp_err_to_name(ret)); + return; + } + ret = esp_bt_controller_enable(ESP_BT_MODE_BLE); + if (ret) { + ESP_LOGE(BLE_ANCS_TAG, "%s enable controller failed: %s", __func__, esp_err_to_name(ret)); + return; + } + + ESP_LOGI(BLE_ANCS_TAG, "%s init bluetooth", __func__); + ret = esp_bluedroid_init(); + if (ret) { + ESP_LOGE(BLE_ANCS_TAG, "%s init bluetooth failed: %s", __func__, esp_err_to_name(ret)); + return; + } + ret = esp_bluedroid_enable(); + if (ret) { + ESP_LOGE(BLE_ANCS_TAG, "%s enable bluetooth failed: %s", __func__, esp_err_to_name(ret)); + return; + } + + //register the callback function to the gattc module + ret = esp_ble_gattc_register_callback(esp_gattc_cb); + if (ret) { + ESP_LOGE(BLE_ANCS_TAG, "%s gattc register error, error code = %x\n", __func__, ret); + return; + } + + ret = esp_ble_gap_register_callback(gap_event_handler); + if (ret) { + ESP_LOGE(BLE_ANCS_TAG, "gap register error, error code = %x", ret); + return; + } + + ret = esp_ble_gattc_app_register(PROFILE_A_APP_ID); + if (ret) { + ESP_LOGE(BLE_ANCS_TAG, "%s gattc app register error, error code = %x\n", __func__, ret); + } + + ret = esp_ble_gatt_set_local_mtu(500); + if (ret) { + ESP_LOGE(BLE_ANCS_TAG, "set local MTU failed, error code = %x", ret); + } + + /* set the security iocap & auth_req & key size & init key response key parameters to the stack*/ + esp_ble_auth_req_t auth_req = ESP_LE_AUTH_REQ_SC_MITM_BOND; //bonding with peer device after authentication + esp_ble_io_cap_t iocap = ESP_IO_CAP_NONE; //set the IO capability to No output No input + uint8_t key_size = 16; //the key size should be 7~16 bytes + uint8_t init_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK; + uint8_t rsp_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK; + //set static passkey + uint32_t passkey = 123456; + uint8_t auth_option = ESP_BLE_ONLY_ACCEPT_SPECIFIED_AUTH_DISABLE; + uint8_t oob_support = ESP_BLE_OOB_DISABLE; + esp_ble_gap_set_security_param(ESP_BLE_SM_SET_STATIC_PASSKEY, &passkey, sizeof(uint32_t)); + esp_ble_gap_set_security_param(ESP_BLE_SM_AUTHEN_REQ_MODE, &auth_req, sizeof(uint8_t)); + esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t)); + esp_ble_gap_set_security_param(ESP_BLE_SM_MAX_KEY_SIZE, &key_size, sizeof(uint8_t)); + esp_ble_gap_set_security_param(ESP_BLE_SM_ONLY_ACCEPT_SPECIFIED_SEC_AUTH, &auth_option, sizeof(uint8_t)); + esp_ble_gap_set_security_param(ESP_BLE_SM_OOB_SUPPORT, &oob_support, sizeof(uint8_t)); + /* If your BLE device acts as a Slave, the init_key means you hope which types of key of the master should distribute to you, + and the response key means which key you can distribute to the master; + If your BLE device acts as a master, the response key means you hope which types of key of the slave should distribute to you, + and the init key means which key you can distribute to the slave. */ + esp_ble_gap_set_security_param(ESP_BLE_SM_SET_INIT_KEY, &init_key, sizeof(uint8_t)); + esp_ble_gap_set_security_param(ESP_BLE_SM_SET_RSP_KEY, &rsp_key, sizeof(uint8_t)); +} + + + diff --git a/examples/bluetooth/ble_ancs/main/component.mk b/examples/bluetooth/ble_ancs/main/component.mk new file mode 100644 index 0000000000..f2f38c36c0 --- /dev/null +++ b/examples/bluetooth/ble_ancs/main/component.mk @@ -0,0 +1,9 @@ +# +# Main Makefile. This is basically the same as a component makefile. +# +# This Makefile should, at the very least, just include $(SDK_PATH)/make/component_common.mk. By default, +# this will take the sources in the src/ directory, compile them and link them into +# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, +# please read the ESP-IDF documents if you need to do this. +# + diff --git a/examples/bluetooth/ble_ancs/sdkconfig.defaults b/examples/bluetooth/ble_ancs/sdkconfig.defaults new file mode 100644 index 0000000000..50fc4ba9d8 --- /dev/null +++ b/examples/bluetooth/ble_ancs/sdkconfig.defaults @@ -0,0 +1,6 @@ +# Override some defaults so BT stack is enabled +# by default in this example +CONFIG_BT_ENABLED=y +CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY=y +CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY= +CONFIG_BTDM_CONTROLLER_MODE_BTDM= From faf23df19ac936710b12d36d56683d0bf0468189 Mon Sep 17 00:00:00 2001 From: baohongde Date: Fri, 14 Sep 2018 17:24:25 +0800 Subject: [PATCH 075/486] component/bt : modify OSI thread of bluedroid abstract of OSI thread to make bluedroid more compatible with different OS. --- components/bt/CMakeLists.txt | 1 + .../bt/bluedroid/bta/sys/bta_sys_main.c | 5 +- components/bt/bluedroid/btc/core/btc_task.c | 77 ++--- .../btc/profile/std/a2dp/btc_a2dp_sink.c | 132 +++----- .../btc/profile/std/a2dp/btc_a2dp_source.c | 133 +++------ .../common/include/common/bt_target.h | 8 + components/bt/bluedroid/hci/hci_hal_h4.c | 52 ++-- components/bt/bluedroid/hci/hci_layer.c | 83 ++---- .../bt/bluedroid/hci/include/hci/hci_layer.h | 3 + .../bt/bluedroid/osi/include/osi/thread.h | 92 ++---- components/bt/bluedroid/osi/thread.c | 282 ++++++++++++++++++ components/bt/bluedroid/stack/btu/btu_hcif.c | 4 +- components/bt/bluedroid/stack/btu/btu_init.c | 40 +-- components/bt/bluedroid/stack/btu/btu_task.c | 99 +++--- .../bt/bluedroid/stack/include/stack/btu.h | 14 + 15 files changed, 581 insertions(+), 444 deletions(-) create mode 100644 components/bt/bluedroid/osi/thread.c diff --git a/components/bt/CMakeLists.txt b/components/bt/CMakeLists.txt index e1d1fef01c..73f6879039 100644 --- a/components/bt/CMakeLists.txt +++ b/components/bt/CMakeLists.txt @@ -189,6 +189,7 @@ if(CONFIG_BT_ENABLED) "bluedroid/osi/mutex.c" "bluedroid/osi/osi.c" "bluedroid/osi/semaphore.c" + "bluedroid/osi/thread.c" "bluedroid/stack/a2dp/a2d_api.c" "bluedroid/stack/a2dp/a2d_sbc.c" "bluedroid/stack/avct/avct_api.c" diff --git a/components/bt/bluedroid/bta/sys/bta_sys_main.c b/components/bt/bluedroid/bta/sys/bta_sys_main.c index e3b8c77fa9..c9a7f450f4 100644 --- a/components/bt/bluedroid/bta/sys/bta_sys_main.c +++ b/components/bt/bluedroid/bta/sys/bta_sys_main.c @@ -29,6 +29,7 @@ #include "osi/alarm.h" #include "osi/thread.h" #include "stack/btm_api.h" +#include "stack/btu.h" #include "bta/bta_api.h" #include "bta/bta_sys.h" #include "bta_sys_int.h" @@ -571,7 +572,7 @@ void bta_sys_sendmsg(void *p_msg) // there is a procedure in progress that can schedule a task via this // message queue. This causes |btu_bta_msg_queue| to get cleaned up before // it gets used here; hence we check for NULL before using it. - if (btu_task_post(SIG_BTU_BTA_MSG, p_msg, TASK_POST_BLOCKING) != TASK_POST_SUCCESS) { + if (btu_task_post(SIG_BTU_BTA_MSG, p_msg, OSI_THREAD_BLOCKING) == false) { osi_free(p_msg); } } @@ -591,7 +592,7 @@ void bta_alarm_cb(void *data) assert(data != NULL); TIMER_LIST_ENT *p_tle = (TIMER_LIST_ENT *)data; - btu_task_post(SIG_BTU_BTA_ALARM, p_tle, TASK_POST_BLOCKING); + btu_task_post(SIG_BTU_BTA_ALARM, p_tle, OSI_THREAD_BLOCKING); } void bta_sys_start_timer(TIMER_LIST_ENT *p_tle, UINT16 type, INT32 timeout_ms) diff --git a/components/bt/bluedroid/btc/core/btc_task.c b/components/bt/bluedroid/btc/core/btc_task.c index 86d2b4a63e..2787bc2056 100644 --- a/components/bt/bluedroid/btc/core/btc_task.c +++ b/components/bt/bluedroid/btc/core/btc_task.c @@ -47,9 +47,13 @@ #endif /* #if BTC_HF_CLIENT_INCLUDED */ #endif /* #if CONFIG_BT_CLASSIC_ENABLED */ +#define BTC_TASK_PINNED_TO_CORE (TASK_PINNED_TO_CORE) +#define BTC_TASK_STACK_SIZE (CONFIG_BTC_TASK_STACK_SIZE + BT_TASK_EXTRA_STACK_SIZE) //by menuconfig +#define BTC_TASK_NAME "btcT" +#define BTC_TASK_PRIO (configMAX_PRIORITIES - 6) +#define BTC_TASK_QUEUE_LEN 60 -static xTaskHandle xBtcTaskHandle = NULL; -static xQueueHandle xBtcQueue = 0; +static osi_thread_t *btc_thread; static btc_func_t profile_tab[BTC_PID_NUM] = { [BTC_PID_MAIN_INIT] = {btc_main_call_handler, NULL }, @@ -96,38 +100,40 @@ static btc_func_t profile_tab[BTC_PID_NUM] = { ** ** Description Process profile Task Thread. ******************************************************************************/ -static void btc_task(void *arg) +static void btc_thread_handler(void *arg) { - btc_msg_t msg; + btc_msg_t *msg = (btc_msg_t *)arg; - for (;;) { - if (pdTRUE == xQueueReceive(xBtcQueue, &msg, (portTickType)portMAX_DELAY)) { - BTC_TRACE_DEBUG("%s msg %u %u %u %p\n", __func__, msg.sig, msg.pid, msg.act, msg.arg); - switch (msg.sig) { - case BTC_SIG_API_CALL: - profile_tab[msg.pid].btc_call(&msg); - break; - case BTC_SIG_API_CB: - profile_tab[msg.pid].btc_cb(&msg); - break; - default: - break; - } - if (msg.arg) { - osi_free(msg.arg); - } - } + BTC_TRACE_DEBUG("%s msg %u %u %u %p\n", __func__, msg->sig, msg->pid, msg->act, msg->arg); + switch (msg->sig) { + case BTC_SIG_API_CALL: + profile_tab[msg->pid].btc_call(msg); + break; + case BTC_SIG_API_CB: + profile_tab[msg->pid].btc_cb(msg); + break; + default: + break; } + + if (msg->arg) { + osi_free(msg->arg); + } + osi_free(msg); } -static bt_status_t btc_task_post(btc_msg_t *msg, task_post_t timeout) +static bt_status_t btc_task_post(btc_msg_t *msg, osi_thread_blocking_t blocking) { - if (msg == NULL) { - return BT_STATUS_PARM_INVALID; + btc_msg_t *lmsg; + + lmsg = (btc_msg_t *)osi_malloc(sizeof(btc_msg_t)); + if (lmsg == NULL) { + return BT_STATUS_NOMEM; } - if (xQueueSend(xBtcQueue, msg, timeout) != pdTRUE) { - BTC_TRACE_ERROR("Btc Post failed\n"); + memcpy(lmsg, msg, sizeof(btc_msg_t)); + + if (osi_thread_post(btc_thread, btc_thread_handler, lmsg, 0, blocking) == false) { return BT_STATUS_BUSY; } @@ -159,17 +165,18 @@ bt_status_t btc_transfer_context(btc_msg_t *msg, void *arg, int arg_len, btc_arg lmsg.arg = NULL; } - return btc_task_post(&lmsg, TASK_POST_BLOCKING); + return btc_task_post(&lmsg, OSI_THREAD_BLOCKING); + } int btc_init(void) { - xBtcQueue = xQueueCreate(BTC_TASK_QUEUE_LEN, sizeof(btc_msg_t)); - xTaskCreatePinnedToCore(btc_task, "Btc_task", BTC_TASK_STACK_SIZE, NULL, BTC_TASK_PRIO, &xBtcTaskHandle, BTC_TASK_PINNED_TO_CORE); - if (xBtcTaskHandle == NULL || xBtcQueue == 0){ + btc_thread = osi_thread_create("BTC_TASK", BTC_TASK_STACK_SIZE, BTC_TASK_PRIO, BTC_TASK_PINNED_TO_CORE, 1); + if (btc_thread == NULL) { return BT_STATUS_NOMEM; } + btc_gap_callback_init(); #if SCAN_QUEUE_CONGEST_CHECK btc_adv_list_init(); @@ -180,20 +187,18 @@ int btc_init(void) void btc_deinit(void) { - vTaskDelete(xBtcTaskHandle); - vQueueDelete(xBtcQueue); + osi_thread_free(btc_thread); + btc_thread = NULL; #if SCAN_QUEUE_CONGEST_CHECK btc_adv_list_deinit(); #endif - xBtcTaskHandle = NULL; - xBtcQueue = 0; } bool btc_check_queue_is_congest(void) { - UBaseType_t wait_size = uxQueueMessagesWaiting(xBtcQueue); - if(wait_size >= QUEUE_CONGEST_SIZE) { + if (osi_thread_queue_wait_size(btc_thread, 0) >= QUEUE_CONGEST_SIZE) { return true; } + return false; } diff --git a/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_sink.c b/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_sink.c index a5d09b2ca1..c8e1ec9c90 100644 --- a/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_sink.c +++ b/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_sink.c @@ -45,6 +45,13 @@ #if (BTC_AV_SINK_INCLUDED == TRUE) +/* Macro */ +#define BTC_A2DP_SINK_TASK_PINNED_TO_CORE (TASK_PINNED_TO_CORE) +#define BTC_A2DP_SINK_TASK_STACK_SIZE (CONFIG_A2DP_SINK_TASK_STACK_SIZE + BT_TASK_EXTRA_STACK_SIZE) // by menuconfig +#define BTC_A2DP_SINK_TASK_NAME "BtA2dSinkT" +#define BTC_A2DP_SINK_TASK_PRIO (configMAX_PRIORITIES - 3) + + /***************************************************************************** ** Constants *****************************************************************************/ @@ -64,10 +71,6 @@ enum { BTC_A2DP_SINK_STATE_SHUTTING_DOWN = 2 }; -enum { - BTC_A2DP_SINK_DATA_EVT = 0, -}; - /* * CONGESTION COMPENSATION CTRL :: * @@ -90,6 +93,11 @@ enum { /* 18 frames is equivalent to 6.89*18*2.9 ~= 360 ms @ 44.1 khz, 20 ms mediatick */ #define MAX_OUTPUT_A2DP_SNK_FRAME_QUEUE_SZ (18) +typedef struct { + uint32_t sig; + void *param; +} a2dp_sink_task_evt_t; + typedef struct { UINT16 num_frames_to_be_processed; UINT16 len; @@ -115,18 +123,15 @@ static void btc_a2dp_sink_handle_inc_media(tBT_SBC_HDR *p_msg); static void btc_a2dp_sink_handle_decoder_reset(tBTC_MEDIA_SINK_CFG_UPDATE *p_msg); static void btc_a2dp_sink_handle_clear_track(void); static BOOLEAN btc_a2dp_sink_clear_track(void); -static void btc_a2dp_sink_task_handler(void *arg); -static void btc_a2dp_sink_data_ready(UNUSED_ATTR void *context); +static void btc_a2dp_sink_ctrl_handler(void *arg); + +static void btc_a2dp_sink_data_ready(void *context); static tBTC_A2DP_SINK_CB btc_aa_snk_cb; static int btc_a2dp_sink_state = BTC_A2DP_SINK_STATE_OFF; static future_t *btc_a2dp_sink_future = NULL; -static xTaskHandle btc_aa_snk_task_hdl = NULL; -static QueueHandle_t btc_aa_snk_data_queue = NULL; -static QueueHandle_t btc_aa_snk_ctrl_queue = NULL; -static QueueSetHandle_t btc_aa_snk_queue_set; - +static osi_thread_t *btc_aa_snk_task_hdl = NULL; static esp_a2d_sink_data_cb_t bt_aa_snk_data_cb = NULL; void btc_a2dp_sink_reg_data_cb(esp_a2d_sink_data_cb_t callback) @@ -174,26 +179,28 @@ static inline void btc_a2d_cb_to_app(esp_a2d_cb_event_t event, esp_a2d_cb_param_ ** BTC ADAPTATION *****************************************************************************/ -static void btc_a2dp_sink_ctrl_post(uint32_t sig, void *par) +static bool btc_a2dp_sink_ctrl_post(uint32_t sig, void *param) { - BtTaskEvt_t *evt = (BtTaskEvt_t *)osi_malloc(sizeof(BtTaskEvt_t)); + a2dp_sink_task_evt_t *evt = (a2dp_sink_task_evt_t *)osi_malloc(sizeof(a2dp_sink_task_evt_t)); + if (evt == NULL) { - return; + return false; } evt->sig = sig; - evt->par = par; + evt->param = param; - if (xQueueSend(btc_aa_snk_ctrl_queue, &evt, portMAX_DELAY) != pdTRUE) { - APPL_TRACE_WARNING("btc_aa_snk_ctrl_queue failed, sig 0x%x\n", sig); - } + return osi_thread_post(btc_aa_snk_task_hdl, btc_a2dp_sink_ctrl_handler, evt, 0, OSI_THREAD_BLOCKING); } -static void btc_a2dp_sink_ctrl_handler(BtTaskEvt_t *e) +static void btc_a2dp_sink_ctrl_handler(void *arg) { + a2dp_sink_task_evt_t *e = (a2dp_sink_task_evt_t *)arg; + if (e == NULL) { return; } + switch (e->sig) { case BTC_MEDIA_TASK_SINK_INIT: btc_a2dp_sink_thread_init(NULL); @@ -202,7 +209,7 @@ static void btc_a2dp_sink_ctrl_handler(BtTaskEvt_t *e) btc_a2dp_sink_thread_cleanup(NULL); break; case BTC_MEDIA_AUDIO_SINK_CFG_UPDATE: - btc_a2dp_sink_handle_decoder_reset(e->par); + btc_a2dp_sink_handle_decoder_reset(e->param); break; case BTC_MEDIA_AUDIO_SINK_CLEAR_TRACK: btc_a2dp_sink_handle_clear_track(); @@ -213,29 +220,12 @@ static void btc_a2dp_sink_ctrl_handler(BtTaskEvt_t *e) default: APPL_TRACE_WARNING("media task unhandled evt: 0x%x\n", e->sig); } - if (e->par != NULL) { - osi_free(e->par); - } -} -static void btc_a2dp_sink_task_handler(void *arg) -{ - QueueSetMemberHandle_t xActivatedMember; - BtTaskEvt_t *e = NULL; - for (;;) { - xActivatedMember = xQueueSelectFromSet(btc_aa_snk_queue_set, portMAX_DELAY); - if (xActivatedMember == btc_aa_snk_data_queue) { - int32_t data_evt; - xQueueReceive(xActivatedMember, &data_evt, 0); - if (data_evt == BTC_A2DP_SINK_DATA_EVT) { - btc_a2dp_sink_data_ready(NULL); - } - } else if (xActivatedMember == btc_aa_snk_ctrl_queue) { - xQueueReceive(xActivatedMember, &e, 0); - btc_a2dp_sink_ctrl_handler(e); - osi_free(e); - } + if (e->param != NULL) { + osi_free(e->param); } + + osi_free(e); } bool btc_a2dp_sink_startup(void) @@ -257,26 +247,14 @@ bool btc_a2dp_sink_startup(void) } #endif /* BTC_SBC_DEC_DYNAMIC_MEMORY == TRUE */ - btc_aa_snk_queue_set = xQueueCreateSet(BTC_A2DP_SINK_TASK_QUEUE_SET_LEN); - configASSERT(btc_aa_snk_queue_set); - btc_aa_snk_data_queue = xQueueCreate(BTC_A2DP_SINK_DATA_QUEUE_LEN, sizeof(int32_t)); - configASSERT(btc_aa_snk_data_queue); - xQueueAddToSet(btc_aa_snk_data_queue, btc_aa_snk_queue_set); - - btc_aa_snk_ctrl_queue = xQueueCreate(BTC_A2DP_SINK_CTRL_QUEUE_LEN, sizeof(void *)); - configASSERT(btc_aa_snk_ctrl_queue); - xQueueAddToSet(btc_aa_snk_ctrl_queue, btc_aa_snk_queue_set); - - if (!btc_aa_snk_data_queue || !btc_aa_snk_ctrl_queue || !btc_aa_snk_queue_set ) { - goto error_exit; - } - - xTaskCreatePinnedToCore(btc_a2dp_sink_task_handler, BTC_A2DP_SINK_TASK_NAME, BTC_A2DP_SINK_TASK_STACK_SIZE, NULL, BTC_A2DP_SINK_TASK_PRIO, &btc_aa_snk_task_hdl, BTC_A2DP_SINK_TASK_PINNED_TO_CORE); + btc_aa_snk_task_hdl = osi_thread_create(BTC_A2DP_SINK_TASK_NAME, BTC_A2DP_SINK_TASK_STACK_SIZE, BTC_A2DP_SINK_TASK_PRIO, BTC_A2DP_SINK_TASK_PINNED_TO_CORE, 2); if (btc_aa_snk_task_hdl == NULL) { goto error_exit; } - btc_a2dp_sink_ctrl_post(BTC_MEDIA_TASK_SINK_INIT, NULL); + if (btc_a2dp_sink_ctrl_post(BTC_MEDIA_TASK_SINK_INIT, NULL) == false) { + goto error_exit; + } APPL_TRACE_EVENT("## A2DP SINK MEDIA THREAD STARTED ##\n"); @@ -284,24 +262,11 @@ bool btc_a2dp_sink_startup(void) error_exit:; APPL_TRACE_ERROR("%s unable to start up media thread\n", __func__); - if (btc_aa_snk_task_hdl != NULL) { - vTaskDelete(btc_aa_snk_task_hdl); + osi_thread_free(btc_aa_snk_task_hdl); btc_aa_snk_task_hdl = NULL; } - if (btc_aa_snk_data_queue) { - vQueueDelete(btc_aa_snk_data_queue); - btc_aa_snk_data_queue = NULL; - } - if (btc_aa_snk_ctrl_queue) { - vQueueDelete(btc_aa_snk_ctrl_queue); - btc_aa_snk_ctrl_queue = NULL; - } - if (btc_aa_snk_queue_set) { - vQueueDelete(btc_aa_snk_queue_set); - btc_aa_snk_queue_set = NULL; - } #if (BTC_SBC_DEC_DYNAMIC_MEMORY == TRUE) if (btc_sbc_decoder_context_ptr) { osi_free(btc_sbc_decoder_context_ptr); @@ -332,18 +297,9 @@ void btc_a2dp_sink_shutdown(void) future_await(btc_a2dp_sink_future); btc_a2dp_sink_future = NULL; - vTaskDelete(btc_aa_snk_task_hdl); + osi_thread_free(btc_aa_snk_task_hdl); btc_aa_snk_task_hdl = NULL; - vQueueDelete(btc_aa_snk_data_queue); - btc_aa_snk_data_queue = NULL; - - vQueueDelete(btc_aa_snk_ctrl_queue); - btc_aa_snk_ctrl_queue = NULL; - - vQueueDelete(btc_aa_snk_queue_set); - btc_aa_snk_queue_set = NULL; - #if (BTC_SBC_DEC_DYNAMIC_MEMORY == TRUE) osi_free(btc_sbc_decoder_context_ptr); btc_sbc_decoder_context_ptr = NULL; @@ -397,11 +353,9 @@ void btc_a2dp_sink_on_suspended(tBTA_AV_SUSPEND *p_av) return; } -static void btc_a2dp_sink_data_post(int32_t data_type) +static void btc_a2dp_sink_data_post(void) { - if (xQueueSend(btc_aa_snk_data_queue, &data_type, 0) != pdTRUE) { - APPL_TRACE_DEBUG("Media data Q filled\n"); - } + osi_thread_post(btc_aa_snk_task_hdl, btc_a2dp_sink_data_ready, NULL, 1, OSI_THREAD_BLOCKING); } /******************************************************************************* @@ -415,8 +369,7 @@ static void btc_a2dp_sink_data_post(int32_t data_type) *******************************************************************************/ static BOOLEAN btc_a2dp_sink_clear_track(void) { - btc_a2dp_sink_ctrl_post(BTC_MEDIA_AUDIO_SINK_CLEAR_TRACK, NULL); - return TRUE; + return btc_a2dp_sink_ctrl_post(BTC_MEDIA_AUDIO_SINK_CLEAR_TRACK, NULL); } /* when true media task discards any rx frames */ @@ -685,8 +638,7 @@ BOOLEAN btc_a2dp_sink_rx_flush_req(void) return TRUE; } - btc_a2dp_sink_ctrl_post(BTC_MEDIA_FLUSH_AA_RX, NULL); - return TRUE; + return btc_a2dp_sink_ctrl_post(BTC_MEDIA_FLUSH_AA_RX, NULL); } /******************************************************************************* @@ -777,7 +729,7 @@ UINT8 btc_a2dp_sink_enque_buf(BT_HDR *p_pkt) p_msg->num_frames_to_be_processed = (*((UINT8 *)(p_msg + 1) + p_msg->offset)) & 0x0f; APPL_TRACE_VERBOSE("btc_a2dp_sink_enque_buf %d + \n", p_msg->num_frames_to_be_processed); fixed_queue_enqueue(btc_aa_snk_cb.RxSbcQ, p_msg); - btc_a2dp_sink_data_post(BTC_A2DP_SINK_DATA_EVT); + btc_a2dp_sink_data_post(); } else { /* let caller deal with a failed allocation */ APPL_TRACE_WARNING("btc_a2dp_sink_enque_buf No Buffer left - "); diff --git a/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_source.c b/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_source.c index 64e563b440..893ade6e0d 100644 --- a/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_source.c +++ b/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_source.c @@ -50,6 +50,13 @@ #if BTC_AV_SRC_INCLUDED +/* Macro */ +#define BTC_A2DP_SOURCE_TASK_PINNED_TO_CORE (TASK_PINNED_TO_CORE) +#define BTC_A2DP_SOURCE_TASK_STACK_SIZE (CONFIG_A2DP_SOURCE_TASK_STACK_SIZE + BT_TASK_EXTRA_STACK_SIZE) // by menuconfig +#define BTC_A2DP_SOURCE_TASK_NAME "BtA2dSourceT" +#define BTC_A2DP_SOURCE_TASK_PRIO (configMAX_PRIORITIES - 3) + + /***************************************************************************** ** Constants *****************************************************************************/ @@ -72,9 +79,6 @@ enum { BTC_A2DP_SOURCE_STATE_SHUTTING_DOWN = 2 }; -enum { - BTC_A2DP_SOURCE_DATA_EVT = 1, -}; /* Media task tick in milliseconds, must be set to multiple of (1000/TICKS_PER_SEC) */ @@ -127,6 +131,11 @@ enum { #define MAX_OUTPUT_A2DP_FRAME_QUEUE_SZ (5) #define MAX_OUTPUT_A2DP_SRC_FRAME_QUEUE_SZ (27) // 18 for 20ms tick +typedef struct { + uint32_t sig; + void *param; +} a2dp_src_task_evt_t; + typedef struct { UINT16 num_frames_to_be_processed; UINT16 len; @@ -174,15 +183,12 @@ static void btc_a2dp_source_aa_tx_flush(void); static void btc_a2dp_source_prep_2_send(UINT8 nb_frame); static void btc_a2dp_source_handle_timer(UNUSED_ATTR void *context); static void btc_a2dp_source_encoder_init(void); +static void btc_a2dp_source_ctrl_handler(void *arg); static tBTC_A2DP_SOURCE_CB btc_aa_src_cb; static int btc_a2dp_source_state = BTC_A2DP_SOURCE_STATE_OFF; static future_t *btc_a2dp_source_future = NULL; -static xTaskHandle btc_aa_src_task_hdl = NULL; -static QueueHandle_t btc_aa_src_data_queue = NULL; -static QueueHandle_t btc_aa_src_ctrl_queue = NULL; -static QueueSetHandle_t btc_aa_src_queue_set; - +static osi_thread_t *btc_aa_src_task_hdl = NULL; static esp_a2d_source_data_cb_t btc_aa_src_data_cb = NULL; static UINT64 last_frame_us = 0; @@ -234,26 +240,28 @@ bool btc_a2dp_source_is_task_shutting_down(void) return btc_a2dp_source_state == BTC_A2DP_SOURCE_STATE_SHUTTING_DOWN; } -static void btc_a2dp_source_ctrl_post(uint32_t sig, void *par) +static void btc_a2dp_source_ctrl_post(uint32_t sig, void *param) { - BtTaskEvt_t *evt = (BtTaskEvt_t *)osi_malloc(sizeof(BtTaskEvt_t)); + a2dp_src_task_evt_t *evt = (a2dp_src_task_evt_t *)osi_malloc(sizeof(a2dp_src_task_evt_t)); + if (evt == NULL) { return; } evt->sig = sig; - evt->par = par; + evt->param = param; - if (xQueueSend(btc_aa_src_ctrl_queue, &evt, portMAX_DELAY) != pdTRUE) { - APPL_TRACE_WARNING("btc_aa_src_ctrl_queue failed, sig 0x%x\n", sig); - } + osi_thread_post(btc_aa_src_task_hdl, btc_a2dp_source_ctrl_handler, evt, 0, OSI_THREAD_BLOCKING); } -static void btc_a2dp_source_ctrl_handler(BtTaskEvt_t *e) +static void btc_a2dp_source_ctrl_handler(void *arg) { + a2dp_src_task_evt_t *e = (a2dp_src_task_evt_t *)arg; + if (e == NULL) { return; } + switch (e->sig) { case BTC_MEDIA_TASK_INIT: btc_a2dp_source_thread_init(NULL); @@ -268,13 +276,13 @@ static void btc_a2dp_source_ctrl_handler(BtTaskEvt_t *e) btc_a2dp_source_aa_stop_tx(); break; case BTC_MEDIA_SBC_ENC_INIT: - btc_a2dp_source_enc_init(e->par); + btc_a2dp_source_enc_init(e->param); break; case BTC_MEDIA_SBC_ENC_UPDATE: - btc_a2dp_source_enc_update(e->par); + btc_a2dp_source_enc_update(e->param); break; case BTC_MEDIA_AUDIO_FEEDING_INIT: - btc_a2dp_source_audio_feeding_init(e->par); + btc_a2dp_source_audio_feeding_init(e->param); break; case BTC_MEDIA_FLUSH_AA_TX: btc_a2dp_source_aa_tx_flush(); @@ -282,29 +290,12 @@ static void btc_a2dp_source_ctrl_handler(BtTaskEvt_t *e) default: APPL_TRACE_WARNING("media task unhandled evt: 0x%x\n", e->sig); } - if (e->par != NULL) { - osi_free(e->par); - } -} -static void btc_a2dp_source_task_handler(void *arg) -{ - QueueSetMemberHandle_t xActivatedMember; - BtTaskEvt_t *e = NULL; - for (;;) { - xActivatedMember = xQueueSelectFromSet(btc_aa_src_queue_set, portMAX_DELAY); - if (xActivatedMember == btc_aa_src_data_queue) { - int32_t data_evt; - xQueueReceive(xActivatedMember, &data_evt, 0); - if (data_evt == BTC_A2DP_SOURCE_DATA_EVT) { - btc_a2dp_source_handle_timer(NULL); - } - } else if (xActivatedMember == btc_aa_src_ctrl_queue) { - xQueueReceive(xActivatedMember, &e, 0); - btc_a2dp_source_ctrl_handler(e); - osi_free(e); - } + if (e->param != NULL) { + osi_free(e->param); } + + osi_free(e); } bool btc_a2dp_source_startup(void) @@ -324,57 +315,28 @@ bool btc_a2dp_source_startup(void) } #endif /* #if BTC_SBC_ENC_DYNAMIC_MEMORY == TRUE */ - btc_aa_src_queue_set = xQueueCreateSet(BTC_A2DP_SOURCE_TASK_QUEUE_SET_LEN); - configASSERT(btc_aa_src_queue_set); - btc_aa_src_data_queue = xQueueCreate(BTC_A2DP_SOURCE_DATA_QUEUE_LEN, sizeof(void *)); - configASSERT(btc_aa_src_data_queue); - xQueueAddToSet(btc_aa_src_data_queue, btc_aa_src_queue_set); - - btc_aa_src_ctrl_queue = xQueueCreate(BTC_A2DP_SOURCE_CTRL_QUEUE_LEN, sizeof(void *)); - configASSERT(btc_aa_src_ctrl_queue); - xQueueAddToSet(btc_aa_src_ctrl_queue, btc_aa_src_queue_set); - - if (!btc_aa_src_data_queue || !btc_aa_src_ctrl_queue || !btc_aa_src_queue_set ) { - goto error_exit; - } - - xTaskCreatePinnedToCore(btc_a2dp_source_task_handler, BTC_A2DP_SOURCE_TASK_NAME, BTC_A2DP_SOURCE_TASK_STACK_SIZE, NULL, BTC_A2DP_SOURCE_TASK_PRIO, &btc_aa_src_task_hdl, BTC_A2DP_SOURCE_TASK_PINNED_TO_CORE); + btc_aa_src_task_hdl = osi_thread_create(BTC_A2DP_SOURCE_TASK_NAME, BTC_A2DP_SOURCE_TASK_STACK_SIZE, BTC_A2DP_SOURCE_TASK_PRIO, BTC_A2DP_SOURCE_TASK_PINNED_TO_CORE, 2); if (btc_aa_src_task_hdl == NULL) { goto error_exit; } btc_a2dp_source_ctrl_post(BTC_MEDIA_TASK_INIT, NULL); - APPL_TRACE_EVENT("## A2DP SOURCE MEDIA THREAD STARTED ##\n"); return true; error_exit:; APPL_TRACE_ERROR("%s unable to start up media thread\n", __func__); + osi_thread_free(btc_aa_src_task_hdl); + btc_aa_src_task_hdl = NULL; - if (btc_aa_src_task_hdl != NULL) { - vTaskDelete(btc_aa_src_task_hdl); - btc_aa_src_task_hdl = NULL; - } - - if (btc_aa_src_data_queue) { - vQueueDelete(btc_aa_src_data_queue); - btc_aa_src_data_queue = NULL; - } - if (btc_aa_src_ctrl_queue) { - vQueueDelete(btc_aa_src_ctrl_queue); - btc_aa_src_ctrl_queue = NULL; - } - if (btc_aa_src_queue_set) { - vQueueDelete(btc_aa_src_queue_set); - btc_aa_src_queue_set = NULL; - } #if (BTC_SBC_ENC_DYNAMIC_MEMORY == TRUE) if (btc_sbc_encoder_ptr) { osi_free(btc_sbc_encoder_ptr); btc_sbc_encoder_ptr = NULL; } #endif /* #if BTC_SBC_ENC_DYNAMIC_MEMORY == TRUE */ + return false; } @@ -390,18 +352,9 @@ void btc_a2dp_source_shutdown(void) future_await(btc_a2dp_source_future); btc_a2dp_source_future = NULL; - vTaskDelete(btc_aa_src_task_hdl); + osi_thread_free(btc_aa_src_task_hdl); btc_aa_src_task_hdl = NULL; - vQueueDelete(btc_aa_src_data_queue); - btc_aa_src_data_queue = NULL; - - vQueueDelete(btc_aa_src_ctrl_queue); - btc_aa_src_ctrl_queue = NULL; - - vQueueDelete(btc_aa_src_queue_set); - btc_aa_src_queue_set = NULL; - #if (BTC_SBC_ENC_DYNAMIC_MEMORY == TRUE) osi_free(btc_sbc_encoder_ptr); btc_sbc_encoder_ptr = NULL; @@ -472,11 +425,9 @@ void btc_a2dp_source_on_suspended(tBTA_AV_SUSPEND *p_av) btc_a2dp_source_stop_audio_req(); } -static void btc_a2dp_source_data_post(int32_t data_type) +static void btc_a2dp_source_data_post(void) { - if (xQueueSend(btc_aa_src_data_queue, &data_type, 0) != pdTRUE) { - APPL_TRACE_DEBUG("Media data Q filled\n"); - } + osi_thread_post(btc_aa_src_task_hdl, btc_a2dp_source_handle_timer, NULL, 1, OSI_THREAD_BLOCKING); } static UINT64 time_now_us() @@ -604,9 +555,13 @@ BOOLEAN btc_a2dp_source_stop_audio_req(void) * the "cleanup() -> btc_a2dp_stop_media_task()" processing during * the shutdown of the Bluetooth stack. */ +#if 0 if (btc_aa_src_ctrl_queue != NULL) { +#endif btc_a2dp_source_ctrl_post(BTC_MEDIA_STOP_AA_TX, NULL); +#if 0 } +#endif return TRUE; } @@ -696,9 +651,13 @@ BOOLEAN btc_a2dp_source_tx_flush_req(void) * the "cleanup() -> btc_a2dp_stop_media_task()" processing during * the shutdown of the Bluetooth stack. */ +#if 0 if (btc_aa_src_ctrl_queue != NULL) { +#endif btc_a2dp_source_ctrl_post(BTC_MEDIA_FLUSH_AA_TX, NULL); +#if 0 } +#endif return TRUE; } @@ -1515,7 +1474,7 @@ static void btc_a2dp_source_handle_timer(UNUSED_ATTR void *context) static void btc_a2dp_source_alarm_cb(UNUSED_ATTR void *context) { - btc_a2dp_source_data_post(BTC_A2DP_SOURCE_DATA_EVT); + btc_a2dp_source_data_post(); } /******************************************************************************* diff --git a/components/bt/bluedroid/common/include/common/bt_target.h b/components/bt/bluedroid/common/include/common/bt_target.h index 8b504e0010..3e9dac6131 100644 --- a/components/bt/bluedroid/common/include/common/bt_target.h +++ b/components/bt/bluedroid/common/include/common/bt_target.h @@ -39,6 +39,14 @@ #include "stack/dyn_mem.h" /* defines static and/or dynamic memory for components */ + +/* OS Configuration from User config (eg: sdkconfig) */ +#if CONFIG_BLUEDROID_PINNED_TO_CORE +#define TASK_PINNED_TO_CORE (CONFIG_BLUEDROID_PINNED_TO_CORE < portNUM_PROCESSORS ? CONFIG_BLUEDROID_PINNED_TO_CORE : tskNO_AFFINITY) +#else +#define TASK_PINNED_TO_CORE (0) +#endif + /****************************************************************************** ** ** Classic BT features diff --git a/components/bt/bluedroid/hci/hci_hal_h4.c b/components/bt/bluedroid/hci/hci_hal_h4.c index 89fba87b5d..4189aa2fe5 100644 --- a/components/bt/bluedroid/hci/hci_hal_h4.c +++ b/components/bt/bluedroid/hci/hci_hal_h4.c @@ -27,9 +27,17 @@ #include "esp_bt.h" #include "stack/hcimsgs.h" +#define HCI_H4_TASK_PINNED_TO_CORE (TASK_PINNED_TO_CORE) +#define HCI_H4_TASK_STACK_SIZE (2048 + BT_TASK_EXTRA_STACK_SIZE) +#define HCI_H4_TASK_PRIO (configMAX_PRIORITIES - 4) +#define HCI_H4_TASK_NAME "hciH4T" +#define HCI_H4_QUEUE_LEN 1 + + #if (C2H_FLOW_CONTROL_INCLUDED == TRUE) #include "l2c_int.h" #endif ///C2H_FLOW_CONTROL_INCLUDED == TRUE +#include "stack/hcimsgs.h" #define HCI_HAL_SERIAL_BUFFER_SIZE 1026 #define HCI_BLE_EVENT 0x3e @@ -63,9 +71,7 @@ static hci_hal_env_t hci_hal_env; static const hci_hal_t interface; static const hci_hal_callbacks_t *callbacks; static const esp_vhci_host_callback_t vhci_host_cb; - -static xTaskHandle xHciH4TaskHandle; -static xQueueHandle xHciH4Queue; +static osi_thread_t *hci_h4_thread; static void host_send_pkt_available_cb(void); static int host_recv_pkt_cb(uint8_t *data, uint16_t len); @@ -110,8 +116,10 @@ static bool hal_open(const hci_hal_callbacks_t *upper_callbacks) hci_hal_env_init(HCI_HAL_SERIAL_BUFFER_SIZE, QUEUE_SIZE_MAX); #endif - xHciH4Queue = xQueueCreate(HCI_H4_QUEUE_LEN, sizeof(BtTaskEvt_t)); - xTaskCreatePinnedToCore(hci_hal_h4_rx_handler, HCI_H4_TASK_NAME, HCI_H4_TASK_STACK_SIZE, NULL, HCI_H4_TASK_PRIO, &xHciH4TaskHandle, HCI_H4_TASK_PINNED_TO_CORE); + hci_h4_thread = osi_thread_create(HCI_H4_TASK_NAME, HCI_H4_TASK_STACK_SIZE, HCI_H4_TASK_PRIO, HCI_H4_TASK_PINNED_TO_CORE, 1); + if (hci_h4_thread == NULL) { + return false; + } //register vhci host cb if (esp_vhci_host_register_callback(&vhci_host_cb) != ESP_OK) { @@ -125,9 +133,8 @@ static void hal_close() { hci_hal_env_deinit(); - /* delete task and queue */ - vTaskDelete(xHciH4TaskHandle); - vQueueDelete(xHciH4Queue); + osi_thread_free(hci_h4_thread); + hci_h4_thread = NULL; } /** @@ -169,30 +176,12 @@ static uint16_t transmit_data(serial_data_type_t type, // Internal functions static void hci_hal_h4_rx_handler(void *arg) { - BtTaskEvt_t e; - - for (;;) { - if (pdTRUE == xQueueReceive(xHciH4Queue, &e, (portTickType)portMAX_DELAY)) { - if (e.sig == SIG_HCI_HAL_RECV_PACKET) { - fixed_queue_process(hci_hal_env.rx_q); - - } - } - } + fixed_queue_process(hci_hal_env.rx_q); } -task_post_status_t hci_hal_h4_task_post(task_post_t timeout) +bool hci_hal_h4_task_post(osi_thread_blocking_t blocking) { - BtTaskEvt_t evt; - - evt.sig = SIG_HCI_HAL_RECV_PACKET; - evt.par = 0; - - if (xQueueSend(xHciH4Queue, &evt, timeout) != pdTRUE) { - return TASK_POST_SUCCESS; - } - - return TASK_POST_FAIL; + return osi_thread_post(hci_h4_thread, hci_hal_h4_rx_handler, NULL, 0, blocking); } #if (C2H_FLOW_CONTROL_INCLUDED == TRUE) @@ -343,7 +332,7 @@ static void host_send_pkt_available_cb(void) { //Controller rx cache buffer is ready for receiving new host packet //Just Call Host main thread task to process pending packets. - hci_host_task_post(TASK_POST_BLOCKING); + hci_host_task_post(OSI_THREAD_BLOCKING); } static int host_recv_pkt_cb(uint8_t *data, uint16_t len) @@ -368,7 +357,8 @@ static int host_recv_pkt_cb(uint8_t *data, uint16_t len) pkt->layer_specific = 0; memcpy(pkt->data, data, len); fixed_queue_enqueue(hci_hal_env.rx_q, pkt); - hci_hal_h4_task_post(0); + hci_hal_h4_task_post(OSI_THREAD_NON_BLOCKING); + BTTRC_DUMP_BUFFER("Recv Pkt", pkt->data, len); diff --git a/components/bt/bluedroid/hci/hci_layer.c b/components/bt/bluedroid/hci/hci_layer.c index c2b6223ce7..67ed0a2bcc 100644 --- a/components/bt/bluedroid/hci/hci_layer.c +++ b/components/bt/bluedroid/hci/hci_layer.c @@ -21,6 +21,7 @@ #include "common/bt_trace.h" #include "stack/hcidefs.h" #include "stack/hcimsgs.h" +#include "stack/btu.h" #include "common/bt_vendor_lib.h" #include "hci/hci_internals.h" #include "hci/hci_hal.h" @@ -33,6 +34,12 @@ #include "osi/mutex.h" #include "osi/fixed_queue.h" +#define HCI_HOST_TASK_PINNED_TO_CORE (TASK_PINNED_TO_CORE) +#define HCI_HOST_TASK_STACK_SIZE (2048 + BT_TASK_EXTRA_STACK_SIZE) +#define HCI_HOST_TASK_PRIO (configMAX_PRIORITIES - 3) +#define HCI_HOST_TASK_NAME "hciHostT" +#define HCI_HOST_QUEUE_LEN 40 + typedef struct { uint16_t opcode; future_t *complete_future; @@ -70,10 +77,7 @@ static const uint32_t COMMAND_PENDING_TIMEOUT = 8000; static bool interface_created; static hci_t interface; static hci_host_env_t hci_host_env; - -static xTaskHandle xHciHostTaskHandle; -static xQueueHandle xHciHostQueue; - +static osi_thread_t *hci_host_thread; static bool hci_host_startup_flag; // Modules we import and callbacks we export @@ -102,8 +106,10 @@ int hci_start_up(void) goto error; } - xHciHostQueue = xQueueCreate(HCI_HOST_QUEUE_LEN, sizeof(BtTaskEvt_t)); - xTaskCreatePinnedToCore(hci_host_thread_handler, HCI_HOST_TASK_NAME, HCI_HOST_TASK_STACK_SIZE, NULL, HCI_HOST_TASK_PRIO, &xHciHostTaskHandle, HCI_HOST_TASK_PINNED_TO_CORE); + hci_host_thread = osi_thread_create(HCI_HOST_TASK_NAME, HCI_HOST_TASK_STACK_SIZE, HCI_HOST_TASK_PRIO, HCI_HOST_TASK_PINNED_TO_CORE, 1); + if (hci_host_thread == NULL) { + return -2; + } packet_fragmenter->init(&packet_fragmenter_callbacks); hal->open(&hal_callbacks); @@ -124,28 +130,15 @@ void hci_shut_down(void) //low_power_manager->cleanup(); hal->close(); - vTaskDelete(xHciHostTaskHandle); - vQueueDelete(xHciHostQueue); + + osi_thread_free(hci_host_thread); + hci_host_thread = NULL; } -task_post_status_t hci_host_task_post(task_post_t timeout) +bool hci_host_task_post(osi_thread_blocking_t blocking) { - BtTaskEvt_t evt; - - if (hci_host_startup_flag == false) { - return TASK_POST_FAIL; - } - - evt.sig = SIG_HCI_HOST_SEND_AVAILABLE; - evt.par = 0; - - if (xQueueSend(xHciHostQueue, &evt, timeout) != pdTRUE) { - HCI_TRACE_ERROR("xHciHostQueue failed\n"); - return TASK_POST_FAIL; - } - - return TASK_POST_SUCCESS; + return osi_thread_post(hci_host_thread, hci_host_thread_handler, NULL, 0, blocking); } static int hci_layer_init_env(void) @@ -218,27 +211,17 @@ static void hci_host_thread_handler(void *arg) * All packets will be directly copied to single queue in driver layer with * H4 type header added (1 byte). */ - - BtTaskEvt_t e; - - for (;;) { - if (pdTRUE == xQueueReceive(xHciHostQueue, &e, (portTickType)portMAX_DELAY)) { - - if (e.sig == SIG_HCI_HOST_SEND_AVAILABLE) { - if (esp_vhci_host_check_send_available()) { - /*Now Target only allowed one packet per TX*/ - BT_HDR *pkt = packet_fragmenter->fragment_current_packet(); - if (pkt != NULL) { - packet_fragmenter->fragment_and_dispatch(pkt); - } else { - if (!fixed_queue_is_empty(hci_host_env.command_queue) && - hci_host_env.command_credits > 0) { - fixed_queue_process(hci_host_env.command_queue); - } else if (!fixed_queue_is_empty(hci_host_env.packet_queue)) { - fixed_queue_process(hci_host_env.packet_queue); - } - } - } + if (esp_vhci_host_check_send_available()) { + /*Now Target only allowed one packet per TX*/ + BT_HDR *pkt = packet_fragmenter->fragment_current_packet(); + if (pkt != NULL) { + packet_fragmenter->fragment_and_dispatch(pkt); + } else { + if (!fixed_queue_is_empty(hci_host_env.command_queue) && + hci_host_env.command_credits > 0) { + fixed_queue_process(hci_host_env.command_queue); + } else if (!fixed_queue_is_empty(hci_host_env.packet_queue)) { + fixed_queue_process(hci_host_env.packet_queue); } } } @@ -271,7 +254,7 @@ static void transmit_command( BTTRC_DUMP_BUFFER(NULL, command->data + command->offset, command->len); fixed_queue_enqueue(hci_host_env.command_queue, wait_entry); - hci_host_task_post(TASK_POST_BLOCKING); + hci_host_task_post(OSI_THREAD_BLOCKING); } @@ -292,7 +275,7 @@ static future_t *transmit_command_futured(BT_HDR *command) command->event = MSG_STACK_TO_HC_HCI_CMD; fixed_queue_enqueue(hci_host_env.command_queue, wait_entry); - hci_host_task_post(TASK_POST_BLOCKING); + hci_host_task_post(OSI_THREAD_BLOCKING); return future; } @@ -305,7 +288,7 @@ static void transmit_downward(uint16_t type, void *data) fixed_queue_enqueue(hci_host_env.packet_queue, data); } - hci_host_task_post(TASK_POST_BLOCKING); + hci_host_task_post(OSI_THREAD_BLOCKING); } @@ -479,7 +462,7 @@ intercepted: /*Tell HCI Host Task to continue TX Pending commands*/ if (hci_host_env.command_credits && !fixed_queue_is_empty(hci_host_env.command_queue)) { - hci_host_task_post(TASK_POST_BLOCKING); + hci_host_task_post(OSI_THREAD_BLOCKING); } if (wait_entry) { @@ -507,7 +490,7 @@ static void dispatch_reassembled(BT_HDR *packet) { // Events should already have been dispatched before this point //Tell Up-layer received packet. - if (btu_task_post(SIG_BTU_HCI_MSG, packet, TASK_POST_BLOCKING) != TASK_POST_SUCCESS) { + if (btu_task_post(SIG_BTU_HCI_MSG, packet, OSI_THREAD_BLOCKING) == false) { osi_free(packet); } } diff --git a/components/bt/bluedroid/hci/include/hci/hci_layer.h b/components/bt/bluedroid/hci/include/hci/hci_layer.h index 4b1018098b..90f0a8221e 100644 --- a/components/bt/bluedroid/hci/include/hci/hci_layer.h +++ b/components/bt/bluedroid/hci/include/hci/hci_layer.h @@ -23,6 +23,8 @@ #include "osi/allocator.h" #include "osi/osi.h" #include "osi/future.h" +#include "osi/thread.h" + ///// LEGACY DEFINITIONS ///// /* Message event mask across Host/Controller lib and stack */ @@ -95,5 +97,6 @@ const hci_t *hci_layer_get_interface(); int hci_start_up(void); void hci_shut_down(void); +bool hci_host_task_post(osi_thread_blocking_t blocking); #endif /* _HCI_LAYER_H_ */ diff --git a/components/bt/bluedroid/osi/include/osi/thread.h b/components/bt/bluedroid/osi/include/osi/thread.h index bad1e48809..b3f77725f9 100644 --- a/components/bt/bluedroid/osi/include/osi/thread.h +++ b/components/bt/bluedroid/osi/include/osi/thread.h @@ -25,91 +25,33 @@ #define portBASE_TYPE int -struct bt_task_evt { - uint32_t sig; //task sig - void *par; //point to task param - void *cb; //point to function cb - void *arg; //point to function arg -}; -typedef struct bt_task_evt BtTaskEvt_t; +struct osi_thread; -typedef bt_status_t (* BtTaskCb_t)(void *arg); +typedef struct osi_thread osi_thread_t; + +typedef void (*osi_thread_func_t)(void *context); typedef enum { - SIG_HCI_HAL_RECV_PACKET = 0, - SIG_HCI_HAL_NUM, -} SIG_HCI_HAL_t; - + OSI_THREAD_CORE_0 = 0, + OSI_THREAD_CORE_1, + OSI_THREAD_CORE_AFFINITY, +} osi_thread_core_t; typedef enum { - SIG_HCI_HOST_SEND_AVAILABLE = 0, - SIG_HCI_HOST_NUM, -} SIG_HCI_HOST_t; + OSI_THREAD_NON_BLOCKING = 0, + OSI_THREAD_BLOCKING, +} osi_thread_blocking_t; -typedef enum { - SIG_BTU_START_UP = 0, - SIG_BTU_HCI_MSG, - SIG_BTU_BTA_MSG, - SIG_BTU_BTA_ALARM, - SIG_BTU_GENERAL_ALARM, - SIG_BTU_ONESHOT_ALARM, - SIG_BTU_L2CAP_ALARM, - SIG_BTU_NUM, -} SIG_BTU_t; +osi_thread_t *osi_thread_create(const char *name, size_t stack_size, int priority, osi_thread_core_t core, uint8_t work_queue_num); -#define TASK_PINNED_TO_CORE (CONFIG_BT_BLUEDROID_PINNED_TO_CORE < portNUM_PROCESSORS ? CONFIG_BT_BLUEDROID_PINNED_TO_CORE : tskNO_AFFINITY) +void osi_thread_free(osi_thread_t *thread); -#define HCI_HOST_TASK_PINNED_TO_CORE (TASK_PINNED_TO_CORE) -#define HCI_HOST_TASK_STACK_SIZE (2048 + BT_TASK_EXTRA_STACK_SIZE) -#define HCI_HOST_TASK_PRIO (configMAX_PRIORITIES - 3) -#define HCI_HOST_TASK_NAME "hciHostT" -#define HCI_HOST_QUEUE_LEN 40 +bool osi_thread_post(osi_thread_t *thread, osi_thread_func_t func, void *context, int queue_idx, osi_thread_blocking_t blocking); -#define HCI_H4_TASK_PINNED_TO_CORE (TASK_PINNED_TO_CORE) -#define HCI_H4_TASK_STACK_SIZE (2048 + BT_TASK_EXTRA_STACK_SIZE) -#define HCI_H4_TASK_PRIO (configMAX_PRIORITIES - 4) -#define HCI_H4_TASK_NAME "hciH4T" -#define HCI_H4_QUEUE_LEN 1 +bool osi_thread_set_priority(osi_thread_t *thread, int priority); -#define BTU_TASK_PINNED_TO_CORE (TASK_PINNED_TO_CORE) -#define BTU_TASK_STACK_SIZE (CONFIG_BT_BTU_TASK_STACK_SIZE + BT_TASK_EXTRA_STACK_SIZE) -#define BTU_TASK_PRIO (configMAX_PRIORITIES - 5) -#define BTU_TASK_NAME "btuT" -#define BTU_QUEUE_LEN 50 +const char *osi_thread_name(osi_thread_t *thread); -#define BTC_TASK_PINNED_TO_CORE (TASK_PINNED_TO_CORE) -#define BTC_TASK_STACK_SIZE (CONFIG_BT_BTC_TASK_STACK_SIZE + BT_TASK_EXTRA_STACK_SIZE) //by menuconfig -#define BTC_TASK_NAME "btcT" -#define BTC_TASK_PRIO (configMAX_PRIORITIES - 6) -#define BTC_TASK_QUEUE_LEN 60 - -#define BTC_A2DP_SINK_TASK_PINNED_TO_CORE (TASK_PINNED_TO_CORE) -#define BTC_A2DP_SINK_TASK_STACK_SIZE (CONFIG_BT_A2DP_SINK_TASK_STACK_SIZE + BT_TASK_EXTRA_STACK_SIZE) // by menuconfig -#define BTC_A2DP_SINK_TASK_NAME "BtA2dSinkT" -#define BTC_A2DP_SINK_TASK_PRIO (configMAX_PRIORITIES - 3) -#define BTC_A2DP_SINK_DATA_QUEUE_LEN (3) -#define BTC_A2DP_SINK_CTRL_QUEUE_LEN (5) -#define BTC_A2DP_SINK_TASK_QUEUE_SET_LEN (BTC_A2DP_SINK_DATA_QUEUE_LEN + BTC_A2DP_SINK_CTRL_QUEUE_LEN) - -#define BTC_A2DP_SOURCE_TASK_PINNED_TO_CORE (TASK_PINNED_TO_CORE) -#define BTC_A2DP_SOURCE_TASK_STACK_SIZE (CONFIG_BT_A2DP_SOURCE_TASK_STACK_SIZE + BT_TASK_EXTRA_STACK_SIZE) // by menuconfig -#define BTC_A2DP_SOURCE_TASK_NAME "BtA2dSourceT" -#define BTC_A2DP_SOURCE_TASK_PRIO (configMAX_PRIORITIES - 3) -#define BTC_A2DP_SOURCE_DATA_QUEUE_LEN (1) -#define BTC_A2DP_SOURCE_CTRL_QUEUE_LEN (5) -#define BTC_A2DP_SOURCE_TASK_QUEUE_SET_LEN (BTC_A2DP_SOURCE_DATA_QUEUE_LEN + BTC_A2DP_SOURCE_CTRL_QUEUE_LEN) - -#define TASK_POST_NON_BLOCKING (0) -#define TASK_POST_BLOCKING (portMAX_DELAY) -typedef uint32_t task_post_t; /* Timeout of task post return, unit TICK */ - -typedef enum { - TASK_POST_SUCCESS = 0, - TASK_POST_FAIL, -} task_post_status_t; - -task_post_status_t btu_task_post(uint32_t sig, void *param, task_post_t timeout); -task_post_status_t hci_host_task_post(task_post_t timeout); -task_post_status_t hci_hal_h4_task_post(task_post_t timeout); +int osi_thread_queue_wait_size(osi_thread_t *thread, int wq_idx); #endif /* __THREAD_H__ */ diff --git a/components/bt/bluedroid/osi/thread.c b/components/bt/bluedroid/osi/thread.c new file mode 100644 index 0000000000..ceac21bf89 --- /dev/null +++ b/components/bt/bluedroid/osi/thread.c @@ -0,0 +1,282 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Google, Inc. + * + * 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 "osi/allocator.h" +#include "osi/fixed_queue.h" +#include "osi/semaphore.h" +#include "osi/thread.h" + +struct osi_thread { + void *thread_handle; /*!< Store the thread object */ + int thread_id; /*!< May for some OS, such as Linux */ + bool stop; + uint8_t work_queue_num; /*!< Work queue number */ + fixed_queue_t **work_queues; /*!< Point to queue array, and the priority inverse array index */ + osi_sem_t work_sem; + osi_sem_t stop_sem; +}; + +struct osi_thread_start_arg { + osi_thread_t *thread; + osi_sem_t start_sem; + int error; +}; + +typedef struct { + osi_thread_func_t func; + void *context; +} work_item_t; + +static const size_t DEFAULT_WORK_QUEUE_CAPACITY = 100; + +static void osi_thread_run(void *arg) +{ + struct osi_thread_start_arg *start = (struct osi_thread_start_arg *)arg; + osi_thread_t *thread = start->thread; + + osi_sem_give(&start->start_sem); + + while (1) { + int idx = 0; + + osi_sem_take(&thread->work_sem, OSI_SEM_MAX_TIMEOUT); + + if (thread->stop) { + break; + } + + while (!thread->stop && idx < thread->work_queue_num) { + work_item_t *item = fixed_queue_try_dequeue(thread->work_queues[idx]); + if (item) { + item->func(item->context); + osi_free(item); + idx = 0; + continue; + } else { + idx++; + } + } + } + + thread->thread_handle = NULL; + osi_sem_give(&thread->stop_sem); + + vTaskDelete(NULL); +} + +static int osi_thread_join(osi_thread_t *thread, uint32_t wait_ms) +{ + assert(thread != NULL); + return osi_sem_take(&thread->stop_sem, wait_ms); +} + +static void osi_thread_stop(osi_thread_t *thread) +{ + int ret; + + assert(thread != NULL); + + //stop the thread + thread->stop = true; + osi_sem_give(&thread->work_sem); + + //join + ret = osi_thread_join(thread, 1000); //wait 1000ms + + //if join failed, delete the task here + if (ret != 0 && thread->thread_handle) { + vTaskDelete(thread->thread_handle); + } +} + +//in linux, the stack_size, priority and core may not be set here, the code will be ignore the arguments +osi_thread_t *osi_thread_create(const char *name, size_t stack_size, int priority, osi_thread_core_t core, uint8_t work_queue_num) +{ + int ret; + osi_thread_t *thread; + struct osi_thread_start_arg start_arg = {0}; + + if (stack_size <= 0 || + core < OSI_THREAD_CORE_0 || core > OSI_THREAD_CORE_AFFINITY || + work_queue_num <= 0) { + return NULL; + } + + thread = (osi_thread_t *)osi_malloc(sizeof(osi_thread_t)); + if (thread == NULL) { + goto _err; + } + + thread->stop = false; + thread->work_queue_num = work_queue_num; + thread->work_queues = (fixed_queue_t **)osi_malloc(sizeof(fixed_queue_t *) * work_queue_num); + if (thread->work_queues == NULL) { + goto _err; + } + + for (int i = 0; i < thread->work_queue_num; i++) { + thread->work_queues[i] = fixed_queue_new(DEFAULT_WORK_QUEUE_CAPACITY); + if (thread->work_queues[i] == NULL) { + goto _err; + } + } + + ret = osi_sem_new(&thread->work_sem, 1, 0); + if (ret != 0) { + goto _err; + } + + ret = osi_sem_new(&thread->stop_sem, 1, 0); + if (ret != 0) { + goto _err; + } + + start_arg.thread = thread; + ret = osi_sem_new(&start_arg.start_sem, 1, 0); + if (ret != 0) { + goto _err; + } + + if (xTaskCreatePinnedToCore(osi_thread_run, name, stack_size, &start_arg, priority, &thread->thread_handle, core) != pdPASS) { + goto _err; + } + + osi_sem_take(&start_arg.start_sem, OSI_SEM_MAX_TIMEOUT); + osi_sem_free(&start_arg.start_sem); + + return thread; + +_err: + + if (thread) { + if (start_arg.start_sem) { + osi_sem_free(&start_arg.start_sem); + } + + if (thread->thread_handle) { + vTaskDelete(thread->thread_handle); + } + + for (int i = 0; i < thread->work_queue_num; i++) { + if (thread->work_queues[i]) { + fixed_queue_free(thread->work_queues[i], osi_free_func); + } + } + + if (thread->work_queues) { + osi_free(thread->work_queues); + } + + if (thread->work_sem) { + osi_sem_free(&thread->work_sem); + } + + if (thread->stop_sem) { + osi_sem_free(&thread->stop_sem); + } + + osi_free(thread); + } + + return NULL; +} + +void osi_thread_free(osi_thread_t *thread) +{ + if (!thread) + return; + + osi_thread_stop(thread); + + for (int i = 0; i < thread->work_queue_num; i++) { + if (thread->work_queues[i]) { + fixed_queue_free(thread->work_queues[i], osi_free_func); + } + } + + if (thread->work_queues) { + osi_free(thread->work_queues); + } + + if (thread->work_sem) { + osi_sem_free(&thread->work_sem); + } + + if (thread->stop_sem) { + osi_sem_free(&thread->stop_sem); + } + + + osi_free(thread); +} + +bool osi_thread_post(osi_thread_t *thread, osi_thread_func_t func, void *context, int queue_idx, osi_thread_blocking_t blocking) +{ + assert(thread != NULL); + assert(func != NULL); + + if (queue_idx >= thread->work_queue_num) { + return false; + } + + work_item_t *item = (work_item_t *)osi_malloc(sizeof(work_item_t)); + if (item == NULL) { + return false; + } + item->func = func; + item->context = context; + + if (blocking == OSI_THREAD_BLOCKING) { + fixed_queue_enqueue(thread->work_queues[queue_idx], item); + } else { + if (fixed_queue_try_enqueue(thread->work_queues[queue_idx], item) == false) { + osi_free(item); + return false; + } + } + + osi_sem_give(&thread->work_sem); + + return true; +} + +bool osi_thread_set_priority(osi_thread_t *thread, int priority) +{ + assert(thread != NULL); + + vTaskPrioritySet(thread->thread_handle, priority); + return true; +} + +const char *osi_thread_name(osi_thread_t *thread) +{ + assert(thread != NULL); + + return pcTaskGetTaskName(thread->thread_handle); +} + +int osi_thread_queue_wait_size(osi_thread_t *thread, int wq_idx) +{ + if (wq_idx < 0 || wq_idx >= thread->work_queue_num) { + return -1; + } + + return fixed_queue_length(thread->work_queues[wq_idx]); +} diff --git a/components/bt/bluedroid/stack/btu/btu_hcif.c b/components/bt/bluedroid/stack/btu/btu_hcif.c index 7f0395a5b3..7d1ab95f96 100644 --- a/components/bt/bluedroid/stack/btu/btu_hcif.c +++ b/components/bt/bluedroid/stack/btu/btu_hcif.c @@ -1086,7 +1086,7 @@ static void btu_hcif_command_complete_evt(BT_HDR *response, void *context) event->event = BTU_POST_TO_TASK_NO_GOOD_HORRIBLE_HACK; - btu_task_post(SIG_BTU_HCI_MSG, event, TASK_POST_BLOCKING); + btu_task_post(SIG_BTU_HCI_MSG, event, OSI_THREAD_BLOCKING); } @@ -1291,7 +1291,7 @@ static void btu_hcif_command_status_evt(uint8_t status, BT_HDR *command, void *c event->event = BTU_POST_TO_TASK_NO_GOOD_HORRIBLE_HACK; - btu_task_post(SIG_BTU_HCI_MSG, event, TASK_POST_BLOCKING); + btu_task_post(SIG_BTU_HCI_MSG, event, OSI_THREAD_BLOCKING); } /******************************************************************************* diff --git a/components/bt/bluedroid/stack/btu/btu_init.c b/components/bt/bluedroid/stack/btu/btu_init.c index 7014cfde00..510fab7ecf 100644 --- a/components/bt/bluedroid/stack/btu/btu_init.c +++ b/components/bt/bluedroid/stack/btu/btu_init.c @@ -44,6 +44,12 @@ #endif #endif +#define BTU_TASK_PINNED_TO_CORE (TASK_PINNED_TO_CORE) +#define BTU_TASK_STACK_SIZE (4096 + BT_TASK_EXTRA_STACK_SIZE) +#define BTU_TASK_PRIO (configMAX_PRIORITIES - 5) +#define BTU_TASK_NAME "btuT" +#define BTU_QUEUE_LEN 50 + hash_map_t *btu_general_alarm_hash_map; osi_mutex_t btu_general_alarm_lock; static const size_t BTU_GENERAL_ALARM_HASH_MAP_SIZE = 34; @@ -56,16 +62,14 @@ hash_map_t *btu_l2cap_alarm_hash_map; osi_mutex_t btu_l2cap_alarm_lock; static const size_t BTU_L2CAP_ALARM_HASH_MAP_SIZE = 34; -//thread_t *bt_workqueue_thread; -//static const char *BT_WORKQUEUE_NAME = "bt_workqueue"; -xTaskHandle xBtuTaskHandle = NULL; -xQueueHandle xBtuQueue = 0; +osi_thread_t *btu_thread = NULL; extern void PLATFORM_DisableHciTransport(UINT8 bDisable); extern void btu_task_thread_handler(void *arg); void btu_task_start_up(void); void btu_task_shut_down(void); + /***************************************************************************** ** V A R I A B L E S * ******************************************************************************/ @@ -178,10 +182,14 @@ void BTU_StartUp(void) osi_mutex_new(&btu_l2cap_alarm_lock); - xBtuQueue = xQueueCreate(BTU_QUEUE_LEN, sizeof(BtTaskEvt_t)); - xTaskCreatePinnedToCore(btu_task_thread_handler, BTU_TASK_NAME, BTU_TASK_STACK_SIZE, NULL, BTU_TASK_PRIO, &xBtuTaskHandle, BTU_TASK_PINNED_TO_CORE); + btu_thread = osi_thread_create(BTU_TASK_NAME, BTU_TASK_STACK_SIZE, BTU_TASK_PRIO, BTU_TASK_PINNED_TO_CORE, 1); + if (btu_thread == NULL) { + goto error_exit; + } - btu_task_post(SIG_BTU_START_UP, NULL, TASK_POST_BLOCKING); + if (btu_task_post(SIG_BTU_START_UP, NULL, OSI_THREAD_BLOCKING) == false) { + goto error_exit; + } return; @@ -206,17 +214,14 @@ void BTU_ShutDown(void) hash_map_free(btu_l2cap_alarm_hash_map); osi_mutex_free(&btu_l2cap_alarm_lock); - vTaskDelete(xBtuTaskHandle); - vQueueDelete(xBtuQueue); - - btu_general_alarm_hash_map = NULL; + if (btu_thread) { + osi_thread_free(btu_thread); + btu_thread = NULL; + } + btu_general_alarm_hash_map = NULL; btu_oneshot_alarm_hash_map = NULL; - btu_l2cap_alarm_hash_map = NULL; - - xBtuTaskHandle = NULL; - xBtuQueue = 0; } /***************************************************************************** @@ -236,13 +241,14 @@ UINT16 BTU_BleAclPktSize(void) return 0; #endif } + #if SCAN_QUEUE_CONGEST_CHECK bool BTU_check_queue_is_congest(void) { - UBaseType_t wait_size = uxQueueMessagesWaiting(xBtuQueue); - if(wait_size >= QUEUE_CONGEST_SIZE ) { + if (osi_thread_queue_wait_size(btu_thread, 0) >= QUEUE_CONGEST_SIZE) { return true; } + return false; } #endif diff --git a/components/bt/bluedroid/stack/btu/btu_task.c b/components/bt/bluedroid/stack/btu/btu_task.c index 644731504d..916811df1e 100644 --- a/components/bt/bluedroid/stack/btu/btu_task.c +++ b/components/bt/bluedroid/stack/btu/btu_task.c @@ -81,6 +81,11 @@ extern void avdt_rcv_sync_info (BT_HDR *p_buf); #include "btm_ble_int.h" #endif +typedef struct { + uint32_t sig; + void *param; +} btu_thread_evt_t; + //#if (defined(BT_APP_DEMO) && BT_APP_DEMO == TRUE) //#include "bt_app_common.h" //#endif @@ -107,8 +112,8 @@ extern osi_mutex_t btu_oneshot_alarm_lock; extern hash_map_t *btu_l2cap_alarm_hash_map; extern osi_mutex_t btu_l2cap_alarm_lock; -extern xTaskHandle xBtuTaskHandle; -extern xQueueHandle xBtuQueue; +extern void *btu_thread; + extern bluedroid_init_done_cb_t bluedroid_init_done_cb; /* Define a function prototype to allow a generic timeout handler */ @@ -208,66 +213,52 @@ static void btu_bta_alarm_process(TIMER_LIST_ENT *p_tle) } #endif -/***************************************************************************** -** -** Function btu_task_thread_handler -** -** Description Process BTU Task Thread. -******************************************************************************/ -void btu_task_thread_handler(void *arg) +void btu_thread_handler(void *arg) { - BtTaskEvt_t e; + btu_thread_evt_t *evt = (btu_thread_evt_t *)arg; - for (;;) { - if (pdTRUE == xQueueReceive(xBtuQueue, &e, (portTickType)portMAX_DELAY)) { - - switch (e.sig) { - case SIG_BTU_START_UP: - btu_task_start_up(); - break; - case SIG_BTU_HCI_MSG: - btu_hci_msg_process((BT_HDR *)e.par); - break; + switch (evt->sig) { + case SIG_BTU_START_UP: + btu_task_start_up(); + break; + case SIG_BTU_HCI_MSG: + btu_hci_msg_process((BT_HDR *)evt->param); + break; #if (defined(BTA_INCLUDED) && BTA_INCLUDED == TRUE) - case SIG_BTU_BTA_MSG: - bta_sys_event((BT_HDR *)e.par); - break; - case SIG_BTU_BTA_ALARM: - btu_bta_alarm_process((TIMER_LIST_ENT *)e.par); - break; + case SIG_BTU_BTA_MSG: + bta_sys_event((BT_HDR *)evt->param); + break; + case SIG_BTU_BTA_ALARM: + btu_bta_alarm_process((TIMER_LIST_ENT *)evt->param); + break; #endif - case SIG_BTU_GENERAL_ALARM: - btu_general_alarm_process((TIMER_LIST_ENT *)e.par); - break; - case SIG_BTU_ONESHOT_ALARM: { - TIMER_LIST_ENT *p_tle = (TIMER_LIST_ENT *)e.par; - btu_general_alarm_process(p_tle); - break; - } - case SIG_BTU_L2CAP_ALARM: - btu_l2cap_alarm_process((TIMER_LIST_ENT *)e.par); - break; - default: - break; - } - } + case SIG_BTU_GENERAL_ALARM: + case SIG_BTU_ONESHOT_ALARM: + btu_general_alarm_process((TIMER_LIST_ENT *)evt->param); + break; + case SIG_BTU_L2CAP_ALARM: + btu_l2cap_alarm_process((TIMER_LIST_ENT *)evt->param); + break; + default: + break; } + + osi_free(evt); } - -task_post_status_t btu_task_post(uint32_t sig, void *param, task_post_t timeout) +bool btu_task_post(uint32_t sig, void *param, osi_thread_blocking_t blocking) { - BtTaskEvt_t evt; + btu_thread_evt_t *evt; - evt.sig = sig; - evt.par = param; - - if (xQueueSend(xBtuQueue, &evt, timeout) != pdTRUE) { - HCI_TRACE_ERROR("xBtuQueue failed\n"); - return TASK_POST_FAIL; + evt = (btu_thread_evt_t *)osi_malloc(sizeof(btu_thread_evt_t)); + if (evt == NULL) { + return false; } - return TASK_POST_SUCCESS; + evt->sig = sig; + evt->param = param; + + return osi_thread_post(btu_thread, btu_thread_handler, evt, 0, blocking); } void btu_task_start_up(void) @@ -426,7 +417,7 @@ void btu_general_alarm_cb(void *data) assert(data != NULL); TIMER_LIST_ENT *p_tle = (TIMER_LIST_ENT *)data; - btu_task_post(SIG_BTU_GENERAL_ALARM, p_tle, TASK_POST_BLOCKING); + btu_task_post(SIG_BTU_GENERAL_ALARM, p_tle, OSI_THREAD_BLOCKING); } void btu_start_timer(TIMER_LIST_ENT *p_tle, UINT16 type, UINT32 timeout_sec) @@ -540,7 +531,7 @@ static void btu_l2cap_alarm_cb(void *data) assert(data != NULL); TIMER_LIST_ENT *p_tle = (TIMER_LIST_ENT *)data; - btu_task_post(SIG_BTU_L2CAP_ALARM, p_tle, TASK_POST_BLOCKING); + btu_task_post(SIG_BTU_L2CAP_ALARM, p_tle, OSI_THREAD_BLOCKING); } void btu_start_quick_timer(TIMER_LIST_ENT *p_tle, UINT16 type, UINT32 timeout_ticks) @@ -623,7 +614,7 @@ void btu_oneshot_alarm_cb(void *data) btu_stop_timer_oneshot(p_tle); - btu_task_post(SIG_BTU_ONESHOT_ALARM, p_tle, TASK_POST_BLOCKING); + btu_task_post(SIG_BTU_ONESHOT_ALARM, p_tle, OSI_THREAD_BLOCKING); } /* diff --git a/components/bt/bluedroid/stack/include/stack/btu.h b/components/bt/bluedroid/stack/include/stack/btu.h index 449b18da7e..a038ded1e1 100644 --- a/components/bt/bluedroid/stack/include/stack/btu.h +++ b/components/bt/bluedroid/stack/include/stack/btu.h @@ -29,6 +29,7 @@ #include "common/bt_target.h" #include "common/bt_defs.h" +#include "osi/thread.h" // HACK(zachoverflow): temporary dark magic #define BTU_POST_TO_TASK_NO_GOOD_HORRIBLE_HACK 0x1700 // didn't look used in bt_types...here goes nothing @@ -163,6 +164,17 @@ typedef void (*tBTU_EVENT_CALLBACK)(BT_HDR *p_hdr); #define BTU_TTYPE_UCD_TO 108 #define BTU_TTYPE_BLE_SCAN 109 +/* BTU Task Signal */ +typedef enum { + SIG_BTU_START_UP = 0, + SIG_BTU_HCI_MSG, + SIG_BTU_BTA_MSG, + SIG_BTU_BTA_ALARM, + SIG_BTU_GENERAL_ALARM, + SIG_BTU_ONESHOT_ALARM, + SIG_BTU_L2CAP_ALARM, + SIG_BTU_NUM, +} SIG_BTU_t; /* This is the inquiry response information held by BTU, and available ** to applications. @@ -276,6 +288,8 @@ void btu_task_shut_down(void); UINT16 BTU_BleAclPktSize(void); +bool btu_task_post(uint32_t sig, void *param, osi_thread_blocking_t blocking); + /* #ifdef __cplusplus } From 442b57b3eef919392ac3fea2f7f4caa9ca545f91 Mon Sep 17 00:00:00 2001 From: Anton Maklakov Date: Thu, 13 Jun 2019 10:05:14 +0700 Subject: [PATCH 076/486] ci: Adjust more 'spawn' settings in test_confserver --- tools/kconfig_new/test/test_confserver.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tools/kconfig_new/test/test_confserver.py b/tools/kconfig_new/test/test_confserver.py index 20fce7e031..ee9cf43497 100755 --- a/tools/kconfig_new/test/test_confserver.py +++ b/tools/kconfig_new/test/test_confserver.py @@ -51,9 +51,7 @@ def main(): cmdline = "../confserver.py --kconfig Kconfig --config %s" % temp_sdkconfig_path print("Running: %s" % cmdline) - p = pexpect.spawn(cmdline, timeout=2) - p.logfile = args.logfile - p.setecho(False) + p = pexpect.spawn(cmdline, timeout=30, logfile=args.logfile, echo=False, use_poll=True, maxread=1) p.expect("Server running.+\r\n") initial = expect_json(p) From 5c9dc44c496ec99e02fae746a5f77ba88e336b3f Mon Sep 17 00:00:00 2001 From: "Michael (XIAO Xufeng)" Date: Thu, 13 Jun 2019 14:12:54 +0800 Subject: [PATCH 077/486] spi: multichip support move hardcoded numbers, etc. into soc files. create headers for shared types which needs to be documented. (MINOR CHANGE) --- components/driver/include/driver/spi_common.h | 28 ++++++------ components/driver/spi_common.c | 43 +++++++++++-------- components/driver/spi_master.c | 10 +++-- components/driver/spi_slave.c | 6 +-- components/driver/test/test_spi_master.c | 8 ++-- .../include/soc/{spi_pins.h => spi_caps.h} | 13 ++++-- components/soc/esp32/spi_periph.c | 5 +++ components/soc/include/hal/spi_hal.h | 6 ++- components/soc/include/hal/spi_ll.h | 4 +- components/soc/include/hal/spi_slave_hal.h | 1 + components/soc/include/hal/spi_types.h | 18 ++++++++ components/soc/include/soc/spi_periph.h | 5 ++- components/soc/src/hal/spi_hal_iram.c | 2 + docs/Doxyfile | 1 + .../api-reference/peripherals/spi_master.rst | 1 + 15 files changed, 102 insertions(+), 49 deletions(-) rename components/soc/esp32/include/soc/{spi_pins.h => spi_caps.h} (84%) create mode 100644 components/soc/include/hal/spi_types.h diff --git a/components/driver/include/driver/spi_common.h b/components/driver/include/driver/spi_common.h index 546735caa0..5fc2ce957c 100644 --- a/components/driver/include/driver/spi_common.h +++ b/components/driver/include/driver/spi_common.h @@ -21,6 +21,7 @@ #include "esp_err.h" #include "esp32/rom/lldesc.h" #include "soc/spi_periph.h" +#include "hal/spi_types.h" #include "sdkconfig.h" #ifdef __cplusplus @@ -61,15 +62,6 @@ extern "C" */ #define SPI_SWAP_DATA_RX(data, len) (__builtin_bswap32(data)>>(32-len)) -/** - * @brief Enum with the three SPI peripherals that are software-accessible in it - */ -typedef enum { - SPI_HOST=0, ///< SPI1, SPI - HSPI_HOST=1, ///< SPI2, HSPI - VSPI_HOST=2 ///< SPI3, VSPI -} spi_host_device_t; - /** * @brief This is a configuration structure for a SPI bus. * @@ -167,7 +159,7 @@ bool spicommon_dma_chan_free(int dma_chan); #define SPICOMMON_BUSFLAG_SLAVE 0 ///< Initialize I/O in slave mode #define SPICOMMON_BUSFLAG_MASTER (1<<0) ///< Initialize I/O in master mode -#define SPICOMMON_BUSFLAG_NATIVE_PINS (1<<1) ///< Check using iomux pins. Or indicates the pins are configured through the IO mux rather than GPIO matrix. +#define SPICOMMON_BUSFLAG_IOMUX_PINS (1<<1) ///< Check using iomux pins. Or indicates the pins are configured through the IO mux rather than GPIO matrix. #define SPICOMMON_BUSFLAG_SCLK (1<<2) ///< Check existing of SCLK pin. Or indicates CLK line initialized. #define SPICOMMON_BUSFLAG_MISO (1<<3) ///< Check existing of MISO pin. Or indicates MISO line initialized. #define SPICOMMON_BUSFLAG_MOSI (1<<4) ///< Check existing of MOSI pin. Or indicates CLK line initialized. @@ -175,6 +167,9 @@ bool spicommon_dma_chan_free(int dma_chan); #define SPICOMMON_BUSFLAG_WPHD (1<<6) ///< Check existing of WP and HD pins. Or indicates WP & HD pins initialized. #define SPICOMMON_BUSFLAG_QUAD (SPICOMMON_BUSFLAG_DUAL|SPICOMMON_BUSFLAG_WPHD) ///< Check existing of MOSI/MISO/WP/HD pins as output. Or indicates bus able to work under QIO mode. +#define SPICOMMON_BUSFLAG_NATIVE_PINS SPICOMMON_BUSFLAG_IOMUX_PINS + + /** * @brief Connect a SPI peripheral to GPIO pins * @@ -188,7 +183,7 @@ bool spicommon_dma_chan_free(int dma_chan); * @param flags Combination of SPICOMMON_BUSFLAG_* flags, set to ensure the pins set are capable with some functions: * - ``SPICOMMON_BUSFLAG_MASTER``: Initialize I/O in master mode * - ``SPICOMMON_BUSFLAG_SLAVE``: Initialize I/O in slave mode - * - ``SPICOMMON_BUSFLAG_NATIVE_PINS``: Pins set should match the iomux pins of the controller. + * - ``SPICOMMON_BUSFLAG_IOMUX_PINS``: Pins set should match the iomux pins of the controller. * - ``SPICOMMON_BUSFLAG_SCLK``, ``SPICOMMON_BUSFLAG_MISO``, ``SPICOMMON_BUSFLAG_MOSI``: * Make sure SCLK/MISO/MOSI is/are set to a valid GPIO. Also check output capability according to the mode. * - ``SPICOMMON_BUSFLAG_DUAL``: Make sure both MISO and MOSI are output capable so that DIO mode is capable. @@ -196,7 +191,7 @@ bool spicommon_dma_chan_free(int dma_chan); * - ``SPICOMMON_BUSFLAG_QUAD``: Combination of ``SPICOMMON_BUSFLAG_DUAL`` and ``SPICOMMON_BUSFLAG_WPHD``. * @param[out] flags_o A SPICOMMON_BUSFLAG_* flag combination of bus abilities will be written to this address. * Leave to NULL if not needed. - * - ``SPICOMMON_BUSFLAG_NATIVE_PINS``: The bus is connected to iomux pins. + * - ``SPICOMMON_BUSFLAG_IOMUX_PINS``: The bus is connected to iomux pins. * - ``SPICOMMON_BUSFLAG_SCLK``, ``SPICOMMON_BUSFLAG_MISO``, ``SPICOMMON_BUSFLAG_MOSI``: The bus has * CLK/MISO/MOSI connected. * - ``SPICOMMON_BUSFLAG_DUAL``: The bus is capable with DIO mode. @@ -294,6 +289,15 @@ spi_dev_t *spicommon_hw_for_host(spi_host_device_t host); */ int spicommon_irqsource_for_host(spi_host_device_t host); +/** + * @brief Get the IRQ source for a specific SPI DMA + * + * @param host The SPI host + * + * @return The hosts IRQ source + */ +int spicommon_irqdma_source_for_host(spi_host_device_t host); + /** * Callback, to be called when a DMA engine reset is completed */ diff --git a/components/driver/spi_common.c b/components/driver/spi_common.c index 6677ef6562..716a795a35 100644 --- a/components/driver/spi_common.c +++ b/components/driver/spi_common.c @@ -1,9 +1,9 @@ -// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD +// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD // // 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 @@ -49,14 +49,13 @@ static const char *SPI_TAG = "spi"; typedef struct spi_device_t spi_device_t; -#define FUNC_SPI 1 //all pins of HSPI and VSPI shares this function number #define FUNC_GPIO PIN_FUNC_GPIO - #define DMA_CHANNEL_ENABLED(dma_chan) (BIT(dma_chan-1)) //Periph 1 is 'claimed' by SPI flash code. -static atomic_bool spi_periph_claimed[3] = { ATOMIC_VAR_INIT(true), ATOMIC_VAR_INIT(false), ATOMIC_VAR_INIT(false)}; +static atomic_bool spi_periph_claimed[SOC_SPI_PERIPH_NUM] = { ATOMIC_VAR_INIT(true), ATOMIC_VAR_INIT(false), ATOMIC_VAR_INIT(false), +}; static const char* spi_claiming_func[3] = {NULL, NULL, NULL}; static uint8_t spi_dma_chan_enabled = 0; static portMUX_TYPE spi_dma_spinlock = portMUX_INITIALIZER_UNLOCKED; @@ -96,15 +95,25 @@ int spicommon_irqsource_for_host(spi_host_device_t host) return spi_periph_signal[host].irq; } +int spicommon_irqdma_source_for_host(spi_host_device_t host) +{ + return spi_periph_signal[host].irq_dma; +} + spi_dev_t *spicommon_hw_for_host(spi_host_device_t host) { return spi_periph_signal[host].hw; } +static inline uint32_t get_dma_periph(int dma_chan) +{ + return PERIPH_SPI_DMA_MODULE; +} + bool spicommon_dma_chan_claim (int dma_chan) { bool ret = false; - assert( dma_chan == 1 || dma_chan == 2 ); + assert(dma_chan >= 1 && dma_chan <= SOC_SPI_DMA_CHAN_NUM); portENTER_CRITICAL(&spi_dma_spinlock); if ( !(spi_dma_chan_enabled & DMA_CHANNEL_ENABLED(dma_chan)) ) { @@ -112,7 +121,7 @@ bool spicommon_dma_chan_claim (int dma_chan) spi_dma_chan_enabled |= DMA_CHANNEL_ENABLED(dma_chan); ret = true; } - periph_module_enable( PERIPH_SPI_DMA_MODULE ); + periph_module_enable(get_dma_periph(dma_chan)); portEXIT_CRITICAL(&spi_dma_spinlock); return ret; @@ -133,7 +142,7 @@ bool spicommon_dma_chan_free(int dma_chan) spi_dma_chan_enabled &= ~DMA_CHANNEL_ENABLED(dma_chan); if ( spi_dma_chan_enabled == 0 ) { //disable the DMA only when all the channels are freed. - periph_module_disable( PERIPH_SPI_DMA_MODULE ); + periph_module_disable(get_dma_periph(dma_chan)); } portEXIT_CRITICAL(&spi_dma_spinlock); @@ -212,7 +221,7 @@ esp_err_t spicommon_bus_initialize_io(spi_host_device_t host, const spi_bus_conf //check if the selected pins correspond to the iomux pins of the peripheral bool use_iomux = bus_uses_iomux_pins(host, bus_config); - if (use_iomux) temp_flag |= SPICOMMON_BUSFLAG_NATIVE_PINS; + if (use_iomux) temp_flag |= SPICOMMON_BUSFLAG_IOMUX_PINS; uint32_t missing_flag = flags & ~temp_flag; missing_flag &= ~SPICOMMON_BUSFLAG_MASTER;//don't check this flag @@ -224,7 +233,7 @@ esp_err_t spicommon_bus_initialize_io(spi_host_device_t host, const spi_bus_conf if (missing_flag & SPICOMMON_BUSFLAG_MISO) ESP_LOGE(SPI_TAG, "miso pin required."); if (missing_flag & SPICOMMON_BUSFLAG_DUAL) ESP_LOGE(SPI_TAG, "not both mosi and miso output capable"); if (missing_flag & SPICOMMON_BUSFLAG_WPHD) ESP_LOGE(SPI_TAG, "both wp and hd required."); - if (missing_flag & SPICOMMON_BUSFLAG_NATIVE_PINS) ESP_LOGE(SPI_TAG, "not using iomux pins"); + if (missing_flag & SPICOMMON_BUSFLAG_IOMUX_PINS) ESP_LOGE(SPI_TAG, "not using iomux pins"); SPI_CHECK(missing_flag == 0, "not all required capabilities satisfied.", ESP_ERR_INVALID_ARG); } @@ -234,25 +243,25 @@ esp_err_t spicommon_bus_initialize_io(spi_host_device_t host, const spi_bus_conf ESP_LOGD(SPI_TAG, "SPI%d use iomux pins.", host+1); if (bus_config->mosi_io_num >= 0) { gpio_iomux_in(bus_config->mosi_io_num, spi_periph_signal[host].spid_in); - gpio_iomux_out(bus_config->mosi_io_num, FUNC_SPI, false); + gpio_iomux_out(bus_config->mosi_io_num, spi_periph_signal[host].func, false); } if (bus_config->miso_io_num >= 0) { gpio_iomux_in(bus_config->miso_io_num, spi_periph_signal[host].spiq_in); - gpio_iomux_out(bus_config->miso_io_num, FUNC_SPI, false); + gpio_iomux_out(bus_config->miso_io_num, spi_periph_signal[host].func, false); } if (bus_config->quadwp_io_num >= 0) { gpio_iomux_in(bus_config->quadwp_io_num, spi_periph_signal[host].spiwp_in); - gpio_iomux_out(bus_config->quadwp_io_num, FUNC_SPI, false); + gpio_iomux_out(bus_config->quadwp_io_num, spi_periph_signal[host].func, false); } if (bus_config->quadhd_io_num >= 0) { gpio_iomux_in(bus_config->quadhd_io_num, spi_periph_signal[host].spihd_in); - gpio_iomux_out(bus_config->quadhd_io_num, FUNC_SPI, false); + gpio_iomux_out(bus_config->quadhd_io_num, spi_periph_signal[host].func, false); } if (bus_config->sclk_io_num >= 0) { gpio_iomux_in(bus_config->sclk_io_num, spi_periph_signal[host].spiclk_in); - gpio_iomux_out(bus_config->sclk_io_num, FUNC_SPI, false); + gpio_iomux_out(bus_config->sclk_io_num, spi_periph_signal[host].func, false); } - temp_flag |= SPICOMMON_BUSFLAG_NATIVE_PINS; + temp_flag |= SPICOMMON_BUSFLAG_IOMUX_PINS; } else { //Use GPIO matrix ESP_LOGD(SPI_TAG, "SPI%d use gpio matrix.", host+1); @@ -354,7 +363,7 @@ void spicommon_cs_initialize(spi_host_device_t host, int cs_io_num, int cs_num, if (!force_gpio_matrix && cs_io_num == spi_periph_signal[host].spics0_iomux_pin && cs_num == 0) { //The cs0s for all SPI peripherals map to pin mux source 1, so we use that instead of a define. gpio_iomux_in(cs_io_num, spi_periph_signal[host].spics_in); - gpio_iomux_out(cs_io_num, FUNC_SPI, false); + gpio_iomux_out(cs_io_num, spi_periph_signal[host].func, false); } else { //Use GPIO matrix if (GPIO_IS_VALID_OUTPUT_GPIO(cs_io_num)) { diff --git a/components/driver/spi_master.c b/components/driver/spi_master.c index 90382fd01a..fcbbdedc97 100644 --- a/components/driver/spi_master.c +++ b/components/driver/spi_master.c @@ -199,7 +199,7 @@ struct spi_device_t { bool waiting; //the device is waiting for the exclusive control of the bus }; -static spi_host_t *spihost[3]; +static spi_host_t *spihost[SOC_SPI_PERIPH_NUM]; static const char *SPI_TAG = "spi_master"; @@ -263,7 +263,7 @@ esp_err_t spi_bus_initialize(spi_host_device_t host, const spi_bus_config_t *bus int dma_desc_ct=0; spihost[host]->dma_chan=dma_chan; if (dma_chan == 0) { - spihost[host]->max_transfer_sz = 64; + spihost[host]->max_transfer_sz = SOC_SPI_MAXIMUM_BUFFER_SIZE; } else { //See how many dma descriptors we need and allocate them dma_desc_ct=lldesc_get_required_num(bus_config->max_transfer_sz); @@ -389,13 +389,15 @@ esp_err_t spi_bus_add_device(spi_host_device_t host, const spi_device_interface_ int freq; spi_hal_context_t *hal = &spihost[host]->hal; hal->half_duplex = dev_config->flags & SPI_DEVICE_HALFDUPLEX ? 1 : 0; +#ifdef SOC_SPI_SUPPORT_AS_CS hal->as_cs = dev_config->flags & SPI_DEVICE_CLK_AS_CS ? 1 : 0; +#endif hal->positive_cs = dev_config->flags & SPI_DEVICE_POSITIVE_CS ? 1 : 0; hal->no_compensate = dev_config->flags & SPI_DEVICE_NO_DUMMY ? 1 : 0; spi_hal_timing_conf_t temp_timing_conf; esp_err_t ret = spi_hal_get_clock_conf(hal, dev_config->clock_speed_hz, duty_cycle, - !(spihost[host]->flags & SPICOMMON_BUSFLAG_NATIVE_PINS), + !(spihost[host]->flags & SPICOMMON_BUSFLAG_IOMUX_PINS), dev_config->input_delay_ns, &freq, &temp_timing_conf); @@ -427,7 +429,7 @@ esp_err_t spi_bus_add_device(spi_host_device_t host, const spi_device_interface_ //Set CS pin, CS options if (dev_config->spics_io_num >= 0) { - spicommon_cs_initialize(host, dev_config->spics_io_num, freecs, !(spihost[host]->flags&SPICOMMON_BUSFLAG_NATIVE_PINS)); + spicommon_cs_initialize(host, dev_config->spics_io_num, freecs, !(spihost[host]->flags&SPICOMMON_BUSFLAG_IOMUX_PINS)); } *handle=dev; diff --git a/components/driver/spi_slave.c b/components/driver/spi_slave.c index f9f792a280..2cac4610ff 100644 --- a/components/driver/spi_slave.c +++ b/components/driver/spi_slave.c @@ -72,13 +72,13 @@ typedef struct { #endif } spi_slave_t; -static spi_slave_t *spihost[3]; +static spi_slave_t *spihost[SOC_SPI_PERIPH_NUM]; static void IRAM_ATTR spi_intr(void *arg); static inline bool bus_is_iomux(spi_slave_t *host) { - return host->flags&SPICOMMON_BUSFLAG_NATIVE_PINS; + return host->flags&SPICOMMON_BUSFLAG_IOMUX_PINS; } static void freeze_cs(spi_slave_t *host) @@ -149,7 +149,7 @@ esp_err_t spi_slave_initialize(spi_host_device_t host, const spi_bus_config_t *b spihost[host]->max_transfer_sz = dma_desc_ct * SPI_MAX_DMA_LEN; } else { //We're limited to non-DMA transfers: the SPI work registers can hold 64 bytes at most. - spihost[host]->max_transfer_sz = 16 * 4; + spihost[host]->max_transfer_sz = SOC_SPI_MAXIMUM_BUFFER_SIZE; } #ifdef CONFIG_PM_ENABLE err = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "spi_slave", diff --git a/components/driver/test/test_spi_master.c b/components/driver/test/test_spi_master.c index 22b1961cc0..5b1c3c2335 100644 --- a/components/driver/test/test_spi_master.c +++ b/components/driver/test/test_spi_master.c @@ -329,7 +329,7 @@ TEST_CASE("spi bus setting with different pin configs", "[spi]") uint32_t flags_expected; ESP_LOGI(TAG, "test 6 iomux output pins..."); - flags_expected = SPICOMMON_BUSFLAG_SCLK | SPICOMMON_BUSFLAG_MOSI | SPICOMMON_BUSFLAG_MISO | SPICOMMON_BUSFLAG_NATIVE_PINS | SPICOMMON_BUSFLAG_QUAD; + flags_expected = SPICOMMON_BUSFLAG_SCLK | SPICOMMON_BUSFLAG_MOSI | SPICOMMON_BUSFLAG_MISO | SPICOMMON_BUSFLAG_IOMUX_PINS | SPICOMMON_BUSFLAG_QUAD; cfg = (spi_bus_config_t){.mosi_io_num = HSPI_IOMUX_PIN_NUM_MOSI, .miso_io_num = HSPI_IOMUX_PIN_NUM_MISO, .sclk_io_num = HSPI_IOMUX_PIN_NUM_CLK, .quadhd_io_num = HSPI_IOMUX_PIN_NUM_HD, .quadwp_io_num = HSPI_IOMUX_PIN_NUM_WP, .max_transfer_sz = 8, .flags = flags_expected}; TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, 0, flags_expected|SPICOMMON_BUSFLAG_MASTER, &flags_o)); @@ -338,7 +338,7 @@ TEST_CASE("spi bus setting with different pin configs", "[spi]") TEST_ASSERT_EQUAL_HEX32( flags_expected, flags_o ); ESP_LOGI(TAG, "test 4 iomux output pins..."); - flags_expected = SPICOMMON_BUSFLAG_SCLK | SPICOMMON_BUSFLAG_MOSI | SPICOMMON_BUSFLAG_MISO | SPICOMMON_BUSFLAG_NATIVE_PINS | SPICOMMON_BUSFLAG_DUAL; + flags_expected = SPICOMMON_BUSFLAG_SCLK | SPICOMMON_BUSFLAG_MOSI | SPICOMMON_BUSFLAG_MISO | SPICOMMON_BUSFLAG_IOMUX_PINS | SPICOMMON_BUSFLAG_DUAL; cfg = (spi_bus_config_t){.mosi_io_num = HSPI_IOMUX_PIN_NUM_MOSI, .miso_io_num = HSPI_IOMUX_PIN_NUM_MISO, .sclk_io_num = HSPI_IOMUX_PIN_NUM_CLK, .quadhd_io_num = -1, .quadwp_io_num = -1, .max_transfer_sz = 8, .flags = flags_expected}; TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, 0, flags_expected|SPICOMMON_BUSFLAG_MASTER, &flags_o)); @@ -396,7 +396,7 @@ TEST_CASE("spi bus setting with different pin configs", "[spi]") TEST_ASSERT_EQUAL_HEX32( flags_expected, flags_o ); ESP_LOGI(TAG, "check native flag for 6 output pins..."); - flags_expected = SPICOMMON_BUSFLAG_NATIVE_PINS; + flags_expected = SPICOMMON_BUSFLAG_IOMUX_PINS; //swap MOSI and MISO cfg = (spi_bus_config_t){.mosi_io_num = HSPI_IOMUX_PIN_NUM_MISO, .miso_io_num = HSPI_IOMUX_PIN_NUM_MOSI, .sclk_io_num = HSPI_IOMUX_PIN_NUM_CLK, .quadhd_io_num = HSPI_IOMUX_PIN_NUM_HD, .quadwp_io_num = HSPI_IOMUX_PIN_NUM_WP, .max_transfer_sz = 8, .flags = flags_expected}; @@ -404,7 +404,7 @@ TEST_CASE("spi bus setting with different pin configs", "[spi]") TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, 0, flags_expected|SPICOMMON_BUSFLAG_SLAVE, &flags_o)); ESP_LOGI(TAG, "check native flag for 4 output pins..."); - flags_expected = SPICOMMON_BUSFLAG_NATIVE_PINS; + flags_expected = SPICOMMON_BUSFLAG_IOMUX_PINS; //swap MOSI and MISO cfg = (spi_bus_config_t){.mosi_io_num = HSPI_IOMUX_PIN_NUM_MISO, .miso_io_num = HSPI_IOMUX_PIN_NUM_MOSI, .sclk_io_num = HSPI_IOMUX_PIN_NUM_CLK, .quadhd_io_num = -1, .quadwp_io_num = -1, .max_transfer_sz = 8, .flags = flags_expected}; diff --git a/components/soc/esp32/include/soc/spi_pins.h b/components/soc/esp32/include/soc/spi_caps.h similarity index 84% rename from components/soc/esp32/include/soc/spi_pins.h rename to components/soc/esp32/include/soc/spi_caps.h index 80a17ff090..881e229976 100644 --- a/components/soc/esp32/include/soc/spi_pins.h +++ b/components/soc/esp32/include/soc/spi_caps.h @@ -12,8 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef _SOC_SPI_PINS_H_ -#define _SOC_SPI_PINS_H_ +#ifndef _SOC_SPI_CAPS_H_ +#define _SOC_SPI_CAPS_H_ + +#define SOC_SPI_PERIPH_NUM 3 +#define SOC_SPI_DMA_CHAN_NUM 2 #define SPI_IOMUX_PIN_NUM_MISO 7 #define SPI_IOMUX_PIN_NUM_MOSI 8 @@ -44,4 +47,8 @@ #define VSPI_IOMUX_PIN_NUM_WP 22 #define VSPI_IOMUX_PIN_NUM_HD 21 -#endif /* _SOC_SPI_PINS_H_ */ \ No newline at end of file +#define SOC_SPI_MAXIMUM_BUFFER_SIZE 64 + +#define SOC_SPI_SUPPORT_AS_CS 1 //Support to toggle the CS while the clock toggles + +#endif /* _SOC_SPI_CAPS_H_ */ \ No newline at end of file diff --git a/components/soc/esp32/spi_periph.c b/components/soc/esp32/spi_periph.c index 0847777089..a800cc6ec5 100644 --- a/components/soc/esp32/spi_periph.c +++ b/components/soc/esp32/spi_periph.c @@ -14,6 +14,8 @@ #include "soc/spi_periph.h" +#define FUNC_SPI 1 //all pins of SPI1, HSPI and VSPI shares this function number + /* Bunch of constants for every SPI peripheral: GPIO signals, irqs, hw addr of registers etc */ @@ -40,6 +42,7 @@ const spi_signal_conn_t spi_periph_signal[3] = { .irq = ETS_SPI1_INTR_SOURCE, .irq_dma = ETS_SPI1_DMA_INTR_SOURCE, .module = PERIPH_SPI_MODULE, + .func = FUNC_SPI, .hw = &SPI1 }, { .spiclk_out = HSPICLK_OUT_IDX, @@ -63,6 +66,7 @@ const spi_signal_conn_t spi_periph_signal[3] = { .irq = ETS_SPI2_INTR_SOURCE, .irq_dma = ETS_SPI2_DMA_INTR_SOURCE, .module = PERIPH_HSPI_MODULE, + .func = FUNC_SPI, .hw = &SPI2 }, { .spiclk_out = VSPICLK_OUT_IDX, @@ -86,6 +90,7 @@ const spi_signal_conn_t spi_periph_signal[3] = { .irq = ETS_SPI3_INTR_SOURCE, .irq_dma = ETS_SPI3_DMA_INTR_SOURCE, .module = PERIPH_VSPI_MODULE, + .func = FUNC_SPI, .hw = &SPI3 } }; \ No newline at end of file diff --git a/components/soc/include/hal/spi_hal.h b/components/soc/include/hal/spi_hal.h index ddc34e29b6..2012433a26 100644 --- a/components/soc/include/hal/spi_hal.h +++ b/components/soc/include/hal/spi_hal.h @@ -34,7 +34,7 @@ // field comments. #pragma once -#include "spi_ll.h" +#include "hal/spi_ll.h" #include #include "soc/lldesc.h" @@ -85,7 +85,9 @@ typedef struct { uint32_t rx_lsbfirst : 1; ///< Whether LSB is received first for RX data, device specific uint32_t dma_enabled : 1; ///< Whether the DMA is enabled, do not update after initialization uint32_t no_compensate : 1; ///< No need to add dummy to compensate the timing, device specific - uint32_t as_cs : 1; ///< Whether the AS_CS feature is enabled, device specific +#ifdef SOC_SPI_SUPPORT_AS_CS + uint32_t as_cs : 1; ///< Whether to toggle the CS while the clock toggles, device specific +#endif uint32_t positive_cs : 1; ///< Whether the postive CS feature is abled, device specific };//boolean configurations diff --git a/components/soc/include/hal/spi_ll.h b/components/soc/include/hal/spi_ll.h index 31feb83302..0213d6dc12 100644 --- a/components/soc/include/hal/spi_ll.h +++ b/components/soc/include/hal/spi_ll.h @@ -18,11 +18,11 @@ * See readme.md in soc/include/hal/readme.md ******************************************************************************/ -// The LL layer for SPI register operations +// The LL layer for ESP32 SPI register operations #pragma once -#include "hal_defs.h" +#include "hal/hal_defs.h" #include "soc/spi_periph.h" #include "esp32/rom/lldesc.h" #include diff --git a/components/soc/include/hal/spi_slave_hal.h b/components/soc/include/hal/spi_slave_hal.h index 81d1706a7f..80e1113716 100644 --- a/components/soc/include/hal/spi_slave_hal.h +++ b/components/soc/include/hal/spi_slave_hal.h @@ -35,6 +35,7 @@ #include "soc/lldesc.h" #include "soc/spi_struct.h" #include +#include "soc/spi_caps.h" /** * Context that should be maintained by both the driver and the HAL. diff --git a/components/soc/include/hal/spi_types.h b/components/soc/include/hal/spi_types.h new file mode 100644 index 0000000000..12ee775e1e --- /dev/null +++ b/components/soc/include/hal/spi_types.h @@ -0,0 +1,18 @@ +#pragma once + +#include "soc/spi_caps.h" +#include "sdkconfig.h" + +/** + * @brief Enum with the three SPI peripherals that are software-accessible in it + */ +typedef enum { + SPI1_HOST=0, ///< SPI1 + SPI2_HOST=1, ///< SPI2 + SPI3_HOST=2, ///< SPI3 +} spi_host_device_t; + +//alias for different chips +#define SPI_HOST SPI1_HOST +#define HSPI_HOST SPI2_HOST +#define VSPI_HOST SPI3_HOST diff --git a/components/soc/include/soc/spi_periph.h b/components/soc/include/soc/spi_periph.h index 812c1becac..bbe864eb6f 100644 --- a/components/soc/include/soc/spi_periph.h +++ b/components/soc/include/soc/spi_periph.h @@ -17,7 +17,7 @@ #include "soc/soc.h" #include "soc/periph_defs.h" //include soc related (generated) definitions -#include "soc/spi_pins.h" +#include "soc/spi_caps.h" #include "soc/spi_reg.h" #include "soc/spi_struct.h" #include "soc/gpio_sig_map.h" @@ -52,10 +52,11 @@ typedef struct { const uint8_t irq; //irq source for interrupt mux const uint8_t irq_dma; //dma irq source for interrupt mux const periph_module_t module; //peripheral module, for enabling clock etc + const int func; //function number for IOMUX spi_dev_t *hw; //Pointer to the hardware registers } spi_signal_conn_t; -extern const spi_signal_conn_t spi_periph_signal[3]; +extern const spi_signal_conn_t spi_periph_signal[SOC_SPI_PERIPH_NUM]; #ifdef __cplusplus } diff --git a/components/soc/src/hal/spi_hal_iram.c b/components/soc/src/hal/spi_hal_iram.c index 2bf0aadb80..57f5607853 100644 --- a/components/soc/src/hal/spi_hal_iram.c +++ b/components/soc/src/hal/spi_hal_iram.c @@ -21,7 +21,9 @@ void spi_hal_setup_device(const spi_hal_context_t *hal) { //Configure clock settings spi_dev_t *hw = hal->hw; +#ifdef SOC_SPI_SUPPORT_AS_CS spi_ll_master_set_cksel(hw, hal->cs_pin_id, hal->as_cs); +#endif spi_ll_master_set_pos_cs(hw, hal->cs_pin_id, hal->positive_cs); spi_ll_master_set_clock_by_reg(hw, &hal->timing_conf->clock_reg); //Configure bit order diff --git a/docs/Doxyfile b/docs/Doxyfile index 858a6a39e0..5d7661e24d 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -87,6 +87,7 @@ INPUT = \ ../../components/driver/include/driver/touch_pad.h \ ../../components/driver/include/driver/uart.h \ ../../components/esp_adc_cal/include/esp_adc_cal.h \ + ../../components/soc/include/hal/spi_types.h \ ../../components/soc/esp32/include/soc/adc_channel.h \ ../../components/soc/esp32/include/soc/dac_channel.h \ ../../components/soc/esp32/include/soc/touch_channel.h \ diff --git a/docs/en/api-reference/peripherals/spi_master.rst b/docs/en/api-reference/peripherals/spi_master.rst index e02bda1513..13084274fc 100644 --- a/docs/en/api-reference/peripherals/spi_master.rst +++ b/docs/en/api-reference/peripherals/spi_master.rst @@ -538,6 +538,7 @@ Display graphics on the 320x240 LCD of WROVER-Kits: :example:`peripherals/spi_ma API Reference - SPI Common -------------------------- +.. include:: /_build/inc/spi_types.inc .. include:: /_build/inc/spi_common.inc From 2eff8f774237072240f9c8401acf4573ca12b4a8 Mon Sep 17 00:00:00 2001 From: "Michael (XIAO Xufeng)" Date: Thu, 13 Jun 2019 15:09:28 +0800 Subject: [PATCH 078/486] spi_slave: fix a example issue caused by word alignment --- examples/peripherals/spi_slave/receiver/main/app_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/peripherals/spi_slave/receiver/main/app_main.c b/examples/peripherals/spi_slave/receiver/main/app_main.c index c5362dd97b..1a51c653a4 100644 --- a/examples/peripherals/spi_slave/receiver/main/app_main.c +++ b/examples/peripherals/spi_slave/receiver/main/app_main.c @@ -109,8 +109,8 @@ void app_main() ret=spi_slave_initialize(HSPI_HOST, &buscfg, &slvcfg, 1); assert(ret==ESP_OK); - char sendbuf[129]=""; - char recvbuf[129]=""; + WORD_ALIGNED_ATTR char sendbuf[129]=""; + WORD_ALIGNED_ATTR char recvbuf[129]=""; memset(recvbuf, 0, 33); spi_slave_transaction_t t; memset(&t, 0, sizeof(t)); From caf121e4b6a67e7ce1746a69bc246257f972da1b Mon Sep 17 00:00:00 2001 From: "Michael (XIAO Xufeng)" Date: Wed, 19 Jun 2019 16:37:55 +0800 Subject: [PATCH 079/486] esp_flash: break the inappropriate include chain in spi_flash_host_drv.h --- components/soc/esp32/include/hal/spi_flash_ll.h | 13 +------------ components/soc/include/hal/spi_flash_hal.h | 2 +- .../{spi_flash_host_drv.h => spi_flash_types.h} | 14 +++++++++++++- components/spi_flash/include/esp_flash.h | 2 +- 4 files changed, 16 insertions(+), 15 deletions(-) rename components/soc/include/hal/{spi_flash_host_drv.h => spi_flash_types.h} (87%) diff --git a/components/soc/esp32/include/hal/spi_flash_ll.h b/components/soc/esp32/include/hal/spi_flash_ll.h index f9adbd5f1b..7155e0a872 100644 --- a/components/soc/esp32/include/hal/spi_flash_ll.h +++ b/components/soc/esp32/include/hal/spi_flash_ll.h @@ -24,6 +24,7 @@ #include #include "soc/spi_periph.h" +#include "hal/spi_flash_types.h" #include // For MIN/MAX #include #include @@ -43,18 +44,6 @@ ///Slowest io mode supported by ESP32, currently SlowRd #define SPI_FLASH_READ_MODE_MIN SPI_FLASH_SLOWRD -/** @brief Mode used for reading from SPI flash */ -typedef enum { - SPI_FLASH_SLOWRD = 0, ///< Data read using single I/O, some limits on speed - SPI_FLASH_FASTRD, ///< Data read using single I/O, no limit on speed - SPI_FLASH_DOUT, ///< Data read using dual I/O - SPI_FLASH_DIO, ///< Both address & data transferred using dual I/O - SPI_FLASH_QOUT, ///< Data read using quad I/O - SPI_FLASH_QIO, ///< Both address & data transferred using quad I/O - - SPI_FLASH_READ_MODE_MAX, ///< The fastest io mode supported by the host is ``ESP_FLASH_READ_MODE_MAX-1``. -} esp_flash_read_mode_t; - /// type to store pre-calculated register value in above layers typedef typeof(SPI1.clock) spi_flash_ll_clock_reg_t; diff --git a/components/soc/include/hal/spi_flash_hal.h b/components/soc/include/hal/spi_flash_hal.h index 1a5c373614..49972c5199 100644 --- a/components/soc/include/hal/spi_flash_hal.h +++ b/components/soc/include/hal/spi_flash_hal.h @@ -23,7 +23,7 @@ #pragma once #include "hal/spi_flash_ll.h" -#include "hal/spi_flash_host_drv.h" +#include "hal/spi_flash_types.h" #include "soc/soc_memory_layout.h" #define ESP_FLASH_DEFAULT_FREQ ESP_FLASH_20MHZ diff --git a/components/soc/include/hal/spi_flash_host_drv.h b/components/soc/include/hal/spi_flash_types.h similarity index 87% rename from components/soc/include/hal/spi_flash_host_drv.h rename to components/soc/include/hal/spi_flash_types.h index 5095bc8f50..067d10fd4b 100644 --- a/components/soc/include/hal/spi_flash_host_drv.h +++ b/components/soc/include/hal/spi_flash_types.h @@ -14,7 +14,7 @@ #pragma once -#include "hal/spi_flash_ll.h" +#include #include "hal/esp_flash_err.h" /** Definition of a common transaction. Also holds the return value. */ @@ -26,6 +26,18 @@ typedef struct { uint32_t miso_data[2]; ///< [out] Input data from slave, little endian } spi_flash_trans_t; +/** @brief Mode used for reading from SPI flash */ +typedef enum { + SPI_FLASH_SLOWRD = 0, ///< Data read using single I/O, some limits on speed + SPI_FLASH_FASTRD, ///< Data read using single I/O, no limit on speed + SPI_FLASH_DOUT, ///< Data read using dual I/O + SPI_FLASH_DIO, ///< Both address & data transferred using dual I/O + SPI_FLASH_QOUT, ///< Data read using quad I/O + SPI_FLASH_QIO, ///< Both address & data transferred using quad I/O + + SPI_FLASH_READ_MODE_MAX, ///< The fastest io mode supported by the host is ``ESP_FLASH_READ_MODE_MAX-1``. +} esp_flash_read_mode_t; + struct spi_flash_host_driver_t; typedef struct spi_flash_host_driver_t spi_flash_host_driver_t; diff --git a/components/spi_flash/include/esp_flash.h b/components/spi_flash/include/esp_flash.h index 904b09b493..48eedcda37 100644 --- a/components/spi_flash/include/esp_flash.h +++ b/components/spi_flash/include/esp_flash.h @@ -17,7 +17,7 @@ #include #include -#include "hal/spi_flash_host_drv.h" +#include "hal/spi_flash_types.h" struct spi_flash_chip_t; typedef struct spi_flash_chip_t spi_flash_chip_t; From 026533cd72190bff864af971238bdeff0964fd32 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 19 Jun 2019 16:37:11 +0800 Subject: [PATCH 080/486] esp_flash: fix C++ compilation and some typos --- components/soc/include/hal/esp_flash_err.h | 8 +++- components/soc/include/hal/spi_flash_hal.h | 23 ---------- components/soc/include/hal/spi_flash_types.h | 28 ++++++++++++ components/spi_flash/README.rst | 10 ++--- components/spi_flash/esp_flash_api.c | 10 ++--- components/spi_flash/include/esp_flash.h | 46 ++++++++++++-------- components/spi_flash/spi_flash_os_func_app.c | 8 ++-- 7 files changed, 76 insertions(+), 57 deletions(-) diff --git a/components/soc/include/hal/esp_flash_err.h b/components/soc/include/hal/esp_flash_err.h index c760f19fe6..cde56aa507 100644 --- a/components/soc/include/hal/esp_flash_err.h +++ b/components/soc/include/hal/esp_flash_err.h @@ -17,6 +17,10 @@ #include #include "esp_err.h" +#ifdef __cplusplus +extern "C" { +#endif + /* * Possible errors returned from esp flash internal functions, these error codes * should be consistent with esp_err_t codes. But in order to make the source @@ -36,4 +40,6 @@ #define ESP_ERR_FLASH_UNSUPPORTED_CHIP (ESP_ERR_FLASH_ERR_BASE+5) ///< Requested operation isn't supported by this model of SPI flash chip. #define ESP_ERR_FLASH_PROTECTED (ESP_ERR_FLASH_ERR_BASE+6) ///< Write operation failed due to chip's write protection being enabled. - +#ifdef __cplusplus +} +#endif diff --git a/components/soc/include/hal/spi_flash_hal.h b/components/soc/include/hal/spi_flash_hal.h index 49972c5199..cd0fb5d361 100644 --- a/components/soc/include/hal/spi_flash_hal.h +++ b/components/soc/include/hal/spi_flash_hal.h @@ -26,33 +26,10 @@ #include "hal/spi_flash_types.h" #include "soc/soc_memory_layout.h" -#define ESP_FLASH_DEFAULT_FREQ ESP_FLASH_20MHZ - /* Hardware host-specific constants */ #define SPI_FLASH_HAL_MAX_WRITE_BYTES 64 #define SPI_FLASH_HAL_MAX_READ_BYTES 64 -///Lowest speed supported by the driver, currently 5 MHz -#define ESP_FLASH_SPEED_MIN ESP_FLASH_5MHZ - -/** - * @brief SPI flash clock speed values, always refer to them by the enum rather - * than the actual value (more speed may be appended into the list). - * - * A strategy to select the maximum allowed speed is to enumerate from the - * ``ESP_FLSH_SPEED_MAX-1`` or highest frequency supported by your flash, and - * decrease the speed until the probing success. - */ -typedef enum { - ESP_FLASH_5MHZ = 0, ///< The flash runs under 5MHz - ESP_FLASH_10MHZ, ///< The flash runs under 10MHz - ESP_FLASH_20MHZ, ///< The flash runs under 20MHz - ESP_FLASH_26MHZ, ///< The flash runs under 26MHz - ESP_FLASH_40MHZ, ///< The flash runs under 40MHz - ESP_FLASH_80MHZ, ///< The flash runs under 80MHz - ESP_FLASH_SPEED_MAX, ///< The maximum frequency supported by the host is ``ESP_FLASH_SPEED_MAX-1``. -} esp_flash_speed_t; - /** * Generic driver context structure for all chips using the SPI peripheral. * Include this into the HEAD of the driver data for other driver diff --git a/components/soc/include/hal/spi_flash_types.h b/components/soc/include/hal/spi_flash_types.h index 067d10fd4b..28e591f82a 100644 --- a/components/soc/include/hal/spi_flash_types.h +++ b/components/soc/include/hal/spi_flash_types.h @@ -17,6 +17,10 @@ #include #include "hal/esp_flash_err.h" +#ifdef __cplusplus +extern "C" { +#endif + /** Definition of a common transaction. Also holds the return value. */ typedef struct { uint8_t command; ///< Command to send, always 8bits @@ -26,6 +30,27 @@ typedef struct { uint32_t miso_data[2]; ///< [out] Input data from slave, little endian } spi_flash_trans_t; +/** + * @brief SPI flash clock speed values, always refer to them by the enum rather + * than the actual value (more speed may be appended into the list). + * + * A strategy to select the maximum allowed speed is to enumerate from the + * ``ESP_FLSH_SPEED_MAX-1`` or highest frequency supported by your flash, and + * decrease the speed until the probing success. + */ +typedef enum { + ESP_FLASH_5MHZ = 0, ///< The flash runs under 5MHz + ESP_FLASH_10MHZ, ///< The flash runs under 10MHz + ESP_FLASH_20MHZ, ///< The flash runs under 20MHz + ESP_FLASH_26MHZ, ///< The flash runs under 26MHz + ESP_FLASH_40MHZ, ///< The flash runs under 40MHz + ESP_FLASH_80MHZ, ///< The flash runs under 80MHz + ESP_FLASH_SPEED_MAX, ///< The maximum frequency supported by the host is ``ESP_FLASH_SPEED_MAX-1``. +} esp_flash_speed_t; + +///Lowest speed supported by the driver, currently 5 MHz +#define ESP_FLASH_SPEED_MIN ESP_FLASH_5MHZ + /** @brief Mode used for reading from SPI flash */ typedef enum { SPI_FLASH_SLOWRD = 0, ///< Data read using single I/O, some limits on speed @@ -122,3 +147,6 @@ struct spi_flash_host_driver_t { bool (*region_protected)(spi_flash_host_driver_t* driver, uint32_t addr, uint32_t size); }; +#ifdef __cplusplus +} +#endif diff --git a/components/spi_flash/README.rst b/components/spi_flash/README.rst index e65b1ad4c6..26387c04a5 100644 --- a/components/spi_flash/README.rst +++ b/components/spi_flash/README.rst @@ -146,7 +146,7 @@ Implementation The ``esp_flash_t`` structure holds chip data as well as three important parts of this API: 1. The host driver, which provides the hardware support to access the chip; -2. The chip driver, which provides compability service to different chips; +2. The chip driver, which provides compatibility service to different chips; 3. The OS functions, provides support of some OS functions (e.g. lock, delay) in different stages (1st/2st boot, or the app). @@ -167,7 +167,7 @@ wrap these functions as ``spi_flash_host_driver_t`` for upper layer to use. You can also implement your own host driver, even with the GPIO. As long as all the functions in the ``spi_flash_host_driver_t`` are implemented, the -esp_flash API can access to the flash regardless of the lowlevel hardware. +esp_flash API can access to the flash regardless of the low-level hardware. Chip driver ^^^^^^^^^^^ @@ -188,17 +188,17 @@ The chip driver relies on the host driver. OS functions ^^^^^^^^^^^^ -Currently the OS function layer provides a lock and a delay entrance. +Currently the OS function layer provides a lock and a delay entries. The lock is used to resolve the conflicts between the SPI chip access and -other functions. E.g. the cache (used for the code and psram data fetch) +other functions. E.g. the cache (used for the code and PSRAM data fetch) should be disabled when the flash chip on the SPI0/1 is being accessed. Also, some devices which don't have CS wire, or the wire is controlled by the software (e.g. SD card via SPI interface), requires the bus to be monopolized during a period. The delay is used by some long operations which requires the master to wait -or polling periodly. +or polling periodically. The top API wraps these the chip driver and OS functions into an entire diff --git a/components/spi_flash/esp_flash_api.c b/components/spi_flash/esp_flash_api.c index 3a48154c7b..48533ec573 100644 --- a/components/spi_flash/esp_flash_api.c +++ b/components/spi_flash/esp_flash_api.c @@ -448,11 +448,11 @@ static esp_err_t find_region(const esp_flash_t *chip, const esp_flash_region_t * return ESP_ERR_NOT_FOUND; } -esp_err_t IRAM_ATTR esp_flash_get_protected_region(esp_flash_t *chip, const esp_flash_region_t *region, bool *protected) +esp_err_t IRAM_ATTR esp_flash_get_protected_region(esp_flash_t *chip, const esp_flash_region_t *region, bool *out_protected) { VERIFY_OP(get_protected_regions); - if (protected == NULL) { + if (out_protected == NULL) { return ESP_ERR_INVALID_ARG; } @@ -470,13 +470,13 @@ esp_err_t IRAM_ATTR esp_flash_get_protected_region(esp_flash_t *chip, const esp_ err = chip->chip_drv->get_protected_regions(chip, &protection_mask); if (err == ESP_OK) { - *protected = protection_mask & (1LL << index); + *out_protected = protection_mask & (1LL << index); } return spiflash_end(chip, err); } -esp_err_t IRAM_ATTR esp_flash_set_protected_region(esp_flash_t *chip, const esp_flash_region_t *region, bool protected) +esp_err_t IRAM_ATTR esp_flash_set_protected_region(esp_flash_t *chip, const esp_flash_region_t *region, bool protect) { VERIFY_OP(set_protected_regions); @@ -494,7 +494,7 @@ esp_err_t IRAM_ATTR esp_flash_set_protected_region(esp_flash_t *chip, const esp_ err = chip->chip_drv->get_protected_regions(chip, &protection_mask); if (err == ESP_OK) { - if (protected) { + if (protect) { protection_mask |= (1LL << index); } else { protection_mask &= ~(1LL << index); diff --git a/components/spi_flash/include/esp_flash.h b/components/spi_flash/include/esp_flash.h index 48eedcda37..81524dc5d2 100644 --- a/components/spi_flash/include/esp_flash.h +++ b/components/spi_flash/include/esp_flash.h @@ -19,6 +19,10 @@ #include "hal/spi_flash_types.h" +#ifdef __cplusplus +extern "C" { +#endif + struct spi_flash_chip_t; typedef struct spi_flash_chip_t spi_flash_chip_t; @@ -50,11 +54,11 @@ typedef struct { Structure must be passed to esp_flash_init() before use. */ struct esp_flash_t { - const spi_flash_chip_t *chip_drv; ///< Pointer to chip-model-specific "adpater" structure. If NULL, will be detected during initialisatiopn. + const spi_flash_chip_t *chip_drv; ///< Pointer to chip-model-specific "adapter" structure. If NULL, will be detected during initialisation. spi_flash_host_driver_t *host; ///< Pointer to hardware-specific "host_driver" structure. - const esp_flash_os_functions_t *os_func; ///< Pointer to os-specific hooker strcuture. - void *os_func_data; ///< Pointer to argument for os-specific hooker. + const esp_flash_os_functions_t *os_func; ///< Pointer to os-specific hook structure. + void *os_func_data; ///< Pointer to argument for os-specific hooks. esp_flash_read_mode_t read_mode; ///< Configured SPI flash read mode. Set before initialisation. uint32_t size; ///< Size of SPI flash in bytes. If 0, size will be detected during initialisation. @@ -65,7 +69,7 @@ struct esp_flash_t { * * This function must be called before any other API functions are called for this chip. * - * @note Only the spi, speed & read_mode fields of the chip structure need to be initialised. Other fields will be auto-detected + * @note Only the host, speed & read_mode fields of the chip structure need to be initialised. Other fields will be auto-detected * if left set to zero or NULL. * * @note If the chip->drv pointer is NULL, chip chip_drv will be autodetected based on its manufacturer & product IDs. See @@ -175,15 +179,14 @@ esp_err_t esp_flash_set_chip_write_protect(esp_flash_t *chip, bool write_protect * * @return ESP_OK on success, or a flash error code if operation failed. */ -esp_err_t -esp_flash_get_protectable_regions(const esp_flash_t *chip, const esp_flash_region_t **regions, uint32_t *num_regions); +esp_err_t esp_flash_get_protectable_regions(const esp_flash_t *chip, const esp_flash_region_t **regions, uint32_t *num_regions); /** @brief Detect if a region of the SPI flash chip is protected * * @param chip Pointer to identify flash chip. Must have been successfully initialised via esp_flash_init() * @param region Pointer to a struct describing a protected region. This must match one of the regions returned from esp_flash_get_protectable_regions(...). - * @param[out] protected Pointer to a flag which is set based on the protected status for this region. + * @param[out] out_protected Pointer to a flag which is set based on the protected status for this region. * * @note It is possible for this result to be false and write operations to still fail, if protection is enabled for the entire chip. * @@ -192,13 +195,13 @@ esp_flash_get_protectable_regions(const esp_flash_t *chip, const esp_flash_regio * * @return ESP_OK on success, or a flash error code if operation failed. */ -esp_err_t esp_flash_get_protected_region(esp_flash_t *chip, const esp_flash_region_t *region, bool *protected); +esp_err_t esp_flash_get_protected_region(esp_flash_t *chip, const esp_flash_region_t *region, bool *out_protected); /** @brief Update the protected status for a region of the SPI flash chip * * @param chip Pointer to identify flash chip. Must have been successfully initialised via esp_flash_init() * @param region Pointer to a struct describing a protected region. This must match one of the regions returned from esp_flash_get_protectable_regions(...). - * @param protected Write protection flag to set. + * @param protect Write protection flag to set. * * @note It is possible for the region protection flag to be cleared and write operations to still fail, if protection is enabled for the entire chip. * @@ -207,7 +210,7 @@ esp_err_t esp_flash_get_protected_region(esp_flash_t *chip, const esp_flash_regi * * @return ESP_OK on success, or a flash error code if operation failed. */ -esp_err_t esp_flash_set_protected_region(esp_flash_t *chip, const esp_flash_region_t *region, bool protected); +esp_err_t esp_flash_set_protected_region(esp_flash_t *chip, const esp_flash_region_t *region, bool protect); /** @brief Read data from the SPI flash chip * @@ -271,24 +274,29 @@ esp_err_t esp_flash_init_default_chip(); /** * Enable OS-level SPI flash protections in IDF * + * Called by OS startup code. You do not need to call this in your own applications. + * * @return ESP_OK if success, otherwise failed. See return value of ``esp_flash_init_os_functions``. */ -esp_err_t esp_flash_app_init(); /* ROM TODO move this to IDF */ +esp_err_t esp_flash_app_init(); /** * Enable OS-level SPI flash for a specific chip. * * @param chip The chip to init os functions. - * @param host_id Which SPI host to use, 0 for SPI1, 1 for HSPI2 and 2 for VSPI. + * @param host_id Which SPI host to use, 1 for SPI1, 2 for SPI2 (HSPI), 3 for SPI3 (VSPI) * - * @return ESP_OK if success, otherwise failed. See return value of ``esp_flash_init_os_functions``. + * @return + * - ESP_OK if success + * - ESP_ERR_INVALID_ARG if host_id is invalid */ esp_err_t esp_flash_init_os_functions(esp_flash_t *chip, int host_id); -/* The default (ie initial boot) no-OS ROM esp_flash_os_functions_t */ -extern const esp_flash_os_functions_t spi1_default_os_functions; //todo: put into non-ROM headers - -/* Pointer to the current esp_flash_os_functions_t structure in use. - Can be changed at runtime to reflect different running conditions. +/** + * The default FreeRTOS-compatible esp_flash_os_functions_t, used for flash chips attached to the SPI1 */ -//extern const esp_flash_os_functions_t *os_func; +extern const esp_flash_os_functions_t esp_flash_spi1_default_os_functions; + +#ifdef __cplusplus +} +#endif diff --git a/components/spi_flash/spi_flash_os_func_app.c b/components/spi_flash/spi_flash_os_func_app.c index 9b9738becf..8b293e5ebf 100644 --- a/components/spi_flash/spi_flash_os_func_app.c +++ b/components/spi_flash/spi_flash_os_func_app.c @@ -89,13 +89,13 @@ static app_func_arg_t spi3_arg = { }; //for SPI1, we have to disable the cache and interrupts before using the SPI bus -const DRAM_ATTR esp_flash_os_functions_t spi1_default_os_functions = { +const DRAM_ATTR esp_flash_os_functions_t esp_flash_spi1_default_os_functions = { .start = spi1_start, .end = spi1_end, .delay_ms = delay_ms, }; -const esp_flash_os_functions_t spi23_default_os_functions = { +const esp_flash_os_functions_t esp_flash_spi23_default_os_functions = { .start = spi23_start, .end = spi23_end, .delay_ms = delay_ms, @@ -105,11 +105,11 @@ esp_err_t esp_flash_init_os_functions(esp_flash_t *chip, int host_id) { if (host_id == 0) { //SPI1 - chip->os_func = &spi1_default_os_functions; + chip->os_func = &esp_flash_spi1_default_os_functions; chip->os_func_data = &spi1_arg; } else if (host_id == 1 || host_id == 2) { //SPI2,3 - chip->os_func = &spi23_default_os_functions; + chip->os_func = &esp_flash_spi23_default_os_functions; chip->os_func_data = (host_id == 1) ? &spi2_arg : &spi3_arg; } else { return ESP_ERR_INVALID_ARG; From cf2ba210ef028ecc563b04d403b37e34e71b470f Mon Sep 17 00:00:00 2001 From: chenjianqiang Date: Fri, 14 Jun 2019 11:01:30 +0800 Subject: [PATCH 081/486] uart: multichip support --- components/driver/include/driver/uart.h | 30 ++++++++-------- components/driver/uart.c | 26 ++++++++++++-- .../common/include/esp_modbus_common.h | 2 +- components/freemodbus/port/portserial.c | 2 +- components/freemodbus/port/portserial_m.c | 2 +- .../modbus_controller/mbc_serial_master.c | 2 +- .../modbus_controller/mbc_serial_slave.c | 2 +- components/soc/esp32/include/soc/uart_caps.h | 26 ++++++++++++++ components/soc/include/soc/uart_periph.h | 2 ++ components/vfs/vfs_uart.c | 35 +++++++++++++++---- 10 files changed, 101 insertions(+), 28 deletions(-) create mode 100644 components/soc/esp32/include/soc/uart_caps.h diff --git a/components/driver/include/driver/uart.h b/components/driver/include/driver/uart.h index 4f0c8c1ca1..4bf855c186 100644 --- a/components/driver/include/driver/uart.h +++ b/components/driver/include/driver/uart.h @@ -1,9 +1,9 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD // // 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 @@ -12,15 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef _DRIVER_UART_H_ -#define _DRIVER_UART_H_ - +#pragma once #ifdef __cplusplus extern "C" { #endif #include "soc/uart_periph.h" +#include "soc/uart_caps.h" #include "esp_err.h" #include "esp_intr_alloc.h" #include "driver/periph_ctrl.h" @@ -82,7 +81,9 @@ typedef enum { typedef enum { UART_NUM_0 = 0x0, /*!< UART base address 0x3ff40000*/ UART_NUM_1 = 0x1, /*!< UART base address 0x3ff50000*/ +#if SOC_UART_NUM > 2 UART_NUM_2 = 0x2, /*!< UART base address 0x3ff6e000*/ +#endif UART_NUM_MAX, } uart_port_t; @@ -257,7 +258,7 @@ esp_err_t uart_get_baudrate(uart_port_t uart_num, uint32_t* baudrate); * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * @param inverse_mask Choose the wires that need to be inverted. - * Inverse_mask should be chosen from + * Inverse_mask should be chosen from * UART_INVERSE_RXD / UART_INVERSE_TXD / UART_INVERSE_RTS / UART_INVERSE_CTS, * combined with OR operation. * @@ -401,7 +402,7 @@ esp_err_t uart_enable_tx_intr(uart_port_t uart_num, int enable, int thresh); * @param fn Interrupt handler function. * @param arg parameter for handler function * @param intr_alloc_flags Flags used to allocate the interrupt. One or multiple (ORred) - * ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info. + * ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info. * @param handle Pointer to return handle. If non-NULL, a handle for the interrupt will * be returned here. * @@ -429,7 +430,7 @@ esp_err_t uart_isr_free(uart_port_t uart_num); * @note Internal signal can be output to multiple GPIO pads. * Only one GPIO pad can connect with input signal. * - * @note Instead of GPIO number a macro 'UART_PIN_NO_CHANGE' may be provided + * @note Instead of GPIO number a macro 'UART_PIN_NO_CHANGE' may be provided to keep the currently allocated pin. * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 @@ -556,7 +557,7 @@ esp_err_t uart_wait_tx_done(uart_port_t uart_num, TickType_t ticks_to_wait); /** * @brief Send data to the UART port from a given buffer and length. - * + * * This function will not wait for enough space in TX FIFO. It will just fill the available TX FIFO and return when the FIFO is full. * @note This function should only be used when UART TX buffer is not enabled. * @@ -748,10 +749,10 @@ int uart_pattern_get_pos(uart_port_t uart_num); esp_err_t uart_pattern_queue_reset(uart_port_t uart_num, int queue_length); /** - * @brief UART set communication mode + * @brief UART set communication mode * @note This function must be executed after uart_driver_install(), when the driver object is initialized. * @param uart_num Uart number to configure - * @param mode UART UART mode to set + * @param mode UART UART mode to set * * @return * - ESP_OK Success @@ -763,8 +764,8 @@ esp_err_t uart_set_mode(uart_port_t uart_num, uart_mode_t mode); * @brief UART set threshold timeout for TOUT feature * * @param uart_num Uart number to configure - * @param tout_thresh This parameter defines timeout threshold in uart symbol periods. The maximum value of threshold is 126. - * tout_thresh = 1, defines TOUT interrupt timeout equal to transmission time of one symbol (~11 bit) on current baudrate. + * @param tout_thresh This parameter defines timeout threshold in uart symbol periods. The maximum value of threshold is 126. + * tout_thresh = 1, defines TOUT interrupt timeout equal to transmission time of one symbol (~11 bit) on current baudrate. * If the time is expired the UART_RXFIFO_TOUT_INT interrupt is triggered. If tout_thresh == 0, * the TOUT feature is disabled. * @@ -785,7 +786,7 @@ esp_err_t uart_set_rx_timeout(uart_port_t uart_num, const uint8_t tout_thresh); * @param collision_flag Pointer to variable of type bool to return collision flag. * * @return - * - ESP_OK Success + * - ESP_OK Success * - ESP_ERR_INVALID_ARG Parameter error */ esp_err_t uart_get_collision_flag(uart_port_t uart_num, bool* collision_flag); @@ -844,4 +845,3 @@ esp_err_t uart_get_wakeup_threshold(uart_port_t uart_num, int* out_wakeup_thresh } #endif -#endif /*_DRIVER_UART_H_*/ diff --git a/components/driver/uart.c b/components/driver/uart.c index b9d222f567..9a26f6423f 100644 --- a/components/driver/uart.c +++ b/components/driver/uart.c @@ -29,6 +29,8 @@ #include "driver/gpio.h" #include "driver/uart_select.h" +#define UART_NUM SOC_UART_NUM + #define XOFF (char)0x13 #define XON (char)0x11 @@ -113,8 +115,20 @@ typedef struct { static uart_obj_t *p_uart_obj[UART_NUM_MAX] = {0}; /* DRAM_ATTR is required to avoid UART array placed in flash, due to accessed from ISR */ -static DRAM_ATTR uart_dev_t* const UART[UART_NUM_MAX] = {&UART0, &UART1, &UART2}; -static portMUX_TYPE uart_spinlock[UART_NUM_MAX] = {portMUX_INITIALIZER_UNLOCKED, portMUX_INITIALIZER_UNLOCKED, portMUX_INITIALIZER_UNLOCKED}; +static DRAM_ATTR uart_dev_t* const UART[UART_NUM_MAX] = { + &UART0, + &UART1, +#if UART_NUM > 2 + &UART2 +#endif +}; +static portMUX_TYPE uart_spinlock[UART_NUM_MAX] = { + portMUX_INITIALIZER_UNLOCKED, + portMUX_INITIALIZER_UNLOCKED, +#if UART_NUM > 2 + portMUX_INITIALIZER_UNLOCKED +#endif +}; static portMUX_TYPE uart_selectlock = portMUX_INITIALIZER_UNLOCKED; esp_err_t uart_set_word_length(uart_port_t uart_num, uart_word_length_t data_bit) @@ -528,9 +542,11 @@ esp_err_t uart_isr_register(uart_port_t uart_num, void (*fn)(void*), void * arg, case UART_NUM_1: ret=esp_intr_alloc(ETS_UART1_INTR_SOURCE, intr_alloc_flags, fn, arg, handle); break; +#if UART_NUM > 2 case UART_NUM_2: ret=esp_intr_alloc(ETS_UART2_INTR_SOURCE, intr_alloc_flags, fn, arg, handle); break; +#endif case UART_NUM_0: default: ret=esp_intr_alloc(ETS_UART0_INTR_SOURCE, intr_alloc_flags, fn, arg, handle); @@ -577,12 +593,14 @@ esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int r rts_sig = U1RTS_OUT_IDX; cts_sig = U1CTS_IN_IDX; break; +#if UART_NUM > 2 case UART_NUM_2: tx_sig = U2TXD_OUT_IDX; rx_sig = U2RXD_IN_IDX; rts_sig = U2RTS_OUT_IDX; cts_sig = U2CTS_IN_IDX; break; +#endif case UART_NUM_MAX: default: tx_sig = U0TXD_OUT_IDX; @@ -656,8 +674,10 @@ esp_err_t uart_param_config(uart_port_t uart_num, const uart_config_t *uart_conf periph_module_enable(PERIPH_UART0_MODULE); } else if(uart_num == UART_NUM_1) { periph_module_enable(PERIPH_UART1_MODULE); +#if UART_NUM > 2 } else if(uart_num == UART_NUM_2) { periph_module_enable(PERIPH_UART2_MODULE); +#endif } r = uart_set_hw_flow_ctrl(uart_num, uart_config->flow_ctrl, uart_config->rx_flow_ctrl_thresh); if (r != ESP_OK) return r; @@ -1460,8 +1480,10 @@ esp_err_t uart_driver_delete(uart_port_t uart_num) periph_module_disable(PERIPH_UART0_MODULE); } else if(uart_num == UART_NUM_1) { periph_module_disable(PERIPH_UART1_MODULE); +#if UART_NUM > 2 } else if(uart_num == UART_NUM_2) { periph_module_disable(PERIPH_UART2_MODULE); +#endif } } return ESP_OK; diff --git a/components/freemodbus/common/include/esp_modbus_common.h b/components/freemodbus/common/include/esp_modbus_common.h index 7bec02476a..cd171c7557 100644 --- a/components/freemodbus/common/include/esp_modbus_common.h +++ b/components/freemodbus/common/include/esp_modbus_common.h @@ -24,7 +24,7 @@ // Default port defines #define MB_DEVICE_ADDRESS (1) // Default slave device address in Modbus #define MB_DEVICE_SPEED (115200) // Default Modbus speed for now hard defined -#define MB_UART_PORT (UART_NUM_2) // Default UART port number +#define MB_UART_PORT (UART_NUM_MAX - 1) // Default UART port number #define MB_PAR_INFO_TOUT (10) // Timeout for get parameter info #define MB_PARITY_NONE (UART_PARITY_DISABLE) diff --git a/components/freemodbus/port/portserial.c b/components/freemodbus/port/portserial.c index 54bbe8318b..10967af7ab 100644 --- a/components/freemodbus/port/portserial.c +++ b/components/freemodbus/port/portserial.c @@ -78,7 +78,7 @@ static TaskHandle_t xMbTaskHandle; static const CHAR *TAG = "MB_SERIAL"; // The UART hardware port number -static UCHAR ucUartNumber = UART_NUM_2; +static UCHAR ucUartNumber = UART_NUM_MAX - 1; static BOOL bRxStateEnabled = FALSE; // Receiver enabled flag static BOOL bTxStateEnabled = FALSE; // Transmitter enabled flag diff --git a/components/freemodbus/port/portserial_m.c b/components/freemodbus/port/portserial_m.c index 74a4a1be04..330269101a 100644 --- a/components/freemodbus/port/portserial_m.c +++ b/components/freemodbus/port/portserial_m.c @@ -73,7 +73,7 @@ static QueueHandle_t xMbUartQueue; static TaskHandle_t xMbTaskHandle; // The UART hardware port number -static UCHAR ucUartNumber = UART_NUM_2; +static UCHAR ucUartNumber = UART_NUM_MAX - 1; static BOOL bRxStateEnabled = FALSE; // Receiver enabled flag static BOOL bTxStateEnabled = FALSE; // Transmitter enabled flag diff --git a/components/freemodbus/serial_master/modbus_controller/mbc_serial_master.c b/components/freemodbus/serial_master/modbus_controller/mbc_serial_master.c index 8db15aea41..30aae200fc 100644 --- a/components/freemodbus/serial_master/modbus_controller/mbc_serial_master.c +++ b/components/freemodbus/serial_master/modbus_controller/mbc_serial_master.c @@ -83,7 +83,7 @@ static esp_err_t mbc_serial_master_setup(void* comm_info) MB_MASTER_CHECK(((comm_info_ptr->mode == MB_MODE_RTU) || (comm_info_ptr->mode == MB_MODE_ASCII)), ESP_ERR_INVALID_ARG, "mb incorrect mode = (0x%x).", (uint32_t)comm_info_ptr->mode); - MB_MASTER_CHECK((comm_info_ptr->port <= UART_NUM_2), ESP_ERR_INVALID_ARG, + MB_MASTER_CHECK((comm_info_ptr->port < UART_NUM_MAX), ESP_ERR_INVALID_ARG, "mb wrong port to set = (0x%x).", (uint32_t)comm_info_ptr->port); MB_MASTER_CHECK((comm_info_ptr->parity <= UART_PARITY_EVEN), ESP_ERR_INVALID_ARG, "mb wrong parity option = (0x%x).", (uint32_t)comm_info_ptr->parity); diff --git a/components/freemodbus/serial_slave/modbus_controller/mbc_serial_slave.c b/components/freemodbus/serial_slave/modbus_controller/mbc_serial_slave.c index 86e9616d02..72d753e520 100644 --- a/components/freemodbus/serial_slave/modbus_controller/mbc_serial_slave.c +++ b/components/freemodbus/serial_slave/modbus_controller/mbc_serial_slave.c @@ -69,7 +69,7 @@ static esp_err_t mbc_serial_slave_setup(void* comm_info) MB_SLAVE_CHECK((comm_settings->slave_addr <= MB_ADDRESS_MAX), ESP_ERR_INVALID_ARG, "mb wrong slave address = (0x%x).", (uint32_t)comm_settings->slave_addr); - MB_SLAVE_CHECK((comm_settings->port <= UART_NUM_2), ESP_ERR_INVALID_ARG, + MB_SLAVE_CHECK((comm_settings->port < UART_NUM_MAX), ESP_ERR_INVALID_ARG, "mb wrong port to set = (0x%x).", (uint32_t)comm_settings->port); MB_SLAVE_CHECK((comm_settings->parity <= UART_PARITY_EVEN), ESP_ERR_INVALID_ARG, "mb wrong parity option = (0x%x).", (uint32_t)comm_settings->parity); diff --git a/components/soc/esp32/include/soc/uart_caps.h b/components/soc/esp32/include/soc/uart_caps.h new file mode 100644 index 0000000000..4234b4b133 --- /dev/null +++ b/components/soc/esp32/include/soc/uart_caps.h @@ -0,0 +1,26 @@ +// Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD +// +// 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 + +#ifdef __cplusplus +extern "C" { +#endif + +#define SOC_UART_NUM 3 + +#ifdef __cplusplus +} +#endif + diff --git a/components/soc/include/soc/uart_periph.h b/components/soc/include/soc/uart_periph.h index d149048aa1..27324cd1d5 100644 --- a/components/soc/include/soc/uart_periph.h +++ b/components/soc/include/soc/uart_periph.h @@ -13,6 +13,8 @@ // limitations under the License. #pragma once + +#include "soc/uart_caps.h" #include "soc/uart_reg.h" #include "soc/uart_struct.h" #include "soc/uart_channel.h" diff --git a/components/vfs/vfs_uart.c b/components/vfs/vfs_uart.c index 147c54cb70..7b0f3d3fb7 100644 --- a/components/vfs/vfs_uart.c +++ b/components/vfs/vfs_uart.c @@ -28,7 +28,7 @@ #include "esp32/rom/uart.h" // TODO: make the number of UARTs chip dependent -#define UART_NUM 3 +#define UART_NUM SOC_UART_NUM // Token signifying that no character is available #define NONE -1 @@ -47,12 +47,27 @@ static void uart_tx_char_via_driver(int fd, int c); static int uart_rx_char_via_driver(int fd); // Pointers to UART peripherals -static uart_dev_t* s_uarts[UART_NUM] = {&UART0, &UART1, &UART2}; +static uart_dev_t* s_uarts[UART_NUM] = { + &UART0, + &UART1, +#if UART_NUM > 2 + &UART2 +#endif +}; + +// One-character buffer used for newline conversion code, per UART +static int s_peek_char[UART_NUM] = { + NONE, + NONE, +#if UART_NUM > 2 + NONE +#endif +}; + // per-UART locks, lazily initialized static _lock_t s_uart_read_locks[UART_NUM]; static _lock_t s_uart_write_locks[UART_NUM]; -// One-character buffer used for newline conversion code, per UART -static int s_peek_char[UART_NUM] = { NONE, NONE, NONE }; + // Per-UART non-blocking flag. Note: default implementation does not honor this // flag, all reads are non-blocking. This option becomes effective if UART // driver is used. @@ -94,12 +109,20 @@ static void uart_end_select(); // Functions used to write bytes to UART. Default to "basic" functions. static tx_func_t s_uart_tx_func[UART_NUM] = { - &uart_tx_char, &uart_tx_char, &uart_tx_char + &uart_tx_char, + &uart_tx_char, +#if UART_NUM > 2 + &uart_tx_char +#endif }; // Functions used to read bytes from UART. Default to "basic" functions. static rx_func_t s_uart_rx_func[UART_NUM] = { - &uart_rx_char, &uart_rx_char, &uart_rx_char + &uart_rx_char, + &uart_rx_char, +#if UART_NUM > 2 + &uart_rx_char +#endif }; From b5c3ac0ec23b59db3da88f8085019e8f934d0c70 Mon Sep 17 00:00:00 2001 From: "Michael (XIAO Xufeng)" Date: Thu, 20 Jun 2019 01:18:20 +0800 Subject: [PATCH 082/486] vfs_uart: refactor to have static context structure --- components/vfs/vfs_uart.c | 202 +++++++++++++++++++------------------- 1 file changed, 102 insertions(+), 100 deletions(-) diff --git a/components/vfs/vfs_uart.c b/components/vfs/vfs_uart.c index 7b0f3d3fb7..2bb865b297 100644 --- a/components/vfs/vfs_uart.c +++ b/components/vfs/vfs_uart.c @@ -33,6 +33,22 @@ // Token signifying that no character is available #define NONE -1 +#if CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF +# define DEFAULT_TX_MODE ESP_LINE_ENDINGS_CRLF +#elif CONFIG_NEWLIB_STDOUT_LINE_ENDING_CR +# define DEFAULT_TX_MODE ESP_LINE_ENDINGS_CR +#else +# define DEFAULT_TX_MODE ESP_LINE_ENDINGS_LF +#endif + +#if CONFIG_NEWLIB_STDIN_LINE_ENDING_CRLF +# define DEFAULT_RX_MODE ESP_LINE_ENDINGS_CRLF +#elif CONFIG_NEWLIB_STDIN_LINE_ENDING_CR +# define DEFAULT_RX_MODE ESP_LINE_ENDINGS_CR +#else +# define DEFAULT_RX_MODE ESP_LINE_ENDINGS_LF +#endif + // UART write bytes function type typedef void (*tx_func_t)(int, int); // UART read bytes function type @@ -46,33 +62,55 @@ static int uart_rx_char(int fd); static void uart_tx_char_via_driver(int fd, int c); static int uart_rx_char_via_driver(int fd); -// Pointers to UART peripherals -static uart_dev_t* s_uarts[UART_NUM] = { - &UART0, - &UART1, +typedef struct { + // Pointers to UART peripherals + uart_dev_t* uart; + // One-character buffer used for newline conversion code, per UART + int peek_char; + // per-UART locks, lazily initialized + _lock_t read_lock; + _lock_t write_lock; + // Per-UART non-blocking flag. Note: default implementation does not honor this + // flag, all reads are non-blocking. This option becomes effective if UART + // driver is used. + bool non_blocking; + // Newline conversion mode when transmitting + esp_line_endings_t tx_mode; + // Newline conversion mode when receiving + esp_line_endings_t rx_mode; + // Functions used to write bytes to UART. Default to "basic" functions. + tx_func_t tx_func; + // Functions used to read bytes from UART. Default to "basic" functions. + rx_func_t rx_func; +} vfs_uart_context_t; + +#define VFS_CTX_DEFAULT_VAL(uart_dev) (vfs_uart_context_t) {\ + .uart = (uart_dev),\ + .peek_char = NONE,\ + .tx_mode = DEFAULT_TX_MODE,\ + .rx_mode = DEFAULT_RX_MODE,\ + .tx_func = uart_tx_char,\ + .rx_func = uart_rx_char,\ +} + +//If the context should be dynamically initialized, remove this structure +//and point s_ctx to allocated data. +static vfs_uart_context_t s_context[UART_NUM] = { + VFS_CTX_DEFAULT_VAL(&UART0), + VFS_CTX_DEFAULT_VAL(&UART1), #if UART_NUM > 2 - &UART2 + VFS_CTX_DEFAULT_VAL(&UART2), #endif }; -// One-character buffer used for newline conversion code, per UART -static int s_peek_char[UART_NUM] = { - NONE, - NONE, +static vfs_uart_context_t* s_ctx[UART_NUM] = { + &s_context[0], + &s_context[1], #if UART_NUM > 2 - NONE + &s_context[2], #endif }; -// per-UART locks, lazily initialized -static _lock_t s_uart_read_locks[UART_NUM]; -static _lock_t s_uart_write_locks[UART_NUM]; - -// Per-UART non-blocking flag. Note: default implementation does not honor this -// flag, all reads are non-blocking. This option becomes effective if UART -// driver is used. -static bool s_non_blocking[UART_NUM]; - /* Lock ensuring that uart_select is used from only one task at the time */ static _lock_t s_one_select_lock; @@ -84,47 +122,9 @@ static fd_set *_readfds_orig = NULL; static fd_set *_writefds_orig = NULL; static fd_set *_errorfds_orig = NULL; -// Newline conversion mode when transmitting -static esp_line_endings_t s_tx_mode = -#if CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF - ESP_LINE_ENDINGS_CRLF; -#elif CONFIG_NEWLIB_STDOUT_LINE_ENDING_CR - ESP_LINE_ENDINGS_CR; -#else - ESP_LINE_ENDINGS_LF; -#endif - -// Newline conversion mode when receiving -static esp_line_endings_t s_rx_mode[UART_NUM] = { [0 ... UART_NUM-1] = -#if CONFIG_NEWLIB_STDIN_LINE_ENDING_CRLF - ESP_LINE_ENDINGS_CRLF -#elif CONFIG_NEWLIB_STDIN_LINE_ENDING_CR - ESP_LINE_ENDINGS_CR -#else - ESP_LINE_ENDINGS_LF -#endif -}; static void uart_end_select(); -// Functions used to write bytes to UART. Default to "basic" functions. -static tx_func_t s_uart_tx_func[UART_NUM] = { - &uart_tx_char, - &uart_tx_char, -#if UART_NUM > 2 - &uart_tx_char -#endif -}; - -// Functions used to read bytes from UART. Default to "basic" functions. -static rx_func_t s_uart_rx_func[UART_NUM] = { - &uart_rx_char, - &uart_rx_char, -#if UART_NUM > 2 - &uart_rx_char -#endif -}; - static int uart_open(const char * path, int flags, int mode) { @@ -143,14 +143,14 @@ static int uart_open(const char * path, int flags, int mode) return fd; } - s_non_blocking[fd] = ((flags & O_NONBLOCK) == O_NONBLOCK); + s_ctx[fd]->non_blocking = ((flags & O_NONBLOCK) == O_NONBLOCK); return fd; } static void uart_tx_char(int fd, int c) { - uart_dev_t* uart = s_uarts[fd]; + uart_dev_t* uart = s_ctx[fd]->uart; while (uart->status.txfifo_cnt >= 127) { ; } @@ -165,7 +165,7 @@ static void uart_tx_char_via_driver(int fd, int c) static int uart_rx_char(int fd) { - uart_dev_t* uart = s_uarts[fd]; + uart_dev_t* uart = s_ctx[fd]->uart; if (uart->status.rxfifo_cnt == 0) { return NONE; } @@ -175,7 +175,7 @@ static int uart_rx_char(int fd) static int uart_rx_char_via_driver(int fd) { uint8_t c; - int timeout = s_non_blocking[fd] ? 0 : portMAX_DELAY; + int timeout = s_ctx[fd]->non_blocking ? 0 : portMAX_DELAY; int n = uart_read_bytes(fd, &c, 1, timeout); if (n <= 0) { return NONE; @@ -191,18 +191,18 @@ static ssize_t uart_write(int fd, const void * data, size_t size) * a dedicated UART lock if two streams (stdout and stderr) point to the * same UART. */ - _lock_acquire_recursive(&s_uart_write_locks[fd]); + _lock_acquire_recursive(&s_ctx[fd]->write_lock); for (size_t i = 0; i < size; i++) { int c = data_c[i]; - if (c == '\n' && s_tx_mode != ESP_LINE_ENDINGS_LF) { - s_uart_tx_func[fd](fd, '\r'); - if (s_tx_mode == ESP_LINE_ENDINGS_CR) { + if (c == '\n' && s_ctx[fd]->tx_mode != ESP_LINE_ENDINGS_LF) { + s_ctx[fd]->tx_func(fd, '\r'); + if (s_ctx[fd]->tx_mode == ESP_LINE_ENDINGS_CR) { continue; } } - s_uart_tx_func[fd](fd, c); + s_ctx[fd]->tx_func(fd, c); } - _lock_release_recursive(&s_uart_write_locks[fd]); + _lock_release_recursive(&s_ctx[fd]->write_lock); return size; } @@ -213,19 +213,19 @@ static ssize_t uart_write(int fd, const void * data, size_t size) static int uart_read_char(int fd) { /* return character from peek buffer, if it is there */ - if (s_peek_char[fd] != NONE) { - int c = s_peek_char[fd]; - s_peek_char[fd] = NONE; + if (s_ctx[fd]->peek_char != NONE) { + int c = s_ctx[fd]->peek_char; + s_ctx[fd]->peek_char = NONE; return c; } - return s_uart_rx_func[fd](fd); + return s_ctx[fd]->rx_func(fd); } /* Push back a character; it will be returned by next call to uart_read_char */ static void uart_return_char(int fd, int c) { - assert(s_peek_char[fd] == NONE); - s_peek_char[fd] = c; + assert(s_ctx[fd]->peek_char == NONE); + s_ctx[fd]->peek_char = c; } static ssize_t uart_read(int fd, void* data, size_t size) @@ -233,13 +233,13 @@ static ssize_t uart_read(int fd, void* data, size_t size) assert(fd >=0 && fd < 3); char *data_c = (char *) data; size_t received = 0; - _lock_acquire_recursive(&s_uart_read_locks[fd]); + _lock_acquire_recursive(&s_ctx[fd]->read_lock); while (received < size) { int c = uart_read_char(fd); if (c == '\r') { - if (s_rx_mode[fd] == ESP_LINE_ENDINGS_CR) { + if (s_ctx[fd]->rx_mode == ESP_LINE_ENDINGS_CR) { c = '\n'; - } else if (s_rx_mode[fd] == ESP_LINE_ENDINGS_CRLF) { + } else if (s_ctx[fd]->rx_mode == ESP_LINE_ENDINGS_CRLF) { /* look ahead */ int c2 = uart_read_char(fd); if (c2 == NONE) { @@ -266,7 +266,7 @@ static ssize_t uart_read(int fd, void* data, size_t size) break; } } - _lock_release_recursive(&s_uart_read_locks[fd]); + _lock_release_recursive(&s_ctx[fd]->read_lock); if (received > 0) { return received; } @@ -292,11 +292,11 @@ static int uart_fcntl(int fd, int cmd, int arg) assert(fd >=0 && fd < 3); int result = 0; if (cmd == F_GETFL) { - if (s_non_blocking[fd]) { + if (s_ctx[fd]->non_blocking) { result |= O_NONBLOCK; } } else if (cmd == F_SETFL) { - s_non_blocking[fd] = (arg & O_NONBLOCK) != 0; + s_ctx[fd]->non_blocking = (arg & O_NONBLOCK) != 0; } else { // unsupported operation result = -1; @@ -329,9 +329,9 @@ static int uart_access(const char *path, int amode) static int uart_fsync(int fd) { assert(fd >= 0 && fd < 3); - _lock_acquire_recursive(&s_uart_write_locks[fd]); + _lock_acquire_recursive(&s_ctx[fd]->write_lock); uart_tx_wait_idle((uint8_t) fd); - _lock_release_recursive(&s_uart_write_locks[fd]); + _lock_release_recursive(&s_ctx[fd]->write_lock); return 0; } @@ -500,11 +500,11 @@ static int uart_tcsetattr(int fd, int optional_actions, const struct termios *p) } if (p->c_iflag & IGNCR) { - s_rx_mode[fd] = ESP_LINE_ENDINGS_CRLF; + s_ctx[fd]->rx_mode = ESP_LINE_ENDINGS_CRLF; } else if (p->c_iflag & ICRNL) { - s_rx_mode[fd] = ESP_LINE_ENDINGS_CR; + s_ctx[fd]->rx_mode = ESP_LINE_ENDINGS_CR; } else { - s_rx_mode[fd] = ESP_LINE_ENDINGS_LF; + s_ctx[fd]->rx_mode = ESP_LINE_ENDINGS_LF; } // output line endings are not supported because there is no alternative in termios for converting LF to CR @@ -683,9 +683,9 @@ static int uart_tcgetattr(int fd, struct termios *p) memset(p, 0, sizeof(struct termios)); - if (s_rx_mode[fd] == ESP_LINE_ENDINGS_CRLF) { + if (s_ctx[fd]->rx_mode == ESP_LINE_ENDINGS_CRLF) { p->c_iflag |= IGNCR; - } else if (s_rx_mode[fd] == ESP_LINE_ENDINGS_CR) { + } else if (s_ctx[fd]->rx_mode == ESP_LINE_ENDINGS_CR) { p->c_iflag |= ICRNL; } @@ -942,31 +942,33 @@ void esp_vfs_dev_uart_register() void esp_vfs_dev_uart_set_rx_line_endings(esp_line_endings_t mode) { for (int i = 0; i < UART_NUM; ++i) { - s_rx_mode[i] = mode; + s_ctx[i]->rx_mode = mode; } } void esp_vfs_dev_uart_set_tx_line_endings(esp_line_endings_t mode) { - s_tx_mode = mode; + for (int i = 0; i < UART_NUM; ++i) { + s_ctx[i]->tx_mode = mode; + } } void esp_vfs_dev_uart_use_nonblocking(int uart_num) { - _lock_acquire_recursive(&s_uart_read_locks[uart_num]); - _lock_acquire_recursive(&s_uart_write_locks[uart_num]); - s_uart_tx_func[uart_num] = uart_tx_char; - s_uart_rx_func[uart_num] = uart_rx_char; - _lock_release_recursive(&s_uart_write_locks[uart_num]); - _lock_release_recursive(&s_uart_read_locks[uart_num]); + _lock_acquire_recursive(&s_ctx[uart_num]->read_lock); + _lock_acquire_recursive(&s_ctx[uart_num]->write_lock); + s_ctx[uart_num]->tx_func = uart_tx_char; + s_ctx[uart_num]->rx_func = uart_rx_char; + _lock_release_recursive(&s_ctx[uart_num]->write_lock); + _lock_release_recursive(&s_ctx[uart_num]->read_lock); } void esp_vfs_dev_uart_use_driver(int uart_num) { - _lock_acquire_recursive(&s_uart_read_locks[uart_num]); - _lock_acquire_recursive(&s_uart_write_locks[uart_num]); - s_uart_tx_func[uart_num] = uart_tx_char_via_driver; - s_uart_rx_func[uart_num] = uart_rx_char_via_driver; - _lock_release_recursive(&s_uart_write_locks[uart_num]); - _lock_release_recursive(&s_uart_read_locks[uart_num]); + _lock_acquire_recursive(&s_ctx[uart_num]->read_lock); + _lock_acquire_recursive(&s_ctx[uart_num]->write_lock); + s_ctx[uart_num]->tx_func = uart_tx_char_via_driver; + s_ctx[uart_num]->rx_func = uart_rx_char_via_driver; + _lock_release_recursive(&s_ctx[uart_num]->write_lock); + _lock_release_recursive(&s_ctx[uart_num]->read_lock); } From 21cce564bad7de6e7162de1a1ac0d08145de175b Mon Sep 17 00:00:00 2001 From: baohongde Date: Mon, 17 Sep 2018 16:44:38 +0800 Subject: [PATCH 083/486] component/bt: Macor from `sdkconfig.h` is used only in `bt_user_config.h` --- components/bt/bluedroid/api/esp_bt_main.c | 2 +- components/bt/bluedroid/btc/core/btc_task.c | 23 +- .../bt/bluedroid/btc/include/btc/btc_task.h | 6 +- .../btc/profile/std/a2dp/btc_a2dp_sink.c | 4 +- .../btc/profile/std/a2dp/btc_a2dp_source.c | 4 +- .../btc/profile/std/hf_client/btc_hf_client.c | 2 +- .../common/include/common/bt_target.h | 185 +++++---- .../common/include/common/bt_trace.h | 157 ++------ .../common/include/common/bt_user_config.h | 372 ++++++++++++++++++ components/bt/bluedroid/hci/hci_hal_h4.c | 3 +- components/bt/bluedroid/hci/hci_layer.c | 3 +- components/bt/bluedroid/osi/allocator.c | 31 +- .../bt/bluedroid/osi/include/osi/allocator.h | 21 +- components/bt/bluedroid/stack/btm/btm_sec.c | 96 ++--- components/bt/bluedroid/stack/btu/btu_init.c | 5 +- .../bluedroid/stack/include/stack/dyn_mem.h | 8 +- 16 files changed, 592 insertions(+), 330 deletions(-) create mode 100644 components/bt/bluedroid/common/include/common/bt_user_config.h diff --git a/components/bt/bluedroid/api/esp_bt_main.c b/components/bt/bluedroid/api/esp_bt_main.c index 1620ae9d80..0b1bcc6ab0 100644 --- a/components/bt/bluedroid/api/esp_bt_main.c +++ b/components/bt/bluedroid/api/esp_bt_main.c @@ -128,7 +128,7 @@ esp_err_t esp_bluedroid_init(void) return ESP_ERR_INVALID_STATE; } -#ifdef CONFIG_BT_BLUEDROID_MEM_DEBUG +#if HEAP_MEMORY_DEBUG osi_mem_dbg_init(); #endif diff --git a/components/bt/bluedroid/btc/core/btc_task.c b/components/bt/bluedroid/btc/core/btc_task.c index 2787bc2056..8b787405bc 100644 --- a/components/bt/bluedroid/btc/core/btc_task.c +++ b/components/bt/bluedroid/btc/core/btc_task.c @@ -30,7 +30,7 @@ #include "btc/btc_dm.h" #include "btc/btc_alarm.h" #include "bta/bta_gatt_api.h" -#if CONFIG_BT_CLASSIC_ENABLED +#if CLASSIC_BT_INCLUDED #include "btc/btc_profile_queue.h" #if (BTC_GAP_BT_INCLUDED == TRUE) #include "btc_gap_bt.h" @@ -39,19 +39,18 @@ #include "btc_av.h" #include "btc_avrc.h" #endif /* #if BTC_AV_INCLUDED */ -#if CONFIG_BT_SPP_ENABLED +#if (BTC_SPP_INCLUDED == TRUE) #include "btc_spp.h" -#endif /* #if CONFIG_BT_SPP_ENABLED */ +#endif /* #if (BTC_SPP_INCLUDED == TRUE) */ #if BTC_HF_CLIENT_INCLUDED #include "btc_hf_client.h" #endif /* #if BTC_HF_CLIENT_INCLUDED */ -#endif /* #if CONFIG_BT_CLASSIC_ENABLED */ +#endif /* #if CLASSIC_BT_INCLUDED */ #define BTC_TASK_PINNED_TO_CORE (TASK_PINNED_TO_CORE) -#define BTC_TASK_STACK_SIZE (CONFIG_BTC_TASK_STACK_SIZE + BT_TASK_EXTRA_STACK_SIZE) //by menuconfig +#define BTC_TASK_STACK_SIZE (BT_BTC_TASK_STACK_SIZE + BT_TASK_EXTRA_STACK_SIZE) //by menuconfig #define BTC_TASK_NAME "btcT" -#define BTC_TASK_PRIO (configMAX_PRIORITIES - 6) -#define BTC_TASK_QUEUE_LEN 60 +#define BTC_TASK_PRIO (BT_TASK_MAX_PRIORITIES - 6) static osi_thread_t *btc_thread; @@ -75,7 +74,7 @@ static btc_func_t profile_tab[BTC_PID_NUM] = { #endif ///GATTS_INCLUDED == TRUE [BTC_PID_DM_SEC] = {NULL, btc_dm_sec_cb_handler }, [BTC_PID_ALARM] = {btc_alarm_handler, NULL }, -#if CONFIG_BT_CLASSIC_ENABLED +#if CLASSIC_BT_INCLUDED #if (BTC_GAP_BT_INCLUDED == TRUE) [BTC_PID_GAP_BT] = {btc_gap_bt_call_handler, btc_gap_bt_cb_handler }, #endif /* (BTC_GAP_BT_INCLUDED == TRUE) */ @@ -85,13 +84,13 @@ static btc_func_t profile_tab[BTC_PID_NUM] = { [BTC_PID_AVRC_CT] = {btc_avrc_ct_call_handler, NULL }, [BTC_PID_AVRC_TG] = {btc_avrc_tg_call_handler, NULL }, #endif /* #if BTC_AV_INCLUDED */ -#if CONFIG_BT_SPP_ENABLED +#if (BTC_SPP_INCLUDED == TRUE) [BTC_PID_SPP] = {btc_spp_call_handler, btc_spp_cb_handler }, -#endif /* #if CONFIG_BT_SPP_ENABLED */ +#endif /* #if (BTC_SPP_INCLUDED == TRUE) */ #if BTC_HF_CLIENT_INCLUDED [BTC_PID_HF_CLIENT] = {btc_hf_client_call_handler, btc_hf_client_cb_handler}, #endif /* #if BTC_HF_CLIENT_INCLUDED */ -#endif /* #if CONFIG_BT_CLASSIC_ENABLED */ +#endif /* #if CLASSIC_BT_INCLUDED */ }; /***************************************************************************** @@ -122,7 +121,7 @@ static void btc_thread_handler(void *arg) osi_free(msg); } -static bt_status_t btc_task_post(btc_msg_t *msg, osi_thread_blocking_t blocking) +static bt_status_t btc_task_post(btc_msg_t *msg, osi_thread_blocking_t blocking) { btc_msg_t *lmsg; diff --git a/components/bt/bluedroid/btc/include/btc/btc_task.h b/components/bt/bluedroid/btc/include/btc/btc_task.h index 2ea76c1772..0ed02d911a 100644 --- a/components/bt/bluedroid/btc/include/btc/btc_task.h +++ b/components/bt/bluedroid/btc/include/btc/btc_task.h @@ -53,17 +53,17 @@ typedef enum { BTC_PID_BLUFI, BTC_PID_DM_SEC, BTC_PID_ALARM, -#if CONFIG_BT_CLASSIC_ENABLED +#if (CLASSIC_BT_INCLUDED == TRUE) BTC_PID_GAP_BT, BTC_PID_PRF_QUE, BTC_PID_A2DP, BTC_PID_AVRC_CT, BTC_PID_AVRC_TG, BTC_PID_SPP, -#if BTC_HF_CLIENT_INCLUDED +#if (BTC_HF_CLIENT_INCLUDED == TRUE) BTC_PID_HF_CLIENT, #endif /* BTC_HF_CLIENT_INCLUDED */ -#endif /* CONFIG_BT_CLASSIC_ENABLED */ +#endif /* CLASSIC_BT_INCLUDED */ BTC_PID_NUM, } btc_pid_t; //btc profile id diff --git a/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_sink.c b/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_sink.c index c8e1ec9c90..d25a1e5de5 100644 --- a/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_sink.c +++ b/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_sink.c @@ -47,9 +47,9 @@ /* Macro */ #define BTC_A2DP_SINK_TASK_PINNED_TO_CORE (TASK_PINNED_TO_CORE) -#define BTC_A2DP_SINK_TASK_STACK_SIZE (CONFIG_A2DP_SINK_TASK_STACK_SIZE + BT_TASK_EXTRA_STACK_SIZE) // by menuconfig +#define BTC_A2DP_SINK_TASK_STACK_SIZE (A2DP_SINK_TASK_STACK_SIZE + BT_TASK_EXTRA_STACK_SIZE) // by menuconfig #define BTC_A2DP_SINK_TASK_NAME "BtA2dSinkT" -#define BTC_A2DP_SINK_TASK_PRIO (configMAX_PRIORITIES - 3) +#define BTC_A2DP_SINK_TASK_PRIO (BT_TASK_MAX_PRIORITIES - 3) /***************************************************************************** diff --git a/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_source.c b/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_source.c index 893ade6e0d..d6f12b69f1 100644 --- a/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_source.c +++ b/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_source.c @@ -52,9 +52,9 @@ /* Macro */ #define BTC_A2DP_SOURCE_TASK_PINNED_TO_CORE (TASK_PINNED_TO_CORE) -#define BTC_A2DP_SOURCE_TASK_STACK_SIZE (CONFIG_A2DP_SOURCE_TASK_STACK_SIZE + BT_TASK_EXTRA_STACK_SIZE) // by menuconfig +#define BTC_A2DP_SOURCE_TASK_STACK_SIZE (A2DP_SOURCE_TASK_STACK_SIZE + BT_TASK_EXTRA_STACK_SIZE) // by menuconfig #define BTC_A2DP_SOURCE_TASK_NAME "BtA2dSourceT" -#define BTC_A2DP_SOURCE_TASK_PRIO (configMAX_PRIORITIES - 3) +#define BTC_A2DP_SOURCE_TASK_PRIO (BT_TASK_MAX_PRIORITIES - 3) /***************************************************************************** diff --git a/components/bt/bluedroid/btc/profile/std/hf_client/btc_hf_client.c b/components/bt/bluedroid/btc/profile/std/hf_client/btc_hf_client.c index 3ae296cf0f..d0cdbc5e66 100644 --- a/components/bt/bluedroid/btc/profile/std/hf_client/btc_hf_client.c +++ b/components/bt/bluedroid/btc/profile/std/hf_client/btc_hf_client.c @@ -174,7 +174,7 @@ bt_status_t btc_hf_client_init(void) btc_hf_client_cb.initialized = true; -#if CONFIG_BT_HFP_AUDIO_DATA_PATH_HCI +#if BTM_SCO_HCI_INCLUDED data_path = ESP_SCO_DATA_PATH_HCI; #else data_path = ESP_SCO_DATA_PATH_PCM; diff --git a/components/bt/bluedroid/common/include/common/bt_target.h b/components/bt/bluedroid/common/include/common/bt_target.h index 3e9dac6131..b781d435ac 100644 --- a/components/bt/bluedroid/common/include/common/bt_target.h +++ b/components/bt/bluedroid/common/include/common/bt_target.h @@ -34,25 +34,25 @@ #include "bdroid_buildcfg.h" #endif -#include "sdkconfig.h" +#include "bt_user_config.h" #include "stack/bt_types.h" /* This must be defined AFTER buildcfg.h */ #include "stack/dyn_mem.h" /* defines static and/or dynamic memory for components */ /* OS Configuration from User config (eg: sdkconfig) */ -#if CONFIG_BLUEDROID_PINNED_TO_CORE -#define TASK_PINNED_TO_CORE (CONFIG_BLUEDROID_PINNED_TO_CORE < portNUM_PROCESSORS ? CONFIG_BLUEDROID_PINNED_TO_CORE : tskNO_AFFINITY) -#else -#define TASK_PINNED_TO_CORE (0) -#endif +#define TASK_PINNED_TO_CORE UC_TASK_PINNED_TO_CORE +#define BT_TASK_MAX_PRIORITIES configMAX_PRIORITIES +#define BT_BTC_TASK_STACK_SIZE UC_BTC_TASK_STACK_SIZE +#define A2DP_SINK_TASK_STACK_SIZE UC_A2DP_SINK_TASK_STACK_SIZE +#define A2DP_SOURCE_TASK_STACK_SIZE UC_A2DP_SOURCE_TASK_STACK_SIZE /****************************************************************************** ** ** Classic BT features ** ******************************************************************************/ -#if CONFIG_BT_CLASSIC_ENABLED +#if (UC_BT_CLASSIC_ENABLED == TRUE) #define CLASSIC_BT_INCLUDED TRUE #define BTC_SM_INCLUDED TRUE #define BTC_PRF_QUEUE_INCLUDED TRUE @@ -61,7 +61,7 @@ #define BTA_DM_PM_INCLUDED TRUE #define SDP_INCLUDED TRUE -#if CONFIG_BT_A2DP_ENABLE +#if (UC_BT_A2DP_ENABLED == TRUE) #define BTA_AR_INCLUDED TRUE #define BTA_AV_INCLUDED TRUE #define AVDT_INCLUDED TRUE @@ -74,15 +74,15 @@ #define SBC_DEC_INCLUDED TRUE #define BTC_AV_SRC_INCLUDED TRUE #define SBC_ENC_INCLUDED TRUE -#endif /* CONFIG_BT_A2DP_ENABLE */ +#endif /* UC_BT_A2DP_ENABLED */ -#if CONFIG_BT_SPP_ENABLED +#if (UC_BT_SPP_ENABLED == TRUE) #define RFCOMM_INCLUDED TRUE #define BTA_JV_INCLUDED TRUE #define BTC_SPP_INCLUDED TRUE -#endif /* CONFIG_BT_SPP_ENABLED */ +#endif /* UC_BT_SPP_ENABLED */ -#if CONFIG_BT_HFP_CLIENT_ENABLE +#if (UC_BT_HFP_CLIENT_ENABLED == TRUE) #define BTC_HF_CLIENT_INCLUDED TRUE #define BTA_HF_INCLUDED TRUE #define PLC_INCLUDED TRUE @@ -95,96 +95,81 @@ #ifndef BTM_MAX_SCO_LINKS #define BTM_MAX_SCO_LINKS (1) #endif + #ifndef SBC_DEC_INCLUDED #define SBC_DEC_INCLUDED TRUE #endif #ifndef SBC_ENC_INCLUDED #define SBC_ENC_INCLUDED TRUE #endif -#endif /* CONFIG_HFP_HF_ENABLE */ +#endif /* UC_BT_HFP_CLIENT_ENABLED */ -#if CONFIG_BT_SSP_ENABLED +#if UC_BT_SSP_ENABLED #define BT_SSP_INCLUDED TRUE -#endif /* CONFIG_BT_SSP_ENABLED */ +#endif /* UC_BT_SSP_ENABLED */ -#endif /* #if CONFIG_BT_CLASSIC_ENABLED */ +#endif /* UC_BT_CLASSIC_ENABLED */ #ifndef CLASSIC_BT_INCLUDED #define CLASSIC_BT_INCLUDED FALSE #endif /* CLASSIC_BT_INCLUDED */ -#ifndef CONFIG_BT_GATTC_CACHE_NVS_FLASH -#define CONFIG_BT_GATTC_CACHE_NVS_FLASH FALSE -#endif /* CONFIG_BT_GATTC_CACHE_NVS_FLASH */ - /****************************************************************************** ** ** BLE features ** ******************************************************************************/ -#if (CONFIG_BT_GATTS_ENABLE) +#if (UC_BT_GATTS_ENABLE) #define GATTS_INCLUDED TRUE #else #define GATTS_INCLUDED FALSE -#endif /* CONFIG_BT_GATTS_ENABLE */ +#endif /* UC_BT_GATTS_ENABLE */ -#if (CONFIG_BT_GATTC_ENABLE) +#if (UC_BT_GATTC_ENABLE) #define GATTC_INCLUDED TRUE #else #define GATTC_INCLUDED FALSE -#endif /* CONFIG_BT_GATTC_ENABLE */ +#endif /* UC_BT_GATTC_ENABLE */ -#if (CONFIG_BT_GATTC_ENABLE && CONFIG_BT_GATTC_CACHE_NVS_FLASH) -#define GATTC_CACHE_NVS TRUE +#if (UC_BT_GATTC_ENABLE && UC_BT_GATTC_CACHE_NVS_FLASH_ENABLED) +#define GATTC_CACHE_NVS TRUE #else -#define GATTC_CACHE_NVS FALSE -#endif /* CONFIG_BT_GATTC_CACHE_NVS_FLASH */ +#define GATTC_CACHE_NVS FALSE +#endif /* UC_BT_GATTC_ENABLE && UC_BT_GATTC_CACHE_NVS_FLASH_ENABLED */ -#if (CONFIG_BT_SMP_ENABLE) -#define SMP_INCLUDED TRUE -#define BLE_PRIVACY_SPT TRUE +#if (UC_BT_SMP_ENABLE) +#define SMP_INCLUDED TRUE +#define BLE_PRIVACY_SPT TRUE #else -#define SMP_INCLUDED FALSE -#define BLE_PRIVACY_SPT FALSE -#endif /* CONFIG_BT_SMP_ENABLE */ +#define SMP_INCLUDED FALSE +#define BLE_PRIVACY_SPT FALSE +#endif /* UC_BT_SMP_ENABLE */ -#ifdef CONFIG_BT_SMP_SLAVE_CON_PARAMS_UPD_ENABLE -#if(CONFIG_BT_SMP_SLAVE_CON_PARAMS_UPD_ENABLE) -#define SMP_SLAVE_CON_PARAMS_UPD_ENABLE TRUE +#if(UC_BT_SMP_SLAVE_CON_PARAMS_UPD_ENABLE) +#define SMP_SLAVE_CON_PARAMS_UPD_ENABLE TRUE #else -#define SMP_SLAVE_CON_PARAMS_UPD_ENABLE FALSE -#endif -#else -#define SMP_SLAVE_CON_PARAMS_UPD_ENABLE FALSE -#endif +#define SMP_SLAVE_CON_PARAMS_UPD_ENABLE FALSE +#endif /* UC_BT_SMP_SLAVE_CON_PARAMS_UPD_ENABLE */ -#ifndef CONFIG_BTDM_BLE_ADV_REPORT_FLOW_CTRL_SUPP -#define BLE_ADV_REPORT_FLOW_CONTROL FALSE -#else -#define BLE_ADV_REPORT_FLOW_CONTROL CONFIG_BTDM_BLE_ADV_REPORT_FLOW_CTRL_SUPP -#endif /* CONFIG_BTDM_BLE_ADV_REPORT_FLOW_CTRL_SUPP */ +#ifdef UC_BTDM_BLE_ADV_REPORT_FLOW_CTRL_SUPP +#define BLE_ADV_REPORT_FLOW_CONTROL UC_BTDM_BLE_ADV_REPORT_FLOW_CTRL_SUPP +#endif /* UC_BTDM_BLE_ADV_REPORT_FLOW_CTRL_SUPP */ -#ifndef CONFIG_BTDM_BLE_ADV_REPORT_FLOW_CTRL_NUM -#define BLE_ADV_REPORT_FLOW_CONTROL_NUM 100 -#else -#define BLE_ADV_REPORT_FLOW_CONTROL_NUM CONFIG_BTDM_BLE_ADV_REPORT_FLOW_CTRL_NUM -#endif /* CONFIG_BTDM_BLE_ADV_REPORT_FLOW_CTRL_NUM */ +#ifdef UC_BTDM_BLE_ADV_REPORT_FLOW_CTRL_NUM +#define BLE_ADV_REPORT_FLOW_CONTROL_NUM UC_BTDM_BLE_ADV_REPORT_FLOW_CTRL_NUM +#endif /* UC_BTDM_BLE_ADV_REPORT_FLOW_CTRL_NUM */ -#ifndef CONFIG_BTDM_BLE_ADV_REPORT_DISCARD_THRSHOLD -#define BLE_ADV_REPORT_DISCARD_THRSHOLD 20 -#else -#define BLE_ADV_REPORT_DISCARD_THRSHOLD CONFIG_BTDM_BLE_ADV_REPORT_DISCARD_THRSHOLD -#endif /* CONFIG_BTDM_BLE_ADV_REPORT_DISCARD_THRSHOLD */ +#ifdef UC_BTDM_BLE_ADV_REPORT_DISCARD_THRSHOLD +#define BLE_ADV_REPORT_DISCARD_THRSHOLD UC_BTDM_BLE_ADV_REPORT_DISCARD_THRSHOLD +#endif /* UC_BTDM_BLE_ADV_REPORT_DISCARD_THRSHOLD */ -#if (CONFIG_BT_ACL_CONNECTIONS) -#define MAX_ACL_CONNECTIONS CONFIG_BT_ACL_CONNECTIONS -#define GATT_MAX_PHY_CHANNEL CONFIG_BT_ACL_CONNECTIONS -#endif /* CONFIG_BT_ACL_CONNECTIONS */ +#ifdef UC_BT_ACL_CONNECTIONS +#define MAX_ACL_CONNECTIONS UC_BT_ACL_CONNECTIONS +#define GATT_MAX_PHY_CHANNEL UC_BT_ACL_CONNECTIONS +#endif /* UC_BT_ACL_CONNECTIONS */ -#if(CONFIG_BT_BLE_ESTAB_LINK_CONN_TOUT) -#define BLE_ESTABLISH_LINK_CONNECTION_TIMEOUT CONFIG_BT_BLE_ESTAB_LINK_CONN_TOUT -#else -#define BLE_ESTABLISH_LINK_CONNECTION_TIMEOUT 30 +#ifdef UC_BT_BLE_ESTAB_LINK_CONN_TOUT +#define BLE_ESTABLISH_LINK_CONNECTION_TIMEOUT UC_BT_BLE_ESTAB_LINK_CONN_TOUT #endif //------------------Added from bdroid_buildcfg.h--------------------- @@ -341,7 +326,7 @@ #endif #ifndef BTA_AVRCP_FF_RW_SUPPORT -#define BTA_AVRCP_FF_RW_SUPPORT FALSE//TRUE +#define BTA_AVRCP_FF_RW_SUPPORT FALSE #endif #ifndef BTA_AG_SCO_PKT_TYPES @@ -357,34 +342,30 @@ #endif #ifndef BTA_AV_CO_CP_SCMS_T -#define BTA_AV_CO_CP_SCMS_T FALSE//FALSE +#define BTA_AV_CO_CP_SCMS_T FALSE #endif #ifndef QUEUE_CONGEST_SIZE #define QUEUE_CONGEST_SIZE 40 #endif -#ifndef CONFIG_BT_BLE_HOST_QUEUE_CONG_CHECK +#if UC_BT_BLE_HOST_QUEUE_CONGESTION_CHECK +#define SCAN_QUEUE_CONGEST_CHECK TRUE +#else #define SCAN_QUEUE_CONGEST_CHECK FALSE -#else -#define SCAN_QUEUE_CONGEST_CHECK CONFIG_BT_BLE_HOST_QUEUE_CONG_CHECK #endif -#ifndef CONFIG_BT_GATTS_SEND_SERVICE_CHANGE_MODE -#define GATTS_SEND_SERVICE_CHANGE_MODE GATTS_SEND_SERVICE_CHANGE_AUTO -#else -#define GATTS_SEND_SERVICE_CHANGE_MODE CONFIG_BT_GATTS_SEND_SERVICE_CHANGE_MODE +#ifdef UC_BT_GATTS_SEND_SERVICE_CHANGE_MODE +#define GATTS_SEND_SERVICE_CHANGE_MODE UC_BT_GATTS_SEND_SERVICE_CHANGE_MODE #endif -#ifndef CONFIG_BT_BLE_ACT_SCAN_REP_ADV_SCAN -#define BTM_BLE_ACTIVE_SCAN_REPORT_ADV_SCAN_RSP_INDIVIDUALLY FALSE -#else -#define BTM_BLE_ACTIVE_SCAN_REPORT_ADV_SCAN_RSP_INDIVIDUALLY CONFIG_BT_BLE_ACT_SCAN_REP_ADV_SCAN +#ifdef UC_BT_BLE_ACT_SCAN_REP_ADV_SCAN +#define BTM_BLE_ACTIVE_SCAN_REPORT_ADV_SCAN_RSP_INDIVIDUALLY UC_BT_BLE_ACT_SCAN_REP_ADV_SCAN #endif /* This feature is used to eanble interleaved scan*/ #ifndef BTA_HOST_INTERLEAVE_SEARCH -#define BTA_HOST_INTERLEAVE_SEARCH FALSE//FALSE +#define BTA_HOST_INTERLEAVE_SEARCH FALSE #endif #ifndef BT_USE_TRACES @@ -412,7 +393,7 @@ #endif #ifndef BTIF_DM_OOB_TEST -#define BTIF_DM_OOB_TEST FALSE//TRUE +#define BTIF_DM_OOB_TEST FALSE #endif // How long to wait before activating sniff mode after entering the @@ -597,11 +578,11 @@ /* Includes SCO if TRUE */ #ifndef BTM_SCO_HCI_INCLUDED -#if CONFIG_BT_HFP_AUDIO_DATA_PATH_HCI +#if UC_BT_HFP_AUDIO_DATA_PATH_HCI #define BTM_SCO_HCI_INCLUDED TRUE /* TRUE includes SCO over HCI code */ #else #define BTM_SCO_HCI_INCLUDED FALSE -#endif /* CONFIG_BT_HFP_AUDIO_DATA_PATH_HCI */ +#endif /* UC_HFP_AUDIO_DATA_PATH_HCI */ #endif /* Includes WBS if TRUE */ @@ -1135,7 +1116,7 @@ #endif #ifndef ATT_DEBUG -#define ATT_DEBUG FALSE//TRUE +#define ATT_DEBUG FALSE #endif #ifndef BLE_PERIPHERAL_MODE_SUPPORT @@ -1537,12 +1518,12 @@ Range: 2 octets ******************************************************************************/ #ifndef BNEP_INCLUDED -#define BNEP_INCLUDED FALSE//TRUE +#define BNEP_INCLUDED FALSE #endif /* BNEP status API call is used mainly to get the L2CAP handle */ #ifndef BNEP_SUPPORTS_STATUS_API -#define BNEP_SUPPORTS_STATUS_API FALSE//TRUE +#define BNEP_SUPPORTS_STATUS_API FALSE #endif /* @@ -1550,7 +1531,7 @@ Range: 2 octets ** we will do an authentication check again on the new role */ #ifndef BNEP_DO_AUTH_FOR_ROLE_SWITCH -#define BNEP_DO_AUTH_FOR_ROLE_SWITCH FALSE//TRUE +#define BNEP_DO_AUTH_FOR_ROLE_SWITCH FALSE #endif @@ -1663,22 +1644,22 @@ Range: 2 octets /* This will enable the PANU role */ #ifndef PAN_SUPPORTS_ROLE_PANU -#define PAN_SUPPORTS_ROLE_PANU FALSE//TRUE +#define PAN_SUPPORTS_ROLE_PANU FALSE #endif /* This will enable the GN role */ #ifndef PAN_SUPPORTS_ROLE_GN -#define PAN_SUPPORTS_ROLE_GN FALSE//TRUE +#define PAN_SUPPORTS_ROLE_GN FALSE #endif /* This will enable the NAP role */ #ifndef PAN_SUPPORTS_ROLE_NAP -#define PAN_SUPPORTS_ROLE_NAP FALSE//TRUE +#define PAN_SUPPORTS_ROLE_NAP FALSE #endif /* This is just for debugging purposes */ #ifndef PAN_SUPPORTS_DEBUG_DUMP -#define PAN_SUPPORTS_DEBUG_DUMP FALSE//TRUE +#define PAN_SUPPORTS_DEBUG_DUMP FALSE #endif /* Maximum number of PAN connections allowed */ @@ -2000,7 +1981,7 @@ The maximum number of payload octets that the local device can receive in a sing ******************************************************************************/ #ifndef HCILP_INCLUDED -#define HCILP_INCLUDED FALSE//TRUE +#define HCILP_INCLUDED FALSE #endif /****************************************************************************** @@ -2042,7 +2023,7 @@ The maximum number of payload octets that the local device can receive in a sing #endif #ifndef BTA_DM_AVOID_A2DP_ROLESWITCH_ON_INQUIRY -#define BTA_DM_AVOID_A2DP_ROLESWITCH_ON_INQUIRY FALSE//TRUE +#define BTA_DM_AVOID_A2DP_ROLESWITCH_ON_INQUIRY FALSE #endif /****************************************************************************** @@ -2053,7 +2034,25 @@ The maximum number of payload octets that the local device can receive in a sing /* Enable/disable BTSnoop memory logging */ #ifndef BTSNOOP_MEM -#define BTSNOOP_MEM FALSE//TRUE +#define BTSNOOP_MEM FALSE +#endif + +#if UC_BT_BLUEDROID_MEM_DEBUG +#define HEAP_MEMORY_DEBUG TRUE +#else +#define HEAP_MEMORY_DEBUG FALSE +#endif + +#if UC_HEAP_ALLOCATION_FROM_SPIRAM_FIRST +#define HEAP_ALLOCATION_FROM_SPIRAM_FIRST TRUE +#else +#define HEAP_ALLOCATION_FROM_SPIRAM_FIRST FALSE +#endif + +#if UC_BT_BLE_DYNAMIC_ENV_MEMORY +#define BT_BLE_DYNAMIC_ENV_MEMORY TRUE +#else +#define BT_BLE_DYNAMIC_ENV_MEMORY FALSE #endif #include "common/bt_trace.h" diff --git a/components/bt/bluedroid/common/include/common/bt_trace.h b/components/bt/bluedroid/common/include/common/bt_trace.h index a0dbf9bc72..920cfa7134 100644 --- a/components/bt/bluedroid/common/include/common/bt_trace.h +++ b/components/bt/bluedroid/common/include/common/bt_trace.h @@ -19,17 +19,16 @@ #ifndef _BT_TRACE_H_ #define _BT_TRACE_H_ -#include "sdkconfig.h" - #include #include +#include "bt_user_config.h" #include "stack/bt_types.h" #ifndef LOG_LOCAL_LEVEL #ifndef BOOTLOADER_BUILD -#define LOG_LOCAL_LEVEL CONFIG_LOG_DEFAULT_LEVEL +#define LOG_LOCAL_LEVEL UC_LOG_DEFAULT_LEVEL #else -#define LOG_LOCAL_LEVEL CONFIG_BOOTLOADER_LOG_LEVEL +#define LOG_LOCAL_LEVEL UC_BOOTLOADER_LOG_LEVEL #endif #endif @@ -217,135 +216,31 @@ inline void trc_dump_buffer(const char *prefix, uint8_t *data, uint16_t len) // btla-specific ++ /* Core Stack default trace levels */ -#ifdef CONFIG_BT_LOG_HCI_TRACE_LEVEL -#define HCI_INITIAL_TRACE_LEVEL CONFIG_BT_LOG_HCI_TRACE_LEVEL -#else -#define HCI_INITIAL_TRACE_LEVEL BT_TRACE_LEVEL_WARNING -#endif - -#ifdef CONFIG_BT_LOG_BTM_TRACE_LEVEL -#define BTM_INITIAL_TRACE_LEVEL CONFIG_BT_LOG_BTM_TRACE_LEVEL -#else -#define BTM_INITIAL_TRACE_LEVEL BT_TRACE_LEVEL_WARNING -#endif - -#ifdef CONFIG_BT_LOG_L2CAP_TRACE_LEVEL -#define L2CAP_INITIAL_TRACE_LEVEL CONFIG_BT_LOG_L2CAP_TRACE_LEVEL -#else -#define L2CAP_INITIAL_TRACE_LEVEL BT_TRACE_LEVEL_WARNING -#endif - -#ifdef CONFIG_BT_LOG_RFCOMM_TRACE_LEVEL -#define RFCOMM_INITIAL_TRACE_LEVEL CONFIG_BT_LOG_RFCOMM_TRACE_LEVEL -#else -#define RFCOMM_INITIAL_TRACE_LEVEL BT_TRACE_LEVEL_WARNING -#endif - -#ifdef CONFIG_SDP_INITIAL_TRACE_LEVEL -#define SDP_INITIAL_TRACE_LEVEL CONFIG_SDP_INITIAL_TRACE_LEVEL -#else -#define SDP_INITIAL_TRACE_LEVEL BT_TRACE_LEVEL_WARNING -#endif - -#ifdef CONFIG_BT_LOG_GAP_TRACE_LEVEL -#define GAP_INITIAL_TRACE_LEVEL CONFIG_BT_LOG_GAP_TRACE_LEVEL -#else -#define GAP_INITIAL_TRACE_LEVEL BT_TRACE_LEVEL_WARNING -#endif - -#ifdef CONFIG_BT_LOG_BNEP_TRACE_LEVEL -#define BNEP_INITIAL_TRACE_LEVEL CONFIG_BT_LOG_BNEP_TRACE_LEVEL -#else -#define BNEP_INITIAL_TRACE_LEVEL BT_TRACE_LEVEL_WARNING -#endif - -#ifdef CONFIG_BT_LOG_PAN_TRACE_LEVEL -#define PAN_INITIAL_TRACE_LEVEL CONFIG_BT_LOG_PAN_TRACE_LEVEL -#else -#define PAN_INITIAL_TRACE_LEVEL BT_TRACE_LEVEL_WARNING -#endif - -#ifdef CONFIG_BT_LOG_A2D_TRACE_LEVEL -#define A2D_INITIAL_TRACE_LEVEL CONFIG_BT_LOG_A2D_TRACE_LEVEL -#else -#define A2D_INITIAL_TRACE_LEVEL BT_TRACE_LEVEL_WARNING -#endif - -#ifdef CONFIG_BT_LOG_AVDT_TRACE_LEVEL -#define AVDT_INITIAL_TRACE_LEVEL CONFIG_BT_LOG_AVDT_TRACE_LEVEL -#else -#define AVDT_INITIAL_TRACE_LEVEL BT_TRACE_LEVEL_WARNING -#endif - -#ifdef CONFIG_BT_LOG_AVCT_TRACE_LEVEL -#define AVCT_INITIAL_TRACE_LEVEL CONFIG_BT_LOG_AVCT_TRACE_LEVEL -#else -#define AVCT_INITIAL_TRACE_LEVEL BT_TRACE_LEVEL_WARNING -#endif - -#ifdef CONFIG_BT_LOG_AVRC_TRACE_LEVEL -#define AVRC_INITIAL_TRACE_LEVEL CONFIG_BT_LOG_AVRC_TRACE_LEVEL -#else -#define AVRC_INITIAL_TRACE_LEVEL BT_TRACE_LEVEL_WARNING -#endif - -#ifdef CONFIG_BT_LOG_MCA_TRACE_LEVEL -#define MCA_INITIAL_TRACE_LEVEL CONFIG_BT_LOG_MCA_TRACE_LEVEL -#else -#define MCA_INITIAL_TRACE_LEVEL BT_TRACE_LEVEL_WARNING -#endif - -#ifdef CONFIG_BT_LOG_HID_TRACE_LEVEL -#define HID_INITIAL_TRACE_LEVEL CONFIG_BT_LOG_HID_TRACE_LEVEL -#else -#define HID_INITIAL_TRACE_LEVEL BT_TRACE_LEVEL_WARNING -#endif - -#ifdef CONFIG_BT_LOG_APPL_TRACE_LEVEL -#define APPL_INITIAL_TRACE_LEVEL CONFIG_BT_LOG_APPL_TRACE_LEVEL -#else -#define APPL_INITIAL_TRACE_LEVEL BT_TRACE_LEVEL_WARNING -#endif - -#ifdef CONFIG_BT_LOG_GATT_TRACE_LEVEL -#define GATT_INITIAL_TRACE_LEVEL CONFIG_BT_LOG_GATT_TRACE_LEVEL -#else -#define GATT_INITIAL_TRACE_LEVEL BT_TRACE_LEVEL_WARNING -#endif - -#ifdef CONFIG_BT_LOG_SMP_TRACE_LEVEL -#define SMP_INITIAL_TRACE_LEVEL CONFIG_BT_LOG_SMP_TRACE_LEVEL -#else -#define SMP_INITIAL_TRACE_LEVEL BT_TRACE_LEVEL_WARNING -#endif - -#ifdef CONFIG_BT_LOG_BTIF_TRACE_LEVEL -#define BTIF_INITIAL_TRACE_LEVEL CONFIG_BT_LOG_BTIF_TRACE_LEVEL -#else -#define BTIF_INITIAL_TRACE_LEVEL BT_TRACE_LEVEL_WARNING -#endif - -#ifdef CONFIG_BT_LOG_BTC_TRACE_LEVEL -#define BTC_INITIAL_TRACE_LEVEL CONFIG_BT_LOG_BTC_TRACE_LEVEL -#else -#define BTC_INITIAL_TRACE_LEVEL BT_TRACE_LEVEL_WARNING -#endif - -#ifdef CONFIG_BT_LOG_OSI_TRACE_LEVEL -#define OSI_INITIAL_TRACE_LEVEL CONFIG_BT_LOG_OSI_TRACE_LEVEL -#else -#define OSI_INITIAL_TRACE_LEVEL BT_TRACE_LEVEL_WARNING -#endif - -#ifdef CONFIG_BT_LOG_BLUFI_TRACE_LEVEL -#define BLUFI_INITIAL_TRACE_LEVEL CONFIG_BT_LOG_BLUFI_TRACE_LEVEL -#else -#define BLUFI_INITIAL_TRACE_LEVEL BT_TRACE_LEVEL_WARNING -#endif +#define HCI_INITIAL_TRACE_LEVEL UC_BT_LOG_HCI_TRACE_LEVEL +#define BTM_INITIAL_TRACE_LEVEL UC_BT_LOG_BTM_TRACE_LEVEL +#define L2CAP_INITIAL_TRACE_LEVEL UC_BT_LOG_L2CAP_TRACE_LEVEL +#define RFCOMM_INITIAL_TRACE_LEVEL UC_BT_LOG_RFCOMM_TRACE_LEVEL +#define SDP_INITIAL_TRACE_LEVEL UC_BT_LOG_SDP_TRACE_LEVEL +#define GAP_INITIAL_TRACE_LEVEL UC_BT_LOG_GAP_TRACE_LEVEL +#define BNEP_INITIAL_TRACE_LEVEL UC_BT_LOG_BNEP_TRACE_LEVEL +#define PAN_INITIAL_TRACE_LEVEL UC_BT_LOG_PAN_TRACE_LEVEL +#define A2D_INITIAL_TRACE_LEVEL UC_BT_LOG_A2D_TRACE_LEVEL +#define AVDT_INITIAL_TRACE_LEVEL UC_BT_LOG_AVDT_TRACE_LEVEL +#define AVCT_INITIAL_TRACE_LEVEL UC_BT_LOG_AVCT_TRACE_LEVEL +#define AVRC_INITIAL_TRACE_LEVEL UC_BT_LOG_AVRC_TRACE_LEVEL +#define MCA_INITIAL_TRACE_LEVEL UC_BT_LOG_MCA_TRACE_LEVEL +#define HID_INITIAL_TRACE_LEVEL UC_BT_LOG_HID_TRACE_LEVEL +#define APPL_INITIAL_TRACE_LEVEL UC_BT_LOG_APPL_TRACE_LEVEL +#define GATT_INITIAL_TRACE_LEVEL UC_BT_LOG_GATT_TRACE_LEVEL +#define SMP_INITIAL_TRACE_LEVEL UC_BT_LOG_SMP_TRACE_LEVEL +#define BTIF_INITIAL_TRACE_LEVEL UC_BT_LOG_BTIF_TRACE_LEVEL +#define BTC_INITIAL_TRACE_LEVEL UC_BT_LOG_BTC_TRACE_LEVEL +#define OSI_INITIAL_TRACE_LEVEL UC_BT_LOG_OSI_TRACE_LEVEL +#define BLUFI_INITIAL_TRACE_LEVEL UC_BT_LOG_BLUFI_TRACE_LEVEL // btla-specific -- -#if !CONFIG_BT_STACK_NO_LOG +#if !UC_BT_STACK_NO_LOG #define LOG_ERROR(format, ... ) {if (LOG_LOCAL_LEVEL >= ESP_LOG_ERROR) esp_log_write(ESP_LOG_ERROR, "BT_LOG", LOG_FORMAT(E, format), esp_log_timestamp(), "BT_LOG", ##__VA_ARGS__); } #define LOG_WARN(format, ... ) {if (LOG_LOCAL_LEVEL >= ESP_LOG_WARN) esp_log_write(ESP_LOG_WARN, "BT_LOG", LOG_FORMAT(W, format), esp_log_timestamp(), "BT_LOG", ##__VA_ARGS__); } #define LOG_INFO(format, ... ) {if (LOG_LOCAL_LEVEL >= ESP_LOG_INFO) esp_log_write(ESP_LOG_INFO, "BT_LOG", LOG_FORMAT(I, format), esp_log_timestamp(), "BT_LOG", ##__VA_ARGS__); } @@ -695,7 +590,7 @@ extern UINT8 btif_trace_level; #define BLUFI_TRACE_EVENT(fmt, args...) #define BLUFI_TRACE_DEBUG(fmt, args...) #define BLUFI_TRACE_VERBOSE(fmt, args...) -#endif ///CONFIG_BT_STACK_NO_LOG +#endif ///!UC_BT_STACK_NO_LOG /* Simplified Trace Helper Macro diff --git a/components/bt/bluedroid/common/include/common/bt_user_config.h b/components/bt/bluedroid/common/include/common/bt_user_config.h new file mode 100644 index 0000000000..10568fbd07 --- /dev/null +++ b/components/bt/bluedroid/common/include/common/bt_user_config.h @@ -0,0 +1,372 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#ifndef __BT_USER_CONFIG_H__ +#define __BT_USER_CONFIG_H__ + + + + +/* All the configuration from SDK defined here */ + +#include "sdkconfig.h" +#include "esp_task.h" + + +/********************************************************** + * Thread/Task reference + **********************************************************/ +#ifdef CONFIG_BLUEDROID_PINNED_TO_CORE +#define UC_TASK_PINNED_TO_CORE (CONFIG_BLUEDROID_PINNED_TO_CORE < portNUM_PROCESSORS ? CONFIG_BLUEDROID_PINNED_TO_CORE : tskNO_AFFINITY) +#else +#define UC_TASK_PINNED_TO_CORE (0) +#endif + +#ifdef CONFIG_BTC_TASK_STACK_SIZE +#define UC_BTC_TASK_STACK_SIZE CONFIG_BTC_TASK_STACK_SIZE +#else +#define UC_BTC_TASK_STACK_SIZE 3072 +#endif + +#ifdef CONFIG_A2DP_SINK_TASK_STACK_SIZE +#define UC_A2DP_SINK_TASK_STACK_SIZE CONFIG_A2DP_SINK_TASK_STACK_SIZE +#else +#define UC_A2DP_SINK_TASK_STACK_SIZE 2048 +#endif +#ifdef CONFIG_A2DP_SOURCE_TASK_STACK_SIZE +#define UC_A2DP_SOURCE_TASK_STACK_SIZE CONFIG_A2DP_SOURCE_TASK_STACK_SIZE +#else +#define UC_A2DP_SOURCE_TASK_STACK_SIZE 2048 +#endif + +/********************************************************** + * Profile reference + **********************************************************/ +//Classic BT reference +#ifdef CONFIG_BT_CLASSIC_ENABLED +#define UC_BT_CLASSIC_ENABLED CONFIG_BT_CLASSIC_ENABLED +#else +#define UC_BT_CLASSIC_ENABLED FALSE +#endif + +//A2DP +#ifdef CONFIG_BT_A2DP_ENABLE +#define UC_BT_A2DP_ENABLED CONFIG_BT_A2DP_ENABLE +#else +#define UC_BT_A2DP_ENABLED FALSE +#endif + +//SPP +#ifdef CONFIG_BT_SPP_ENABLED +#define UC_BT_SPP_ENABLED CONFIG_BT_SPP_ENABLED +#else +#define UC_BT_SPP_ENABLED FALSE +#endif + +//HFP +#ifdef CONFIG_BT_HFP_CLIENT_ENABLE +#define UC_BT_HFP_CLIENT_ENABLED CONFIG_BT_HFP_CLIENT_ENABLE +#else +#define UC_BT_HFP_CLIENT_ENABLED FALSE +#endif + +//SSP +#ifdef CONFIG_BT_SSP_ENABLED +#define UC_BT_SSP_ENABLED CONFIG_BT_SSP_ENABLED +#else +#define UC_BT_SSP_ENABLED FALSE +#endif + +//GATTS +#ifdef CONFIG_BT_GATTS_ENABLE +#define UC_BT_GATTS_ENABLED CONFIG_BT_GATTS_ENABLE +#else +#define UC_BT_GATTS_ENABLED FALSE +#endif + +//GATTC +#ifdef CONFIG_BT_GATTC_ENABLE +#define UC_BT_GATTC_ENABLED CONFIG_BT_GATTC_ENABLE +#else +#define UC_BT_GATTC_ENABLED FALSE +#endif + +//GATTC CACHE +#ifdef CONFIG_BT_GATTC_CACHE_NVS_FLASH +#define UC_BT_GATTC_CACHE_NVS_FLASH_ENABLED CONFIG_BT_GATTC_CACHE_NVS_FLASH +#else +#define UC_BT_GATTC_CACHE_NVS_FLASH_ENABLED FALSE +#endif + +//SMP +#ifdef CONFIG_BT_SMP_ENABLE +#define UC_BT_SMP_ENABLED CONFIG_BT_SMP_ENABLE +#else +#define UC_BT_SMP_ENABLED FALSE +#endif + +//SMP_SLAVE_CON_PARAMS_UPD_ENABLE +#ifdef CONFIG_BT_SMP_SLAVE_CON_PARAMS_UPD_ENABLE +#define UC_BT_SMP_SLAVE_CON_PARAMS_UPD_ENABLE CONFIG_BT_SMP_SLAVE_CON_PARAMS_UPD_ENABLE +#else +#define UC_BT_SMP_SLAVE_CON_PARAMS_UPD_ENABLE FALSE +#endif + +//BTDM_BLE_ADV_REPORT_FLOW_CTRL_SUPP +#ifdef CONFIG_BTDM_BLE_ADV_REPORT_FLOW_CTRL_SUPP +#define UC_BTDM_BLE_ADV_REPORT_FLOW_CTRL_SUPP CONFIG_BTDM_BLE_ADV_REPORT_FLOW_CTRL_SUPP +#else +#define UC_BTDM_BLE_ADV_REPORT_FLOW_CTRL_SUPP FALSE +#endif + +//SMP_SLAVE_CON_PARAMS_UPD_ENABLE +#ifdef CONFIG_BTDM_BLE_ADV_REPORT_FLOW_CTRL_NUM +#define UC_BTDM_BLE_ADV_REPORT_FLOW_CTRL_NUM CONFIG_BTDM_BLE_ADV_REPORT_FLOW_CTRL_NUM +#else +#define UC_BTDM_BLE_ADV_REPORT_FLOW_CTRL_NUM 100 +#endif + +//BTDM_BLE_ADV_REPORT_FLOW_CTRL_SUPP +#ifdef CONFIG_BTDM_BLE_ADV_REPORT_DISCARD_THRSHOLD +#define UC_BTDM_BLE_ADV_REPORT_DISCARD_THRSHOLD CONFIG_BTDM_BLE_ADV_REPORT_DISCARD_THRSHOLD +#else +#define UC_BTDM_BLE_ADV_REPORT_DISCARD_THRSHOLD 20 +#endif + +//BT ACL CONNECTIONS +#ifdef CONFIG_BT_ACL_CONNECTIONS +#define UC_BT_ACL_CONNECTIONS CONFIG_BT_ACL_CONNECTIONS +#else +#define UC_BT_ACL_CONNECTIONS 5 +#endif + +//BT_BLE_ESTAB_LINK_CONN_TOUT +#ifdef CONFIG_BT_BLE_ESTAB_LINK_CONN_TOUT +#define UC_BT_BLE_ESTAB_LINK_CONN_TOUT CONFIG_BT_BLE_ESTAB_LINK_CONN_TOUT +#else +#define UC_BT_BLE_ESTAB_LINK_CONN_TOUT 30 +#endif + + +//HOST QUEUE CONGEST CHECK +#ifdef CONFIG_BT_BLE_HOST_QUEUE_CONGESTION_CHECK +#define UC_BT_BLE_HOST_QUEUE_CONGESTION_CHECK CONFIG_BT_BLE_HOST_QUEUE_CONGESTION_CHECK +#else +#define UC_BT_BLE_HOST_QUEUE_CONGESTION_CHECK FALSE +#endif + +#ifdef CONFIG_BT_GATTS_SEND_SERVICE_CHANGE_MODE +#define UC_BT_GATTS_SEND_SERVICE_CHANGE_MODE CONFIG_BT_GATTS_SEND_SERVICE_CHANGE_MODE +#else +#define UC_BT_GATTS_SEND_SERVICE_CHANGE_MODE 0 +#endif + +#ifdef CONFIG_BT_BLE_ACT_SCAN_REP_ADV_SCAN +#define UC_BT_BLE_ACT_SCAN_REP_ADV_SCAN CONFIG_BT_BLE_ACT_SCAN_REP_ADV_SCAN +#else +#define UC_BT_BLE_ACT_SCAN_REP_ADV_SCAN FALSE +#endif + +//SCO VOICE OVER HCI +#ifdef CONFIG_BT_HFP_AUDIO_DATA_PATH_HCI +#define UC_BT_HFP_AUDIO_DATA_PATH_HCI CONFIG_BT_HFP_AUDIO_DATA_PATH_HCI +#else +#define UC_BT_HFP_AUDIO_DATA_PATH_HCI FALSE +#endif + + +/********************************************************** + * Memory reference + **********************************************************/ +//DYNAMIC ENV ALLOCATOR +#ifdef CONFIG_BT_BLE_DYNAMIC_ENV_MEMORY +#define UC_BT_BLE_DYNAMIC_ENV_MEMORY CONFIG_BT_BLE_DYNAMIC_ENV_MEMORY +#else +#define UC_BT_BLE_DYNAMIC_ENV_MEMORY FALSE +#endif + +//MEMORY ALLOCATOR +#ifdef CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST +#define UC_HEAP_ALLOCATION_FROM_SPIRAM_FIRST CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST +#else +#define UC_HEAP_ALLOCATION_FROM_SPIRAM_FIRST FALSE +#endif + +//MEMORY DEBUG +#ifdef CONFIG_BT_BLUEDROID_MEM_DEBUG +#define UC_BT_BLUEDROID_MEM_DEBUG CONFIG_BT_BLUEDROID_MEM_DEBUG +#else +#define UC_BT_BLUEDROID_MEM_DEBUG FALSE +#endif + + +/********************************************************** + * Trace reference + **********************************************************/ +#ifdef CONFIG_LOG_DEFAULT_LEVEL +#define UC_LOG_DEFAULT_LEVEL CONFIG_LOG_DEFAULT_LEVEL +#else +#define UC_LOG_DEFAULT_LEVEL 3 +#endif +#ifdef CONFIG_BOOTLOADER_LOG_LEVEL +#define UC_BOOTLOADER_LOG_LEVEL CONFIG_BOOTLOADER_LOG_LEVEL +#else +#define UC_BOOTLOADER_LOG_LEVEL 3 +#endif + +#ifdef CONFIG_BT_STACK_NO_LOG +#define UC_BT_STACK_NO_LOG CONFIG_BT_STACK_NO_LOG +#else +#define UC_BT_STACK_NO_LOG FALSE +#endif + +#define UC_TRACE_LEVEL_NONE 0 /* No trace messages to be generated */ +#define UC_TRACE_LEVEL_ERROR 1 /* Error condition trace messages */ +#define UC_TRACE_LEVEL_WARNING 2 /* Warning condition trace messages */ +#define UC_TRACE_LEVEL_API 3 /* API traces */ +#define UC_TRACE_LEVEL_EVENT 4 /* Debug messages for events */ +#define UC_TRACE_LEVEL_DEBUG 5 /* Full debug messages */ +#define UC_TRACE_LEVEL_VERBOSE 6 /* Verbose debug messages */ + +#ifdef CONFIG_BT_LOG_HCI_TRACE_LEVEL +#define UC_BT_LOG_HCI_TRACE_LEVEL CONFIG_BT_LOG_HCI_TRACE_LEVEL +#else +#define UC_BT_LOG_HCI_TRACE_LEVEL UC_TRACE_LEVEL_WARNING +#endif + +#ifdef CONFIG_BT_LOG_BTM_TRACE_LEVEL +#define UC_BT_LOG_BTM_TRACE_LEVEL CONFIG_BT_LOG_BTM_TRACE_LEVEL +#else +#define UC_BT_LOG_BTM_TRACE_LEVEL UC_TRACE_LEVEL_WARNING +#endif + +#ifdef CONFIG_BT_LOG_L2CAP_TRACE_LEVEL +#define UC_BT_LOG_L2CAP_TRACE_LEVEL CONFIG_BT_LOG_L2CAP_TRACE_LEVEL +#else +#define UC_BT_LOG_L2CAP_TRACE_LEVEL UC_TRACE_LEVEL_WARNING +#endif + +#ifdef CONFIG_BT_LOG_RFCOMM_TRACE_LEVEL +#define UC_BT_LOG_RFCOMM_TRACE_LEVEL CONFIG_BT_LOG_RFCOMM_TRACE_LEVEL +#else +#define UC_BT_LOG_RFCOMM_TRACE_LEVEL UC_TRACE_LEVEL_WARNING +#endif + +#ifdef CONFIG_BT_LOG_SDP_TRACE_LEVEL +#define UC_BT_LOG_SDP_TRACE_LEVEL CONFIG_BT_LOG_SDP_TRACE_LEVEL +#else +#define UC_BT_LOG_SDP_TRACE_LEVEL UC_TRACE_LEVEL_WARNING +#endif + +#ifdef CONFIG_BT_LOG_GAP_TRACE_LEVEL +#define UC_BT_LOG_GAP_TRACE_LEVEL CONFIG_BT_LOG_GAP_TRACE_LEVEL +#else +#define UC_BT_LOG_GAP_TRACE_LEVEL UC_TRACE_LEVEL_WARNING +#endif + +#ifdef CONFIG_BT_LOG_BNEP_TRACE_LEVEL +#define UC_BT_LOG_BNEP_TRACE_LEVEL CONFIG_BT_LOG_BNEP_TRACE_LEVEL +#else +#define UC_BT_LOG_BNEP_TRACE_LEVEL UC_TRACE_LEVEL_WARNING +#endif + +#ifdef CONFIG_BT_LOG_PAN_TRACE_LEVEL +#define UC_BT_LOG_PAN_TRACE_LEVEL CONFIG_BT_LOG_PAN_TRACE_LEVEL +#else +#define UC_BT_LOG_PAN_TRACE_LEVEL UC_TRACE_LEVEL_WARNING +#endif + +#ifdef CONFIG_BT_LOG_A2D_TRACE_LEVEL +#define UC_BT_LOG_A2D_TRACE_LEVEL CONFIG_BT_LOG_A2D_TRACE_LEVEL +#else +#define UC_BT_LOG_A2D_TRACE_LEVEL UC_TRACE_LEVEL_WARNING +#endif + +#ifdef CONFIG_BT_LOG_AVDT_TRACE_LEVEL +#define UC_BT_LOG_AVDT_TRACE_LEVEL CONFIG_BT_LOG_AVDT_TRACE_LEVEL +#else +#define UC_BT_LOG_AVDT_TRACE_LEVEL UC_TRACE_LEVEL_WARNING +#endif + +#ifdef CONFIG_BT_LOG_AVCT_TRACE_LEVEL +#define UC_BT_LOG_AVCT_TRACE_LEVEL CONFIG_BT_LOG_AVCT_TRACE_LEVEL +#else +#define UC_BT_LOG_AVCT_TRACE_LEVEL UC_TRACE_LEVEL_WARNING +#endif + +#ifdef CONFIG_BT_LOG_AVRC_TRACE_LEVEL +#define UC_BT_LOG_AVRC_TRACE_LEVEL CONFIG_BT_LOG_AVRC_TRACE_LEVEL +#else +#define UC_BT_LOG_AVRC_TRACE_LEVEL UC_TRACE_LEVEL_WARNING +#endif + +#ifdef CONFIG_BT_LOG_MCA_TRACE_LEVEL +#define UC_BT_LOG_MCA_TRACE_LEVEL CONFIG_BT_LOG_MCA_TRACE_LEVEL +#else +#define UC_BT_LOG_MCA_TRACE_LEVEL UC_TRACE_LEVEL_WARNING +#endif + +#ifdef CONFIG_BT_LOG_HID_TRACE_LEVEL +#define UC_BT_LOG_HID_TRACE_LEVEL CONFIG_BT_LOG_HID_TRACE_LEVEL +#else +#define UC_BT_LOG_HID_TRACE_LEVEL UC_TRACE_LEVEL_WARNING +#endif + +#ifdef CONFIG_BT_LOG_APPL_TRACE_LEVEL +#define UC_BT_LOG_APPL_TRACE_LEVEL CONFIG_BT_LOG_APPL_TRACE_LEVEL +#else +#define UC_BT_LOG_APPL_TRACE_LEVEL UC_TRACE_LEVEL_WARNING +#endif + +#ifdef CONFIG_BT_LOG_GATT_TRACE_LEVEL +#define UC_BT_LOG_GATT_TRACE_LEVEL CONFIG_BT_LOG_GATT_TRACE_LEVEL +#else +#define UC_BT_LOG_GATT_TRACE_LEVEL UC_TRACE_LEVEL_WARNING +#endif + +#ifdef CONFIG_BT_LOG_SMP_TRACE_LEVEL +#define UC_BT_LOG_SMP_TRACE_LEVEL CONFIG_BT_LOG_SMP_TRACE_LEVEL +#else +#define UC_BT_LOG_SMP_TRACE_LEVEL UC_TRACE_LEVEL_WARNING +#endif + +#ifdef CONFIG_BT_LOG_BTIF_TRACE_LEVEL +#define UC_BT_LOG_BTIF_TRACE_LEVEL CONFIG_BT_LOG_BTIF_TRACE_LEVEL +#else +#define UC_BT_LOG_BTIF_TRACE_LEVEL UC_TRACE_LEVEL_WARNING +#endif + +#ifdef CONFIG_BT_LOG_BTC_TRACE_LEVEL +#define UC_BT_LOG_BTC_TRACE_LEVEL CONFIG_BT_LOG_BTC_TRACE_LEVEL +#else +#define UC_BT_LOG_BTC_TRACE_LEVEL UC_TRACE_LEVEL_WARNING +#endif + +#ifdef CONFIG_BT_LOG_OSI_TRACE_LEVEL +#define UC_BT_LOG_OSI_TRACE_LEVEL CONFIG_BT_LOG_OSI_TRACE_LEVEL +#else +#define UC_BT_LOG_OSI_TRACE_LEVEL UC_TRACE_LEVEL_WARNING +#endif + +#ifdef CONFIG_BT_LOG_BLUFI_TRACE_LEVEL +#define UC_BT_LOG_BLUFI_TRACE_LEVEL CONFIG_BT_LOG_BLUFI_TRACE_LEVEL +#else +#define UC_BT_LOG_BLUFI_TRACE_LEVEL UC_TRACE_LEVEL_WARNING +#endif + + +#endif /* __BT_USER_CONFIG_H__ */ + + + diff --git a/components/bt/bluedroid/hci/hci_hal_h4.c b/components/bt/bluedroid/hci/hci_hal_h4.c index 4189aa2fe5..b31deda79c 100644 --- a/components/bt/bluedroid/hci/hci_hal_h4.c +++ b/components/bt/bluedroid/hci/hci_hal_h4.c @@ -29,9 +29,8 @@ #define HCI_H4_TASK_PINNED_TO_CORE (TASK_PINNED_TO_CORE) #define HCI_H4_TASK_STACK_SIZE (2048 + BT_TASK_EXTRA_STACK_SIZE) -#define HCI_H4_TASK_PRIO (configMAX_PRIORITIES - 4) +#define HCI_H4_TASK_PRIO (BT_TASK_MAX_PRIORITIES - 4) #define HCI_H4_TASK_NAME "hciH4T" -#define HCI_H4_QUEUE_LEN 1 #if (C2H_FLOW_CONTROL_INCLUDED == TRUE) diff --git a/components/bt/bluedroid/hci/hci_layer.c b/components/bt/bluedroid/hci/hci_layer.c index 67ed0a2bcc..00c6f19e7c 100644 --- a/components/bt/bluedroid/hci/hci_layer.c +++ b/components/bt/bluedroid/hci/hci_layer.c @@ -36,9 +36,8 @@ #define HCI_HOST_TASK_PINNED_TO_CORE (TASK_PINNED_TO_CORE) #define HCI_HOST_TASK_STACK_SIZE (2048 + BT_TASK_EXTRA_STACK_SIZE) -#define HCI_HOST_TASK_PRIO (configMAX_PRIORITIES - 3) +#define HCI_HOST_TASK_PRIO (BT_TASK_MAX_PRIORITIES - 3) #define HCI_HOST_TASK_NAME "hciHostT" -#define HCI_HOST_QUEUE_LEN 40 typedef struct { uint16_t opcode; diff --git a/components/bt/bluedroid/osi/allocator.c b/components/bt/bluedroid/osi/allocator.c index 866ccc2e26..de8c8da722 100644 --- a/components/bt/bluedroid/osi/allocator.c +++ b/components/bt/bluedroid/osi/allocator.c @@ -24,7 +24,8 @@ extern void *pvPortZalloc(size_t size); extern void vPortFree(void *pv); -#ifdef CONFIG_BT_BLUEDROID_MEM_DEBUG + +#if HEAP_MEMORY_DEBUG #define OSI_MEM_DBG_INFO_MAX 1024*3 typedef struct { @@ -130,48 +131,48 @@ char *osi_strdup(const char *str) void *osi_malloc_func(size_t size) { -#ifdef CONFIG_BT_BLUEDROID_MEM_DEBUG +#if HEAP_MEMORY_DEBUG void *p; -#if CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST +#if HEAP_ALLOCATION_FROM_SPIRAM_FIRST p = heap_caps_malloc_prefer(size, 2, MALLOC_CAP_DEFAULT|MALLOC_CAP_SPIRAM, MALLOC_CAP_DEFAULT|MALLOC_CAP_INTERNAL); #else p = malloc(size); -#endif /* #if CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST */ +#endif /* #if HEAP_ALLOCATION_FROM_SPIRAM_FIRST */ osi_mem_dbg_record(p, size, __func__, __LINE__); return p; #else -#if CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST +#if HEAP_ALLOCATION_FROM_SPIRAM_FIRST return heap_caps_malloc_prefer(size, 2, MALLOC_CAP_DEFAULT|MALLOC_CAP_SPIRAM, MALLOC_CAP_DEFAULT|MALLOC_CAP_INTERNAL); #else return malloc(size); -#endif /* #if CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST */ -#endif /* #ifdef CONFIG_BT_BLUEDROID_MEM_DEBUG */ +#endif /* #if HEAP_ALLOCATION_FROM_SPIRAM_FIRST */ +#endif /* #if HEAP_MEMORY_DEBUG */ } void *osi_calloc_func(size_t size) { -#ifdef CONFIG_BT_BLUEDROID_MEM_DEBUG +#if HEAP_MEMORY_DEBUG void *p; -#if CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST +#if HEAP_ALLOCATION_FROM_SPIRAM_FIRST p = heap_caps_calloc_prefer(1, size, 2, MALLOC_CAP_DEFAULT|MALLOC_CAP_SPIRAM, MALLOC_CAP_DEFAULT|MALLOC_CAP_INTERNAL); #else p = calloc(1, size); -#endif /* #if CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST */ +#endif /* #if HEAP_ALLOCATION_FROM_SPIRAM_FIRST */ osi_mem_dbg_record(p, size, __func__, __LINE__); return p; #else -#if CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST +#if HEAP_ALLOCATION_FROM_SPIRAM_FIRST return heap_caps_calloc_prefer(1, size, 2, MALLOC_CAP_DEFAULT|MALLOC_CAP_SPIRAM, MALLOC_CAP_DEFAULT|MALLOC_CAP_INTERNAL); #else return calloc(1, size); -#endif /* #if CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST */ -#endif /* #ifdef CONFIG_BT_BLUEDROID_MEM_DEBUG */ +#endif /* #if HEAP_ALLOCATION_FROM_SPIRAM_FIRST */ +#endif /* #if HEAP_MEMORY_DEBUG */ } void osi_free_func(void *ptr) { -#ifdef CONFIG_BT_BLUEDROID_MEM_DEBUG - osi_mem_dbg_clean(ptr, __func__, __LINE__); +#if HEAP_MEMORY_DEBUG + osi_mem_dbg_clean(ptr, __func__, __LINE__); #endif free(ptr); } diff --git a/components/bt/bluedroid/osi/include/osi/allocator.h b/components/bt/bluedroid/osi/include/osi/allocator.h index 3be366e76a..f30987e058 100644 --- a/components/bt/bluedroid/osi/include/osi/allocator.h +++ b/components/bt/bluedroid/osi/include/osi/allocator.h @@ -22,7 +22,6 @@ #include #include #include "esp_heap_caps.h" -#include "sdkconfig.h" char *osi_strdup(const char *str); @@ -30,14 +29,14 @@ void *osi_malloc_func(size_t size); void *osi_calloc_func(size_t size); void osi_free_func(void *ptr); -#ifdef CONFIG_BT_BLUEDROID_MEM_DEBUG +#if HEAP_MEMORY_DEBUG void osi_mem_dbg_init(void); void osi_mem_dbg_record(void *p, int size, const char *func, int line); void osi_mem_dbg_clean(void *p, const char *func, int line); void osi_mem_dbg_show(void); -#if CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST +#if HEAP_ALLOCATION_FROM_SPIRAM_FIRST #define osi_malloc(size) \ ({ \ void *p; \ @@ -76,7 +75,7 @@ void osi_mem_dbg_show(void); (void *)p; \ }) -#endif /* #if CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST */ +#endif /* #if HEAP_ALLOCATION_FROM_SPIRAM_FIRST */ #if 0 @@ -84,11 +83,11 @@ void osi_mem_dbg_show(void); do { \ void *p; \ \ -#if CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST \ +#if HEAP_ALLOCATION_FROM_SPIRAM_FIRST \ p = heap_caps_malloc_prefer(size, 2, MALLOC_CAP_DEFAULT|MALLOC_CAP_SPIRAM, MALLOC_CAP_DEFAULT|MALLOC_CAP_INTERNAL); \ #else \ p = malloc((size)); \ -#endif /* #if CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST */ \ +#endif /* #if HEAP_ALLOCATION_FROM_SPIRAM_FIRST */ \ osi_mem_dbg_record(p, size, __func__, __LINE__); \ (void *)p; \ }while(0) @@ -97,13 +96,13 @@ do { \ do { \ void *p; \ \ -#if CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST \ +#if HEAP_ALLOCATION_FROM_SPIRAM_FIRST \ p = heap_caps_calloc_prefer(1, size, 2, \ MALLOC_CAP_DEFAULT|MALLOC_CAP_SPIRAM, \ MALLOC_CAP_DEFAULT|MALLOC_CAP_INTERNAL); \ #else \ p = calloc(1, (size)); \ -#endif /* #if CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST */ \ +#endif /* #if HEAP_ALLOCATION_FROM_SPIRAM_FIRST */ \ osi_mem_dbg_record(p, size, __func__, __LINE__); \ (void *)p; \ } while(0) @@ -118,16 +117,16 @@ do { \ #else -#if CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST +#if HEAP_ALLOCATION_FROM_SPIRAM_FIRST #define osi_malloc(size) heap_caps_malloc_prefer(size, 2, MALLOC_CAP_DEFAULT|MALLOC_CAP_SPIRAM, MALLOC_CAP_DEFAULT|MALLOC_CAP_INTERNAL) #define osi_calloc(size) heap_caps_calloc_prefer(1, size, 2, MALLOC_CAP_DEFAULT|MALLOC_CAP_SPIRAM, MALLOC_CAP_DEFAULT|MALLOC_CAP_INTERNAL) #else #define osi_malloc(size) malloc((size)) #define osi_calloc(size) calloc(1, (size)) -#endif /* #if CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST */ +#endif /* #if HEAP_ALLOCATION_FROM_SPIRAM_FIRST */ #define osi_free(p) free((p)) -#endif /* CONFIG_BT_BLUEDROID_MEM_DEBUG */ +#endif /* HEAP_MEMORY_DEBUG */ #define FREE_AND_RESET(a) \ do { \ diff --git a/components/bt/bluedroid/stack/btm/btm_sec.c b/components/bt/bluedroid/stack/btm/btm_sec.c index 1ad3cfce3b..0a61eac6fd 100644 --- a/components/bt/bluedroid/stack/btm/btm_sec.c +++ b/components/bt/bluedroid/stack/btm/btm_sec.c @@ -1789,53 +1789,6 @@ UINT16 BTM_BuildOobData(UINT8 *p_data, UINT16 max_len, BT_OCTET16 c, return len; } -/******************************************************************************* -** -** Function BTM_BothEndsSupportSecureConnections -** -** Description This function is called to check if both the local device and the peer device -** specified by bd_addr support BR/EDR Secure Connections. -** -** Parameters: bd_addr - address of the peer -** -** Returns TRUE if BR/EDR Secure Connections are supported by both local -** and the remote device. -** else FALSE. -** -*******************************************************************************/ -BOOLEAN BTM_BothEndsSupportSecureConnections(BD_ADDR bd_addr) -{ - return ((controller_get_interface()->supports_secure_connections()) && - (BTM_PeerSupportsSecureConnections(bd_addr))); -} - -/******************************************************************************* -** -** Function BTM_PeerSupportsSecureConnections -** -** Description This function is called to check if the peer supports -** BR/EDR Secure Connections. -** -** Parameters: bd_addr - address of the peer -** -** Returns TRUE if BR/EDR Secure Connections are supported by the peer, -** else FALSE. -** -*******************************************************************************/ -BOOLEAN BTM_PeerSupportsSecureConnections(BD_ADDR bd_addr) -{ - tBTM_SEC_DEV_REC *p_dev_rec; - - if ((p_dev_rec = btm_find_dev(bd_addr)) == NULL) { - BTM_TRACE_WARNING("%s: unknown BDA: %08x%04x\n", __FUNCTION__, - (bd_addr[0] << 24) + (bd_addr[1] << 16) + (bd_addr[2] << 8) + bd_addr[3], - (bd_addr[4] << 8) + bd_addr[5]); - return FALSE; - } - - return (p_dev_rec->remote_supports_secure_connections); -} - /******************************************************************************* ** ** Function BTM_ReadOobData @@ -1899,6 +1852,54 @@ UINT8 *BTM_ReadOobData(UINT8 *p_data, UINT8 eir_tag, UINT8 *p_len) } #endif ///BTM_OOB_INCLUDED == TRUE && SMP_INCLUDED == TRUE +#if (CLASSIC_BT_INCLUDED == TRUE) +/******************************************************************************* +** +** Function BTM_BothEndsSupportSecureConnections +** +** Description This function is called to check if both the local device and the peer device +** specified by bd_addr support BR/EDR Secure Connections. +** +** Parameters: bd_addr - address of the peer +** +** Returns TRUE if BR/EDR Secure Connections are supported by both local +** and the remote device. +** else FALSE. +** +*******************************************************************************/ +BOOLEAN BTM_BothEndsSupportSecureConnections(BD_ADDR bd_addr) +{ + return ((controller_get_interface()->supports_secure_connections()) && + (BTM_PeerSupportsSecureConnections(bd_addr))); +} + +/******************************************************************************* +** +** Function BTM_PeerSupportsSecureConnections +** +** Description This function is called to check if the peer supports +** BR/EDR Secure Connections. +** +** Parameters: bd_addr - address of the peer +** +** Returns TRUE if BR/EDR Secure Connections are supported by the peer, +** else FALSE. +** +*******************************************************************************/ +BOOLEAN BTM_PeerSupportsSecureConnections(BD_ADDR bd_addr) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + + if ((p_dev_rec = btm_find_dev(bd_addr)) == NULL) { + BTM_TRACE_WARNING("%s: unknown BDA: %08x%04x\n", __FUNCTION__, + (bd_addr[0] << 24) + (bd_addr[1] << 16) + (bd_addr[2] << 8) + bd_addr[3], + (bd_addr[4] << 8) + bd_addr[5]); + return FALSE; + } + + return (p_dev_rec->remote_supports_secure_connections); +} + /******************************************************************************* ** ** Function BTM_SetOutService @@ -1913,7 +1914,6 @@ UINT8 *BTM_ReadOobData(UINT8 *p_data, UINT8 eir_tag, UINT8 *p_len) ** Returns void ** *******************************************************************************/ -#if (CLASSIC_BT_INCLUDED == TRUE) void BTM_SetOutService(BD_ADDR bd_addr, UINT8 service_id, UINT32 mx_chan_id) { tBTM_SEC_DEV_REC *p_dev_rec; diff --git a/components/bt/bluedroid/stack/btu/btu_init.c b/components/bt/bluedroid/stack/btu/btu_init.c index 510fab7ecf..bf3a55af8c 100644 --- a/components/bt/bluedroid/stack/btu/btu_init.c +++ b/components/bt/bluedroid/stack/btu/btu_init.c @@ -46,9 +46,8 @@ #define BTU_TASK_PINNED_TO_CORE (TASK_PINNED_TO_CORE) #define BTU_TASK_STACK_SIZE (4096 + BT_TASK_EXTRA_STACK_SIZE) -#define BTU_TASK_PRIO (configMAX_PRIORITIES - 5) +#define BTU_TASK_PRIO (BT_TASK_MAX_PRIORITIES - 5) #define BTU_TASK_NAME "btuT" -#define BTU_QUEUE_LEN 50 hash_map_t *btu_general_alarm_hash_map; osi_mutex_t btu_general_alarm_lock; @@ -219,7 +218,7 @@ void BTU_ShutDown(void) btu_thread = NULL; } - btu_general_alarm_hash_map = NULL; + btu_general_alarm_hash_map = NULL; btu_oneshot_alarm_hash_map = NULL; btu_l2cap_alarm_hash_map = NULL; } diff --git a/components/bt/bluedroid/stack/include/stack/dyn_mem.h b/components/bt/bluedroid/stack/include/stack/dyn_mem.h index f3f33537ca..2b2db28453 100644 --- a/components/bt/bluedroid/stack/include/stack/dyn_mem.h +++ b/components/bt/bluedroid/stack/include/stack/dyn_mem.h @@ -18,8 +18,8 @@ #ifndef DYN_MEM_H #define DYN_MEM_H -#include "sdkconfig.h" -#if CONFIG_BT_BLE_DYNAMIC_ENV_MEMORY +#include "common/bt_target.h" +#if BT_BLE_DYNAMIC_ENV_MEMORY #define BTU_DYNAMIC_MEMORY TRUE #define BTM_DYNAMIC_MEMORY TRUE #define L2C_DYNAMIC_MEMORY TRUE @@ -53,7 +53,7 @@ #define BTC_SBC_DEC_DYNAMIC_MEMORY TRUE #define BTC_SBC_ENC_DYNAMIC_MEMORY TRUE -#else /* #if CONFIG_BT_BLE_DYNAMIC_ENV_MEMORY */ +#else /* #if BT_BLE_DYNAMIC_ENV_MEMORY */ #define SDP_DYNAMIC_MEMORY FALSE #define RFC_DYNAMIC_MEMORY FALSE @@ -82,7 +82,7 @@ #define BTC_SBC_DEC_DYNAMIC_MEMORY FALSE #define BTC_SBC_ENC_DYNAMIC_MEMORY FALSE -#endif /* #if CONFIG_BT_BLE_DYNAMIC_ENV_MEMORY */ +#endif /* #if BT_BLE_DYNAMIC_ENV_MEMORY */ /**************************************************************************** ** Define memory usage for each CORE component (if not defined in bdroid_buildcfg.h) ** The default for each component is to use static memory allocations. From 37cb673cd720d1e0ee19e5f92a4f3e2ad253e1a6 Mon Sep 17 00:00:00 2001 From: "Michael (XIAO Xufeng)" Date: Wed, 19 Jun 2019 20:35:55 +0800 Subject: [PATCH 084/486] esp_flash: update the document to the latest API --- components/spi_flash/README.rst | 12 +++++- components/spi_flash/esp_flash_api.c | 28 ++++++------- components/spi_flash/include/esp_flash.h | 44 +++++++++++---------- docs/Doxyfile | 3 +- docs/en/api-reference/storage/spi_flash.rst | 3 +- docs/sphinx-known-warnings.txt | 2 + 6 files changed, 53 insertions(+), 39 deletions(-) diff --git a/components/spi_flash/README.rst b/components/spi_flash/README.rst index 26387c04a5..a6f37b53cb 100644 --- a/components/spi_flash/README.rst +++ b/components/spi_flash/README.rst @@ -13,13 +13,21 @@ the "main" SPI flash chip (the same SPI flash chip from which program runs). With different chip pointers, you can access to external flashes chips on not only SPI0/1 but also HSPI/VSPI buses. -Kconfig option :ref:``CONFIG_SPI_FLASH_USE_LEGACY_IMPL`` can be used to switch +.. note:: + + Flash APIs after IDF v4.0 are no longer *atomic*. A writing operation + during another on-going read operation, on the overlapped flash address, + may cause the return data from the read operation to be partly same as + before, and partly updated as new written. + + +Kconfig option :ref:`CONFIG_SPI_FLASH_USE_LEGACY_IMPL` can be used to switch ``spi_flash_*`` functions back to the implementation before IDF v4.0. However, the code size may get bigger if you use the new API and the old API the same time. Encrypted reads and writes use the old implementation, even if -:ref:``CONFIG_SPI_FLASH_USE_LEGACY_IMPL`` is not enabled. As such, encrypted +:ref:`CONFIG_SPI_FLASH_USE_LEGACY_IMPL` is not enabled. As such, encrypted flash operations are only supported with the main flash chip (and not with other flash chips on SPI1 with different CS). diff --git a/components/spi_flash/esp_flash_api.c b/components/spi_flash/esp_flash_api.c index 48533ec573..58a8c30f2a 100644 --- a/components/spi_flash/esp_flash_api.c +++ b/components/spi_flash/esp_flash_api.c @@ -241,7 +241,7 @@ static esp_err_t IRAM_ATTR detect_spi_flash_chip(esp_flash_t *chip) } \ } while (0) -esp_err_t IRAM_ATTR esp_flash_read_id(esp_flash_t *chip, uint32_t *id) +esp_err_t IRAM_ATTR esp_flash_read_id(esp_flash_t *chip, uint32_t *out_id) { if (chip == NULL) { chip = esp_flash_default_chip; @@ -249,7 +249,7 @@ esp_err_t IRAM_ATTR esp_flash_read_id(esp_flash_t *chip, uint32_t *id) if (chip == NULL || !esp_flash_chip_driver_initialized(chip)) { return ESP_ERR_FLASH_NOT_INITIALISED; } - if (id == NULL) { + if (out_id == NULL) { return ESP_ERR_INVALID_ARG; } esp_err_t err = spiflash_start(chip); @@ -257,19 +257,19 @@ esp_err_t IRAM_ATTR esp_flash_read_id(esp_flash_t *chip, uint32_t *id) return err; } - err = chip->host->read_id(chip->host, id); + err = chip->host->read_id(chip->host, out_id); return spiflash_end(chip, err); } -esp_err_t IRAM_ATTR esp_flash_get_size(esp_flash_t *chip, uint32_t *size) +esp_err_t IRAM_ATTR esp_flash_get_size(esp_flash_t *chip, uint32_t *out_size) { VERIFY_OP(detect_size); - if (size == NULL) { + if (out_size == NULL) { return ESP_ERR_INVALID_ARG; } if (chip->size != 0) { - *size = chip->size; + *out_size = chip->size; return ESP_OK; } @@ -401,7 +401,7 @@ esp_err_t IRAM_ATTR esp_flash_get_chip_write_protect(esp_flash_t *chip, bool *wr return spiflash_end(chip, err); } -esp_err_t IRAM_ATTR esp_flash_set_chip_write_protect(esp_flash_t *chip, bool write_protect_chip) +esp_err_t IRAM_ATTR esp_flash_set_chip_write_protect(esp_flash_t *chip, bool write_protect) { VERIFY_OP(set_chip_write_protect); //TODO: skip writing if already locked or unlocked @@ -411,24 +411,24 @@ esp_err_t IRAM_ATTR esp_flash_set_chip_write_protect(esp_flash_t *chip, bool wri return err; } - err = chip->chip_drv->set_chip_write_protect(chip, write_protect_chip); + err = chip->chip_drv->set_chip_write_protect(chip, write_protect); return spiflash_end(chip, err); } -esp_err_t esp_flash_get_protectable_regions(const esp_flash_t *chip, const esp_flash_region_t **regions, uint32_t *num_regions) +esp_err_t esp_flash_get_protectable_regions(const esp_flash_t *chip, const esp_flash_region_t **out_regions, uint32_t *out_num_regions) { - if(num_regions != NULL) { - *num_regions = 0; // In case caller doesn't check result + if(out_num_regions != NULL) { + *out_num_regions = 0; // In case caller doesn't check result } VERIFY_OP(get_protected_regions); - if(regions == NULL || num_regions == NULL) { + if(out_regions == NULL || out_num_regions == NULL) { return ESP_ERR_INVALID_ARG; } - *num_regions = chip->chip_drv->num_protectable_regions; - *regions = chip->chip_drv->protectable_regions; + *out_num_regions = chip->chip_drv->num_protectable_regions; + *out_regions = chip->chip_drv->protectable_regions; return ESP_OK; } diff --git a/components/spi_flash/include/esp_flash.h b/components/spi_flash/include/esp_flash.h index 81524dc5d2..84a349b489 100644 --- a/components/spi_flash/include/esp_flash.h +++ b/components/spi_flash/include/esp_flash.h @@ -30,11 +30,11 @@ typedef struct esp_flash_t esp_flash_t; /** @brief Structure for describing a region of flash */ typedef struct { - uint32_t offset; - uint32_t size; + uint32_t offset; ///< Start address of this region + uint32_t size; ///< Size of the region } esp_flash_region_t; -/* OS-level integration hooks for accessing flash chips inside a running OS */ +/** OS-level integration hooks for accessing flash chips inside a running OS */ typedef struct { /** * Called before commencing any flash operation. Does not need to be @@ -51,16 +51,16 @@ typedef struct { /** @brief Structure to describe a SPI flash chip connected to the system. - Structure must be passed to esp_flash_init() before use. + Structure must be initialized before use (passed to esp_flash_init()). */ struct esp_flash_t { + spi_flash_host_driver_t *host; ///< Pointer to hardware-specific "host_driver" structure. Must be initialized before used. const spi_flash_chip_t *chip_drv; ///< Pointer to chip-model-specific "adapter" structure. If NULL, will be detected during initialisation. - spi_flash_host_driver_t *host; ///< Pointer to hardware-specific "host_driver" structure. - const esp_flash_os_functions_t *os_func; ///< Pointer to os-specific hook structure. - void *os_func_data; ///< Pointer to argument for os-specific hooks. + const esp_flash_os_functions_t *os_func; ///< Pointer to os-specific hook structure. Call ``esp_flash_init_os_functions()`` to setup this field, after the host is properly initialized. + void *os_func_data; ///< Pointer to argument for os-specific hooks. Left NULL and will be initialized with ``os_func``. - esp_flash_read_mode_t read_mode; ///< Configured SPI flash read mode. Set before initialisation. + esp_flash_read_mode_t read_mode; ///< Configured SPI flash read mode. Set before ``esp_flash_init`` is called. uint32_t size; ///< Size of SPI flash in bytes. If 0, size will be detected during initialisation. }; @@ -69,11 +69,13 @@ struct esp_flash_t { * * This function must be called before any other API functions are called for this chip. * - * @note Only the host, speed & read_mode fields of the chip structure need to be initialised. Other fields will be auto-detected - * if left set to zero or NULL. + * @note Only the ``host`` and ``read_mode`` fields of the chip structure must + * be initialised before this function is called. Other fields may be + * auto-detected if left set to zero or NULL. * - * @note If the chip->drv pointer is NULL, chip chip_drv will be autodetected based on its manufacturer & product IDs. See - * esp_flash_registered_flash_drivers pointer for details of this process. + * @note If the chip->drv pointer is NULL, chip chip_drv will be auto-detected + * based on its manufacturer & product IDs. See + * ``esp_flash_registered_flash_drivers`` pointer for details of this process. * * @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted. * @return ESP_OK on success, or a flash error code if initialisation fails. @@ -92,25 +94,25 @@ bool esp_flash_chip_driver_initialized(const esp_flash_t *chip); /** @brief Read flash ID via the common "RDID" SPI flash command. * * @param chip Pointer to identify flash chip. Must have been successfully initialised via esp_flash_init() - * @param[out] Pointer to receive ID value. + * @param[out] out_id Pointer to receive ID value. * * ID is a 24-bit value. Lower 16 bits of 'id' are the chip ID, upper 8 bits are the manufacturer ID. * * @return ESP_OK on success, or a flash error code if operation failed. */ -esp_err_t esp_flash_read_id(esp_flash_t *chip, uint32_t *id); +esp_err_t esp_flash_read_id(esp_flash_t *chip, uint32_t *out_id); /** @brief Detect flash size based on flash ID. * * @param chip Pointer to identify flash chip. Must have been successfully initialised via esp_flash_init() - * @param[out] Detected size in bytes. + * @param[out] out_size Detected size in bytes. * * @note Most flash chips use a common format for flash ID, where the lower 4 bits specify the size as a power of 2. If * the manufacturer doesn't follow this convention, the size may be incorrectly detected. * * @return ESP_OK on success, or a flash error code if operation failed. */ -esp_err_t esp_flash_get_size(esp_flash_t *chip, uint32_t *size); +esp_err_t esp_flash_get_size(esp_flash_t *chip, uint32_t *out_size); /** @brief Erase flash chip contents * @@ -153,7 +155,7 @@ esp_err_t esp_flash_get_chip_write_protect(esp_flash_t *chip, bool *write_protec /** @brief Set write protection for the SPI flash chip * * @param chip Pointer to identify flash chip. Must have been successfully initialised via esp_flash_init() - * @param write_protected Boolean value for the write protect flag + * @param write_protect Boolean value for the write protect flag * * @note Correct behaviour of this function depends on the SPI flash chip model and chip_drv in use (via the 'chip->drv' * field). @@ -165,21 +167,21 @@ esp_err_t esp_flash_get_chip_write_protect(esp_flash_t *chip, bool *write_protec * * @return ESP_OK on success, or a flash error code if operation failed. */ -esp_err_t esp_flash_set_chip_write_protect(esp_flash_t *chip, bool write_protect_chip); +esp_err_t esp_flash_set_chip_write_protect(esp_flash_t *chip, bool write_protect); /** @brief Read the list of individually protectable regions of this SPI flash chip. * * @param chip Pointer to identify flash chip. Must have been successfully initialised via esp_flash_init() - * @param regions[out] Pointer to receive a pointer to the array of protectable regions of the chip. - * @param[out] Pointer to an integer receiving the count of protectable regions in the array returned in 'regions'. + * @param[out] out_regions Pointer to receive a pointer to the array of protectable regions of the chip. + * @param[out] out_num_regions Pointer to an integer receiving the count of protectable regions in the array returned in 'regions'. * * @note Correct behaviour of this function depends on the SPI flash chip model and chip_drv in use (via the 'chip->drv' * field). * * @return ESP_OK on success, or a flash error code if operation failed. */ -esp_err_t esp_flash_get_protectable_regions(const esp_flash_t *chip, const esp_flash_region_t **regions, uint32_t *num_regions); +esp_err_t esp_flash_get_protectable_regions(const esp_flash_t *chip, const esp_flash_region_t **out_regions, uint32_t *out_num_regions); /** @brief Detect if a region of the SPI flash chip is protected diff --git a/docs/Doxyfile b/docs/Doxyfile index 858a6a39e0..3434ec5082 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -127,9 +127,10 @@ INPUT = \ ## Storage - API Reference ## ## SPI Flash and Partition APIs - ../../components/spi_flash/include/esp_spi_flash.h \ + ../../components/spi_flash/include/esp_flash.h \ ../../components/spi_flash/include/esp_partition.h \ ../../components/bootloader_support/include/esp_flash_encrypt.h \ + ../../components/soc/include/hal/spi_flash_types.h \ ## SPIFFS ../../components/spiffs/include/esp_spiffs.h \ ## SD/MMC Card Host diff --git a/docs/en/api-reference/storage/spi_flash.rst b/docs/en/api-reference/storage/spi_flash.rst index 5595011d77..ff70cfd35b 100644 --- a/docs/en/api-reference/storage/spi_flash.rst +++ b/docs/en/api-reference/storage/spi_flash.rst @@ -31,7 +31,8 @@ In a single core environment (:ref:`CONFIG_FREERTOS_UNICORE` enabled), you need API Reference - SPI Flash ------------------------- -.. include:: /_build/inc/esp_spi_flash.inc +.. include:: /_build/inc/esp_flash.inc +.. include:: /_build/inc/spi_flash_types.inc API Reference - Partition Table ------------------------------- diff --git a/docs/sphinx-known-warnings.txt b/docs/sphinx-known-warnings.txt index 72fb40a5b6..fe496b067f 100644 --- a/docs/sphinx-known-warnings.txt +++ b/docs/sphinx-known-warnings.txt @@ -77,6 +77,8 @@ If type alias or template alias: # spi_master.inc:line: WARNING: Duplicate declaration, struct spi_transaction_t spi_transaction_t spi_slave.inc:line: WARNING: Duplicate declaration, struct spi_slave_transaction_t spi_slave_transaction_t +esp_flash.inc:line: WARNING: Duplicate declaration, struct esp_flash_t esp_flash_t +spi_flash_types.inc:line: WARNING: Duplicate declaration, struct spi_flash_host_driver_t spi_flash_host_driver_t wear-levelling.rst:line: WARNING: Duplicate declaration, bool esp_vfs_fat_mount_config_t::format_if_mount_failed wear-levelling.rst:line: WARNING: Duplicate declaration, int esp_vfs_fat_mount_config_t::max_files wear-levelling.rst:line: WARNING: Duplicate declaration, size_t esp_vfs_fat_mount_config_t::allocation_unit_size From 2a2d932cfe2404057c71bc91d9d9416200e67a03 Mon Sep 17 00:00:00 2001 From: Tuan Date: Thu, 20 Jun 2019 15:37:40 +0800 Subject: [PATCH 085/486] esp_websocket_client: Add websocket client component Closes https://github.com/espressif/esp-idf/issues/2829 --- .../esp_websocket_client/CMakeLists.txt | 4 + components/esp_websocket_client/component.mk | 0 .../esp_websocket_client.c | 606 ++++++++++++++++++ .../include/esp_websocket_client.h | 195 ++++++ components/mqtt/esp-mqtt | 2 +- .../tcp_transport/include/esp_transport_ws.h | 17 + components/tcp_transport/transport_ssl.c | 2 + components/tcp_transport/transport_ws.c | 87 ++- docs/Doxyfile | 1 + .../protocols/esp_websocket_client.rst | 70 ++ docs/en/api-reference/protocols/index.rst | 1 + .../protocols/esp_websocket_client.rst | 1 + docs/zh_CN/api-reference/protocols/index.rst | 1 + examples/protocols/websocket/CMakeLists.txt | 10 + examples/protocols/websocket/Makefile | 10 + examples/protocols/websocket/README.md | 1 + examples/protocols/websocket/example_test.py | 41 ++ .../protocols/websocket/main/CMakeLists.txt | 4 + .../websocket/main/Kconfig.projbuild | 9 + .../protocols/websocket/main/component.mk | 0 .../websocket/main/websocket_example.c | 103 +++ 21 files changed, 1147 insertions(+), 18 deletions(-) create mode 100644 components/esp_websocket_client/CMakeLists.txt create mode 100644 components/esp_websocket_client/component.mk create mode 100644 components/esp_websocket_client/esp_websocket_client.c create mode 100644 components/esp_websocket_client/include/esp_websocket_client.h create mode 100644 docs/en/api-reference/protocols/esp_websocket_client.rst create mode 100644 docs/zh_CN/api-reference/protocols/esp_websocket_client.rst create mode 100644 examples/protocols/websocket/CMakeLists.txt create mode 100644 examples/protocols/websocket/Makefile create mode 100644 examples/protocols/websocket/README.md create mode 100644 examples/protocols/websocket/example_test.py create mode 100644 examples/protocols/websocket/main/CMakeLists.txt create mode 100644 examples/protocols/websocket/main/Kconfig.projbuild create mode 100644 examples/protocols/websocket/main/component.mk create mode 100644 examples/protocols/websocket/main/websocket_example.c diff --git a/components/esp_websocket_client/CMakeLists.txt b/components/esp_websocket_client/CMakeLists.txt new file mode 100644 index 0000000000..723199ce0f --- /dev/null +++ b/components/esp_websocket_client/CMakeLists.txt @@ -0,0 +1,4 @@ +set(COMPONENT_SRCS "esp_websocket_client.c") +set(COMPONENT_ADD_INCLUDEDIRS "include") +set(COMPONENT_REQUIRES lwip esp-tls tcp_transport nghttp) +register_component() diff --git a/components/esp_websocket_client/component.mk b/components/esp_websocket_client/component.mk new file mode 100644 index 0000000000..e69de29bb2 diff --git a/components/esp_websocket_client/esp_websocket_client.c b/components/esp_websocket_client/esp_websocket_client.c new file mode 100644 index 0000000000..9d4b1f053f --- /dev/null +++ b/components/esp_websocket_client/esp_websocket_client.c @@ -0,0 +1,606 @@ +// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 "esp_websocket_client.h" +#include "esp_transport.h" +#include "esp_transport_tcp.h" +#include "esp_transport_ssl.h" +#include "esp_transport_ws.h" +#include "esp_transport_utils.h" +/* using uri parser */ +#include "http_parser.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/queue.h" +#include "freertos/event_groups.h" +#include "esp_log.h" +#include "esp_timer.h" + +static const char *TAG = "WEBSOCKET_CLIENT"; + +#define WEBSOCKET_TCP_DEFAULT_PORT (80) +#define WEBSOCKET_SSL_DEFAULT_PORT (443) +#define WEBSOCKET_BUFFER_SIZE_BYTE (1024) +#define WEBSOCKET_RECONNECT_TIMEOUT_MS (10*1000) +#define WEBSOCKET_TASK_PRIORITY (5) +#define WEBSOCKET_TASK_STACK (4*1024) +#define WEBSOCKET_NETWORK_TIMEOUT_MS (10*1000) +#define WEBSOCKET_PING_TIMEOUT_MS (10*1000) +#define WEBSOCKET_EVENT_QUEUE_SIZE (1) +#define WEBSOCKET_SEND_EVENT_TIMEOUT_MS (1000/portTICK_RATE_MS) + +const static int STOPPED_BIT = BIT0; + +ESP_EVENT_DEFINE_BASE(WEBSOCKET_EVENTS); + +typedef struct { + int task_stack; + int task_prio; + char *uri; + char *host; + char *path; + char *scheme; + char *username; + char *password; + int port; + bool auto_reconnect; + void *user_context; + int network_timeout_ms; +} websocket_config_storage_t; + +typedef enum { + WEBSOCKET_STATE_ERROR = -1, + WEBSOCKET_STATE_UNKNOW = 0, + WEBSOCKET_STATE_INIT, + WEBSOCKET_STATE_CONNECTED, + WEBSOCKET_STATE_WAIT_TIMEOUT, +} websocket_client_state_t; + +struct esp_websocket_client { + esp_event_loop_handle_t event_handle; + esp_transport_list_handle_t transport_list; + esp_transport_handle_t transport; + websocket_config_storage_t *config; + websocket_client_state_t state; + uint64_t keepalive_tick_ms; + uint64_t reconnect_tick_ms; + uint64_t ping_tick_ms; + int wait_timeout_ms; + int auto_reconnect; + bool run; + EventGroupHandle_t status_bits; + xSemaphoreHandle lock; + char *rx_buffer; + char *tx_buffer; + int buffer_size; +}; + +static uint64_t _tick_get_ms() +{ + return esp_timer_get_time()/1000; +} + +static esp_err_t esp_websocket_client_dispatch_event(esp_websocket_client_handle_t client, + esp_websocket_event_id_t event, + const char *data, + int data_len) +{ + esp_err_t err; + esp_websocket_event_data_t event_data; + event_data.data_ptr = data; + event_data.data_len = data_len; + + if ((err = esp_event_post_to(client->event_handle, + WEBSOCKET_EVENTS, event, + &event_data, + sizeof(esp_websocket_event_data_t), + WEBSOCKET_SEND_EVENT_TIMEOUT_MS)) != ESP_OK) { + return err; + } + return esp_event_loop_run(client->event_handle, WEBSOCKET_SEND_EVENT_TIMEOUT_MS); +} + +static esp_err_t esp_websocket_client_abort_connection(esp_websocket_client_handle_t client) +{ + esp_transport_close(client->transport); + client->wait_timeout_ms = WEBSOCKET_RECONNECT_TIMEOUT_MS; + client->reconnect_tick_ms = _tick_get_ms(); + client->state = WEBSOCKET_STATE_WAIT_TIMEOUT; + ESP_LOGI(TAG, "Reconnect after %d ms", client->wait_timeout_ms); + esp_websocket_client_dispatch_event(client, WEBSOCKET_EVENT_DISCONNECTED, NULL, 0); + return ESP_OK; +} + +static esp_err_t esp_websocket_client_set_config(esp_websocket_client_handle_t client, const esp_websocket_client_config_t *config) +{ + websocket_config_storage_t *cfg = client->config; + cfg->task_prio = config->task_prio; + if (cfg->task_prio <= 0) { + cfg->task_prio = WEBSOCKET_TASK_PRIORITY; + } + + cfg->task_stack = config->task_stack; + if (cfg->task_stack == 0) { + cfg->task_stack = WEBSOCKET_TASK_STACK; + } + + if (config->host) { + cfg->host = strdup(config->host); + ESP_TRANSPORT_MEM_CHECK(TAG, cfg->host, return ESP_ERR_NO_MEM); + } + + if (config->port) { + cfg->port = config->port; + } + + if (config->username) { + free(cfg->username); + cfg->username = strdup(config->username); + ESP_TRANSPORT_MEM_CHECK(TAG, cfg->username, { + free(cfg->host); + return ESP_ERR_NO_MEM; + }); + } + + if (config->password) { + free(cfg->password); + cfg->password = strdup(config->password); + ESP_TRANSPORT_MEM_CHECK(TAG, cfg->password, { + free(cfg->host); + free(cfg->username); + return ESP_ERR_NO_MEM; + }); + } + + if (config->uri) { + free(cfg->uri); + cfg->uri = strdup(config->uri); + ESP_TRANSPORT_MEM_CHECK(TAG, cfg->uri, { + free(cfg->host); + free(cfg->username); + free(cfg->password); + return ESP_ERR_NO_MEM; + }); + } + if (config->path) { + free(cfg->path); + cfg->path = strdup(config->path); + ESP_TRANSPORT_MEM_CHECK(TAG, cfg->path, { + free(cfg->uri); + free(cfg->host); + free(cfg->username); + free(cfg->password); + return ESP_ERR_NO_MEM; + }); + } + + cfg->network_timeout_ms = WEBSOCKET_NETWORK_TIMEOUT_MS; + cfg->user_context = config->user_context; + cfg->auto_reconnect = true; + if (config->disable_auto_reconnect) { + cfg->auto_reconnect = false; + } + + + return ESP_OK; +} + +static esp_err_t esp_websocket_client_destroy_config(esp_websocket_client_handle_t client) +{ + if (client == NULL) { + return ESP_ERR_INVALID_ARG; + } + websocket_config_storage_t *cfg = client->config; + if (client->config == NULL) { + return ESP_ERR_INVALID_ARG; + } + free(cfg->host); + free(cfg->uri); + free(cfg->path); + free(cfg->scheme); + free(cfg->username); + free(cfg->password); + memset(cfg, 0, sizeof(websocket_config_storage_t)); + free(client->config); + client->config = NULL; + return ESP_OK; +} + +esp_websocket_client_handle_t esp_websocket_client_init(const esp_websocket_client_config_t *config) +{ + esp_websocket_client_handle_t client = calloc(1, sizeof(struct esp_websocket_client)); + ESP_TRANSPORT_MEM_CHECK(TAG, client, return NULL); + + esp_event_loop_args_t event_args = { + .queue_size = WEBSOCKET_EVENT_QUEUE_SIZE, + .task_name = NULL // no task will be created + }; + + if (esp_event_loop_create(&event_args, &client->event_handle) != ESP_OK) { + ESP_LOGE(TAG, "Error create event handler for websocket client"); + free(client); + return NULL; + } + + client->lock = xSemaphoreCreateMutex(); + ESP_TRANSPORT_MEM_CHECK(TAG, client->lock, goto _websocket_init_fail); + + client->transport_list = esp_transport_list_init(); + ESP_TRANSPORT_MEM_CHECK(TAG, client->transport_list, goto _websocket_init_fail); + + esp_transport_handle_t tcp = esp_transport_tcp_init(); + ESP_TRANSPORT_MEM_CHECK(TAG, tcp, goto _websocket_init_fail); + + esp_transport_set_default_port(tcp, WEBSOCKET_TCP_DEFAULT_PORT); + esp_transport_list_add(client->transport_list, tcp, "_tcp"); // need to save to transport list, for cleanup + + + esp_transport_handle_t ws = esp_transport_ws_init(tcp); + ESP_TRANSPORT_MEM_CHECK(TAG, ws, goto _websocket_init_fail); + + esp_transport_set_default_port(ws, WEBSOCKET_TCP_DEFAULT_PORT); + esp_transport_list_add(client->transport_list, ws, "ws"); + if (config->transport == WEBSOCKET_TRANSPORT_OVER_TCP) { + asprintf(&client->config->scheme, "ws"); + ESP_TRANSPORT_MEM_CHECK(TAG, client->config->scheme, goto _websocket_init_fail); + } + + esp_transport_handle_t ssl = esp_transport_ssl_init(); + ESP_TRANSPORT_MEM_CHECK(TAG, ssl, goto _websocket_init_fail); + + esp_transport_set_default_port(ssl, WEBSOCKET_SSL_DEFAULT_PORT); + if (config->cert_pem) { + esp_transport_ssl_set_cert_data(ssl, config->cert_pem, strlen(config->cert_pem)); + } + esp_transport_list_add(client->transport_list, ssl, "_ssl"); // need to save to transport list, for cleanup + + esp_transport_handle_t wss = esp_transport_ws_init(ssl); + ESP_TRANSPORT_MEM_CHECK(TAG, wss, goto _websocket_init_fail); + + esp_transport_set_default_port(wss, WEBSOCKET_SSL_DEFAULT_PORT); + + esp_transport_list_add(client->transport_list, wss, "wss"); + if (config->transport == WEBSOCKET_TRANSPORT_OVER_TCP) { + asprintf(&client->config->scheme, "wss"); + ESP_TRANSPORT_MEM_CHECK(TAG, client->config->scheme, goto _websocket_init_fail); + } + + client->config = calloc(1, sizeof(websocket_config_storage_t)); + ESP_TRANSPORT_MEM_CHECK(TAG, client->config, goto _websocket_init_fail); + + if (config->uri) { + if (esp_websocket_client_set_uri(client, config->uri) != ESP_OK) { + ESP_LOGE(TAG, "Invalid uri"); + goto _websocket_init_fail; + } + } + + if (esp_websocket_client_set_config(client, config) != ESP_OK) { + ESP_LOGE(TAG, "Failed to set the configuration"); + goto _websocket_init_fail; + } + + if (client->config->scheme == NULL) { + asprintf(&client->config->scheme, "ws"); + ESP_TRANSPORT_MEM_CHECK(TAG, client->config->scheme, goto _websocket_init_fail); + } + + client->keepalive_tick_ms = _tick_get_ms(); + client->reconnect_tick_ms = _tick_get_ms(); + client->ping_tick_ms = _tick_get_ms(); + + int buffer_size = config->buffer_size; + if (buffer_size <= 0) { + buffer_size = WEBSOCKET_BUFFER_SIZE_BYTE; + } + client->rx_buffer = malloc(buffer_size); + ESP_TRANSPORT_MEM_CHECK(TAG, client->rx_buffer, { + goto _websocket_init_fail; + }); + client->tx_buffer = malloc(buffer_size); + ESP_TRANSPORT_MEM_CHECK(TAG, client->tx_buffer, { + goto _websocket_init_fail; + }); + client->status_bits = xEventGroupCreate(); + ESP_TRANSPORT_MEM_CHECK(TAG, client->status_bits, { + goto _websocket_init_fail; + }); + + client->buffer_size = buffer_size; + return client; + +_websocket_init_fail: + esp_websocket_client_destroy(client); + return NULL; +} + +esp_err_t esp_websocket_client_destroy(esp_websocket_client_handle_t client) +{ + if (client == NULL) { + return ESP_ERR_INVALID_ARG; + } + if (client->run) { + esp_websocket_client_stop(client); + } + if (client->event_handle) { + esp_event_loop_delete(client->event_handle); + } + esp_websocket_client_destroy_config(client); + esp_transport_list_destroy(client->transport_list); + vQueueDelete(client->lock); + free(client->tx_buffer); + free(client->rx_buffer); + if (client->status_bits) { + vEventGroupDelete(client->status_bits); + } + free(client); + client = NULL; + return ESP_OK; +} + +esp_err_t esp_websocket_client_set_uri(esp_websocket_client_handle_t client, const char *uri) +{ + if (client == NULL || uri == NULL) { + return ESP_ERR_INVALID_ARG; + } + struct http_parser_url puri; + http_parser_url_init(&puri); + int parser_status = http_parser_parse_url(uri, strlen(uri), 0, &puri); + if (parser_status != 0) { + ESP_LOGE(TAG, "Error parse uri = %s", uri); + return ESP_FAIL; + } + if (puri.field_data[UF_SCHEMA].len) { + free(client->config->scheme); + asprintf(&client->config->scheme, "%.*s", puri.field_data[UF_SCHEMA].len, uri + puri.field_data[UF_SCHEMA].off); + ESP_TRANSPORT_MEM_CHECK(TAG, client->config->scheme, return ESP_ERR_NO_MEM); + } + + if (puri.field_data[UF_HOST].len) { + free(client->config->host); + asprintf(&client->config->host, "%.*s", puri.field_data[UF_HOST].len, uri + puri.field_data[UF_HOST].off); + ESP_TRANSPORT_MEM_CHECK(TAG, client->config->host, return ESP_ERR_NO_MEM); + } + + + if (puri.field_data[UF_PATH].len) { + free(client->config->path); + asprintf(&client->config->path, "%.*s", puri.field_data[UF_PATH].len, uri + puri.field_data[UF_PATH].off); + ESP_TRANSPORT_MEM_CHECK(TAG, client->config->path, return ESP_ERR_NO_MEM); + + esp_transport_handle_t trans = esp_transport_list_get_transport(client->transport_list, "ws"); + if (trans) { + esp_transport_ws_set_path(trans, client->config->path); + } + trans = esp_transport_list_get_transport(client->transport_list, "wss"); + if (trans) { + esp_transport_ws_set_path(trans, client->config->path); + } + } + if (puri.field_data[UF_PORT].off) { + client->config->port = strtol((const char*)(uri + puri.field_data[UF_PORT].off), NULL, 10); + } + + if (puri.field_data[UF_USERINFO].len) { + char *user_info = NULL; + asprintf(&user_info, "%.*s", puri.field_data[UF_USERINFO].len, uri + puri.field_data[UF_USERINFO].off); + if (user_info) { + char *pass = strchr(user_info, ':'); + if (pass) { + pass[0] = 0; //terminal username + pass ++; + free(client->config->password); + client->config->password = strdup(pass); + ESP_TRANSPORT_MEM_CHECK(TAG, client->config->password, return ESP_ERR_NO_MEM); + } + free(client->config->username); + client->config->username = strdup(user_info); + ESP_TRANSPORT_MEM_CHECK(TAG, client->config->username, return ESP_ERR_NO_MEM); + free(user_info); + } else { + return ESP_ERR_NO_MEM; + } + } + return ESP_OK; +} + +static void esp_websocket_client_task(void *pv) +{ + int rlen; + esp_websocket_client_handle_t client = (esp_websocket_client_handle_t) pv; + client->run = true; + + //get transport by scheme + client->transport = esp_transport_list_get_transport(client->transport_list, client->config->scheme); + + if (client->transport == NULL) { + ESP_LOGE(TAG, "There are no transports valid, stop websocket client"); + client->run = false; + } + //default port + if (client->config->port == 0) { + client->config->port = esp_transport_get_default_port(client->transport); + } + + client->state = WEBSOCKET_STATE_INIT; + xEventGroupClearBits(client->status_bits, STOPPED_BIT); + int read_select; + while (client->run) { + switch ((int)client->state) { + case WEBSOCKET_STATE_INIT: + if (client->transport == NULL) { + ESP_LOGE(TAG, "There are no transport"); + client->run = false; + break; + } + + if (esp_transport_connect(client->transport, + client->config->host, + client->config->port, + client->config->network_timeout_ms) < 0) { + ESP_LOGE(TAG, "Error transport connect"); + esp_websocket_client_abort_connection(client); + break; + } + ESP_LOGD(TAG, "Transport connected to %s://%s:%d", client->config->scheme, client->config->host, client->config->port); + + client->state = WEBSOCKET_STATE_CONNECTED; + esp_websocket_client_dispatch_event(client, WEBSOCKET_EVENT_CONNECTED, NULL, 0); + + break; + case WEBSOCKET_STATE_CONNECTED: + read_select = esp_transport_poll_read(client->transport, 1000); //Poll every 1000ms + if (read_select < 0) { + ESP_LOGE(TAG, "Network error, errorno"); + esp_websocket_client_abort_connection(client); + break; + } + if (_tick_get_ms() - client->ping_tick_ms > WEBSOCKET_PING_TIMEOUT_MS) { + client->ping_tick_ms = _tick_get_ms(); + // Send PING + esp_transport_write(client->transport, NULL, 0, client->config->network_timeout_ms); + } + if (read_select == 0) { + ESP_LOGD(TAG, "Timeout..."); + continue; + } + client->ping_tick_ms = _tick_get_ms(); + + rlen = esp_transport_read(client->transport, client->rx_buffer, client->buffer_size, client->config->network_timeout_ms); + if (rlen < 0) { + ESP_LOGE(TAG, "Error read data"); + esp_websocket_client_abort_connection(client); + break; + } + if (rlen > 0) { + esp_websocket_client_dispatch_event(client, WEBSOCKET_EVENT_DATA, client->rx_buffer, rlen); + } + break; + case WEBSOCKET_STATE_WAIT_TIMEOUT: + + if (!client->config->auto_reconnect) { + client->run = false; + break; + } + if (_tick_get_ms() - client->reconnect_tick_ms > client->wait_timeout_ms) { + client->state = WEBSOCKET_STATE_INIT; + client->reconnect_tick_ms = _tick_get_ms(); + ESP_LOGD(TAG, "Reconnecting..."); + } + vTaskDelay(client->wait_timeout_ms / 2 / portTICK_RATE_MS); + break; + } + } + + esp_transport_close(client->transport); + xEventGroupSetBits(client->status_bits, STOPPED_BIT); + client->state = WEBSOCKET_STATE_UNKNOW; + vTaskDelete(NULL); +} + +esp_err_t esp_websocket_client_start(esp_websocket_client_handle_t client) +{ + if (client == NULL) { + return ESP_ERR_INVALID_ARG; + } + if (client->state >= WEBSOCKET_STATE_INIT) { + ESP_LOGE(TAG, "The client has started"); + return ESP_FAIL; + } + if (xTaskCreate(esp_websocket_client_task, "websocket_task", client->config->task_stack, client, client->config->task_prio, NULL) != pdTRUE) { + ESP_LOGE(TAG, "Error create websocket task"); + return ESP_FAIL; + } + xEventGroupClearBits(client->status_bits, STOPPED_BIT); + return ESP_OK; +} + +esp_err_t esp_websocket_client_stop(esp_websocket_client_handle_t client) +{ + if (client == NULL) { + return ESP_ERR_INVALID_ARG; + } + if (!client->run) { + ESP_LOGW(TAG, "Client was not started"); + return ESP_FAIL; + } + client->run = false; + xEventGroupWaitBits(client->status_bits, STOPPED_BIT, false, true, portMAX_DELAY); + client->state = WEBSOCKET_STATE_UNKNOW; + return ESP_OK; +} + +int esp_websocket_client_send(esp_websocket_client_handle_t client, const char *data, int len, TickType_t timeout) +{ + int need_write = len; + int wlen = 0, widx = 0; + + if (client == NULL || data == NULL || len <= 0) { + ESP_LOGE(TAG, "Invalid arguments"); + return ESP_FAIL; + } + + if (!esp_websocket_client_is_connected(client)) { + ESP_LOGE(TAG, "Websocket client is not connected"); + return ESP_FAIL; + } + + if (client->transport == NULL) { + ESP_LOGE(TAG, "Invalid transport"); + return ESP_FAIL; + } + + if (xSemaphoreTake(client->lock, timeout) != pdPASS) { + return ESP_FAIL; + } + + while (widx < len) { + if (need_write > client->buffer_size) { + need_write = client->buffer_size; + } + memcpy(client->tx_buffer, data + widx, need_write); + wlen = esp_transport_write(client->transport, + (char *)client->tx_buffer, + need_write, + client->config->network_timeout_ms); + if (wlen <= 0) { + xSemaphoreGive(client->lock); + return wlen; + } + widx += wlen; + need_write = len - widx; + } + xSemaphoreGive(client->lock); + return widx; +} + +bool esp_websocket_client_is_connected(esp_websocket_client_handle_t client) +{ + if (client == NULL) { + return false; + } + return client->state == WEBSOCKET_STATE_CONNECTED; +} + +esp_err_t esp_websocket_register_events(esp_websocket_client_handle_t client, + esp_websocket_event_id_t event, + esp_event_handler_t event_handler, + void* event_handler_arg) { + if (client == NULL) { + return ESP_ERR_INVALID_ARG; + } + return esp_event_handler_register_with(client->event_handle, WEBSOCKET_EVENTS, event, event_handler, event_handler_arg); +} diff --git a/components/esp_websocket_client/include/esp_websocket_client.h b/components/esp_websocket_client/include/esp_websocket_client.h new file mode 100644 index 0000000000..898fab5ae0 --- /dev/null +++ b/components/esp_websocket_client/include/esp_websocket_client.h @@ -0,0 +1,195 @@ +// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#ifndef _ESP_WEBSOCKET_CLIENT_H_ +#define _ESP_WEBSOCKET_CLIENT_H_ + + +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "esp_err.h" +#include "esp_event.h" +#include "esp_event_loop.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct esp_websocket_client* esp_websocket_client_handle_t; + +ESP_EVENT_DECLARE_BASE(WEBSOCKET_EVENTS); // declaration of the task events family + +/** + * @brief Websocket Client events id + */ +typedef enum { + WEBSOCKET_EVENT_ANY = -1, + WEBSOCKET_EVENT_ERROR = 0, /*!< This event occurs when there are any errors during execution */ + WEBSOCKET_EVENT_CONNECTED, /*!< Once the Websocket has been connected to the server, no data exchange has been performed */ + WEBSOCKET_EVENT_DISCONNECTED, /*!< The connection has been disconnected */ + WEBSOCKET_EVENT_DATA, /*!< When receiving data from the server, possibly multiple portions of the packet */ + WEBSOCKET_EVENT_MAX +} esp_websocket_event_id_t; + +/** + * @brief Websocket event data + */ +typedef struct { + const char *data_ptr; /*!< Data pointer */ + int data_len; /*!< Data length */ +} esp_websocket_event_data_t; + +/** + * @brief Websocket Client transport + */ +typedef enum { + WEBSOCKET_TRANSPORT_UNKNOWN = 0x0, /*!< Transport unknown */ + WEBSOCKET_TRANSPORT_OVER_TCP, /*!< Transport over tcp */ + WEBSOCKET_TRANSPORT_OVER_SSL, /*!< Transport over ssl */ +} esp_websocket_transport_t; + +/** + * @brief Websocket Client events data + */ +typedef struct { + esp_websocket_event_id_t event_id; /*!< event_id, to know the cause of the event */ + esp_websocket_client_handle_t client; /*!< esp_websocket_client_handle_t context */ + void *user_context;/*!< user_data context, from esp_websocket_client_config_t user_data */ + char *data; /*!< data of the event */ + int data_len; /*!< length of data */ +} esp_websocket_event_t; + +typedef esp_websocket_event_t* esp_websocket_event_handle_t; +typedef esp_err_t (* websocket_event_callback_t)(esp_websocket_event_handle_t event); + +/** + * @brief Websocket client setup configuration + */ +typedef struct { + const char *uri; /*!< Websocket URI, the information on the URI can be overrides the other fields below, if any */ + const char *host; /*!< Domain or IP as string */ + int port; /*!< Port to connect, default depend on esp_websocket_transport_t (80 or 443) */ + const char *username; /*!< Using for Http authentication - Not supported for now */ + const char *password; /*!< Using for Http authentication - Not supported for now */ + const char *path; /*!< HTTP Path, if not set, default is `/` */ + bool disable_auto_reconnect; /*!< Disable the automatic reconnect function when disconnected */ + void *user_context; /*!< HTTP user data context */ + int task_prio; /*!< Websocket task priority */ + int task_stack; /*!< Websocket task stack */ + int buffer_size; /*!< Websocket buffer size */ + const char *cert_pem; /*!< SSL Certification, PEM format as string, if the client requires to verify server */ + esp_websocket_transport_t transport; /*!< Websocket transport type, see `esp_websocket_transport_t */ +} esp_websocket_client_config_t; + +/** + * @brief Start a Websocket session + * This function must be the first function to call, + * and it returns a esp_websocket_client_handle_t that you must use as input to other functions in the interface. + * This call MUST have a corresponding call to esp_websocket_client_destroy when the operation is complete. + * + * @param[in] config The configuration + * + * @return + * - `esp_websocket_client_handle_t` + * - NULL if any errors + */ +esp_websocket_client_handle_t esp_websocket_client_init(const esp_websocket_client_config_t *config); + +/** + * @brief Set URL for client, when performing this behavior, the options in the URL will replace the old ones + * Must stop the WebSocket client before set URI if the client has been connected + * + * @param[in] client The client + * @param[in] uri The uri + * + * @return esp_err_t + */ +esp_err_t esp_websocket_client_set_uri(esp_websocket_client_handle_t client, const char *uri); + +/** + * @brief Open the WebSocket connection + * + * @param[in] client The client + * + * @return esp_err_t + */ +esp_err_t esp_websocket_client_start(esp_websocket_client_handle_t client); + +/** + * @brief Close the WebSocket connection + * + * @param[in] client The client + * + * @return esp_err_t + */ +esp_err_t esp_websocket_client_stop(esp_websocket_client_handle_t client); + +/** + * @brief Destroy the WebSocket connection and free all resources. + * This function must be the last function to call for an session. + * It is the opposite of the esp_websocket_client_init function and must be called with the same handle as input that a esp_websocket_client_init call returned. + * This might close all connections this handle has used. + * + * @param[in] client The client + * + * @return esp_err_t + */ +esp_err_t esp_websocket_client_destroy(esp_websocket_client_handle_t client); + +/** + * @brief Write data to the WebSocket connection + * + * @param[in] client The client + * @param[in] data The data + * @param[in] len The length + * @param[in] timeout Write data timeout + * + * @return + * - Number of data was sent + * - (-1) if any errors + */ +int esp_websocket_client_send(esp_websocket_client_handle_t client, const char *data, int len, TickType_t timeout); + +/** + * @brief Check the WebSocket connection status + * + * @param[in] client The client handle + * + * @return + * - true + * - false + */ +bool esp_websocket_client_is_connected(esp_websocket_client_handle_t client); + +/** + * @brief Register the Websocket Events + * + * @param client The client handle + * @param event The event id + * @param event_handler The callback function + * @param event_handler_arg User context + * @return esp_err_t + */ +esp_err_t esp_websocket_register_events(esp_websocket_client_handle_t client, + esp_websocket_event_id_t event, + esp_event_handler_t event_handler, + void* event_handler_arg); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/mqtt/esp-mqtt b/components/mqtt/esp-mqtt index e205913b2c..11f884623b 160000 --- a/components/mqtt/esp-mqtt +++ b/components/mqtt/esp-mqtt @@ -1 +1 @@ -Subproject commit e205913b2cf3eff20b1f8ced7c76cf03533f130b +Subproject commit 11f884623bd32cb4269f24f47847f5d046da93f5 diff --git a/components/tcp_transport/include/esp_transport_ws.h b/components/tcp_transport/include/esp_transport_ws.h index 582c5c7da2..f47fd049cf 100644 --- a/components/tcp_transport/include/esp_transport_ws.h +++ b/components/tcp_transport/include/esp_transport_ws.h @@ -23,8 +23,25 @@ extern "C" { */ esp_transport_handle_t esp_transport_ws_init(esp_transport_handle_t parent_handle); +/** + * @brief Set HTTP path to update protocol to websocket + * + * @param t websocket transport handle + * @param path The HTTP Path + */ void esp_transport_ws_set_path(esp_transport_handle_t t, const char *path); +/** + * @brief Set websocket sub protocol header + * + * @param t websocket transport handle + * @param sub_protocol Sub protocol string + * + * @return + * - ESP_OK on success + * - One of the error codes + */ +esp_err_t esp_transport_ws_set_subprotocol(esp_transport_handle_t t, const char *sub_protocol); #ifdef __cplusplus diff --git a/components/tcp_transport/transport_ssl.c b/components/tcp_transport/transport_ssl.c index 1ea4049878..257a58cba6 100644 --- a/components/tcp_transport/transport_ssl.c +++ b/components/tcp_transport/transport_ssl.c @@ -112,6 +112,7 @@ static int ssl_write(esp_transport_handle_t t, const char *buffer, int len, int ret = esp_tls_conn_write(ssl->tls, (const unsigned char *) buffer, len); if (ret < 0) { ESP_LOGE(TAG, "esp_tls_conn_write error, errno=%s", strerror(errno)); + return -1; } return ret; } @@ -129,6 +130,7 @@ static int ssl_read(esp_transport_handle_t t, char *buffer, int len, int timeout ret = esp_tls_conn_read(ssl->tls, (unsigned char *)buffer, len); if (ret < 0) { ESP_LOGE(TAG, "esp_tls_conn_read error, errno=%s", strerror(errno)); + return -1; } if (ret == 0) { ret = -1; diff --git a/components/tcp_transport/transport_ws.c b/components/tcp_transport/transport_ws.c index 43f0bdd360..599457b895 100644 --- a/components/tcp_transport/transport_ws.c +++ b/components/tcp_transport/transport_ws.c @@ -25,12 +25,13 @@ static const char *TAG = "TRANSPORT_WS"; #define WS_MASK 0x80 #define WS_SIZE16 126 #define WS_SIZE64 127 -#define MAX_WEBSOCKET_HEADER_SIZE 10 +#define MAX_WEBSOCKET_HEADER_SIZE 16 #define WS_RESPONSE_OK 101 typedef struct { char *path; char *buffer; + char *sub_protocol; esp_transport_handle_t parent; } transport_ws_t; @@ -80,7 +81,7 @@ static int ws_connect(esp_transport_handle_t t, const char *host, int port, int { transport_ws_t *ws = esp_transport_get_context_data(t); if (esp_transport_connect(ws->parent, host, port, timeout_ms) < 0) { - ESP_LOGE(TAG, "Error connect to the server"); + ESP_LOGE(TAG, "Error connecting to host %s:%d", host, port); return -1; } @@ -98,12 +99,15 @@ static int ws_connect(esp_transport_handle_t t, const char *host, int port, int "Host: %s:%d\r\n" "Upgrade: websocket\r\n" "Sec-WebSocket-Version: 13\r\n" - "Sec-WebSocket-Protocol: mqtt\r\n" "Sec-WebSocket-Key: %s\r\n" - "User-Agent: ESP32 Websocket Client\r\n\r\n", + "User-Agent: ESP32 Websocket Client\r\n", ws->path, host, port, client_key); + if (ws->sub_protocol) { + len += snprintf(ws->buffer + len, DEFAULT_WS_BUFFER - len, "Sec-WebSocket-Protocol: %s\r\n", ws->sub_protocol); + } + len += snprintf(ws->buffer + len, DEFAULT_WS_BUFFER - len, "\r\n"); if (len <= 0 || len >= DEFAULT_WS_BUFFER) { ESP_LOGE(TAG, "Error in request generation, %d", len); return -1; @@ -152,42 +156,70 @@ static int ws_connect(esp_transport_handle_t t, const char *host, int port, int return 0; } -static int ws_write(esp_transport_handle_t t, const char *buff, int len, int timeout_ms) +static int _ws_write(esp_transport_handle_t t, int opcode, int mask_flag, const char *b, int len, int timeout_ms) { transport_ws_t *ws = esp_transport_get_context_data(t); + char *buffer = (char *)b; char ws_header[MAX_WEBSOCKET_HEADER_SIZE]; char *mask; int header_len = 0, i; - char *buffer = (char *)buff; + int poll_write; if ((poll_write = esp_transport_poll_write(ws->parent, timeout_ms)) <= 0) { + ESP_LOGE(TAG, "Error transport_poll_write"); return poll_write; } + ws_header[header_len++] = opcode; - ws_header[header_len++] = WS_OPCODE_BINARY | WS_FIN; - - // NOTE: no support for > 16-bit sized messages - if (len > 125) { - ws_header[header_len++] = WS_SIZE16 | WS_MASK; + if (len <= 125) { + ws_header[header_len++] = (uint8_t)(len | mask_flag); + } else if (len < 65536) { + ws_header[header_len++] = WS_SIZE16 | mask_flag; ws_header[header_len++] = (uint8_t)(len >> 8); ws_header[header_len++] = (uint8_t)(len & 0xFF); } else { - ws_header[header_len++] = (uint8_t)(len | WS_MASK); + ws_header[header_len++] = WS_SIZE64 | mask_flag; + /* Support maximum 4 bytes length */ + ws_header[header_len++] = 0; //(uint8_t)((len >> 56) & 0xFF); + ws_header[header_len++] = 0; //(uint8_t)((len >> 48) & 0xFF); + ws_header[header_len++] = 0; //(uint8_t)((len >> 40) & 0xFF); + ws_header[header_len++] = 0; //(uint8_t)((len >> 32) & 0xFF); + ws_header[header_len++] = (uint8_t)((len >> 24) & 0xFF); + ws_header[header_len++] = (uint8_t)((len >> 16) & 0xFF); + ws_header[header_len++] = (uint8_t)((len >> 8) & 0xFF); + ws_header[header_len++] = (uint8_t)((len >> 0) & 0xFF); } - mask = &ws_header[header_len]; - getrandom(ws_header + header_len, 4, 0); - header_len += 4; + if (len) { + if (mask_flag) { + mask = &ws_header[header_len]; + getrandom(ws_header + header_len, 4, 0); + header_len += 4; + + for (i = 0; i < len; ++i) { + buffer[i] = (buffer[i] ^ mask[i % 4]); + } + } - for (i = 0; i < len; ++i) { - buffer[i] = (buffer[i] ^ mask[i % 4]); } if (esp_transport_write(ws->parent, ws_header, header_len, timeout_ms) != header_len) { ESP_LOGE(TAG, "Error write header"); return -1; } + if (len == 0) { + return 0; + } return esp_transport_write(ws->parent, buffer, len, timeout_ms); } +static int ws_write(esp_transport_handle_t t, const char *b, int len, int timeout_ms) +{ + if (len == 0) { + ESP_LOGD(TAG, "Write PING message"); + return _ws_write(t, WS_OPCODE_PING | WS_FIN, 0, NULL, 0, timeout_ms); + } + return _ws_write(t, WS_OPCODE_BINARY | WS_FIN, WS_MASK, b, len, timeout_ms); +} + static int ws_read(esp_transport_handle_t t, char *buffer, int len, int timeout_ms) { transport_ws_t *ws = esp_transport_get_context_data(t); @@ -279,6 +311,7 @@ static esp_err_t ws_destroy(esp_transport_handle_t t) transport_ws_t *ws = esp_transport_get_context_data(t); free(ws->buffer); free(ws->path); + free(ws->sub_protocol); free(ws); return 0; } @@ -288,6 +321,7 @@ void esp_transport_ws_set_path(esp_transport_handle_t t, const char *path) ws->path = realloc(ws->path, strlen(path) + 1); strcpy(ws->path, path); } + esp_transport_handle_t esp_transport_ws_init(esp_transport_handle_t parent_handle) { esp_transport_handle_t t = esp_transport_init(); @@ -315,3 +349,22 @@ esp_transport_handle_t esp_transport_ws_init(esp_transport_handle_t parent_handl return t; } +esp_err_t esp_transport_ws_set_subprotocol(esp_transport_handle_t t, const char *sub_protocol) +{ + if (t == NULL) { + return ESP_ERR_INVALID_ARG; + } + transport_ws_t *ws = esp_transport_get_context_data(t); + if (ws->sub_protocol) { + free(ws->sub_protocol); + } + if (sub_protocol == NULL) { + ws->sub_protocol = NULL; + return ESP_OK; + } + ws->sub_protocol = strdup(sub_protocol); + if (ws->sub_protocol == NULL) { + return ESP_ERR_NO_MEM; + } + return ESP_OK; +} diff --git a/docs/Doxyfile b/docs/Doxyfile index 858a6a39e0..c678d93319 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -104,6 +104,7 @@ INPUT = \ ## mDNS ../../components/mdns/include/mdns.h \ ../../components/esp_http_client/include/esp_http_client.h \ + ../../components/esp_websocket_client/include/esp_websocket_client.h \ ../../components/esp_http_server/include/esp_http_server.h \ ../../components/esp_https_server/include/esp_https_server.h \ ## diff --git a/docs/en/api-reference/protocols/esp_websocket_client.rst b/docs/en/api-reference/protocols/esp_websocket_client.rst new file mode 100644 index 0000000000..cd4db2413b --- /dev/null +++ b/docs/en/api-reference/protocols/esp_websocket_client.rst @@ -0,0 +1,70 @@ +ESP WebSocket Client +==================== + +Overview +-------- +The ESP WebSocket client is an implementation of `WebSocket protocol client `_ for ESP32 + +Features +-------- + * supports WebSocket over TCP, SSL with mbedtls + * Easy to setup with URI + * Multiple instances (Multiple clients in one application) + +Configuration +------------- +URI +^^^ + +- Supports ``ws``, ``wss`` schemes +- WebSocket samples: + + - ``ws://websocket.org``: WebSocket over TCP, default port 80 + - ``wss://websocket.org``: WebSocket over SSL, default port 443 + +- Minimal configurations: + +.. code:: c + + const esp_websocket_client_config_t ws_cfg = { + .uri = "ws://websocket.org", + }; + +- If there are any options related to the URI in + ``esp_websocket_client_config_t``, the option defined by the URI will be + overridden. Sample: + +.. code:: c + + const esp_websocket_client_config_t ws_cfg = { + .uri = "ws://websocket.org:123", + .port = 4567, + }; + //WebSocket client will connect to websocket.org using port 4567 + +SSL +^^^ + +- Get certificate from server, example: ``websocket.org`` + ``openssl s_client -showcerts -connect websocket.org:443 /dev/null|openssl x509 -outform PEM >websocket_org.pem`` +- Configuration: + +.. code:: cpp + + const esp_websocket_client_config_t ws_cfg = { + .uri = "wss://websocket.org", + .cert_pem = (const char *)websocket_org_pem_start, + }; + +For more options on ``esp_websocket_client_config_t``, please refer to API reference below + +Application Example +------------------- +Simple WebSocket example that uses esp_websocket_client to establish a websocket connection and send/receive data with the `websocket.org `_ Server: :example:`protocols/websocket`. + + +API Reference +------------- + +.. include:: /_build/inc/esp_websocket_client.inc + diff --git a/docs/en/api-reference/protocols/index.rst b/docs/en/api-reference/protocols/index.rst index f8edf9e811..97386abe4c 100644 --- a/docs/en/api-reference/protocols/index.rst +++ b/docs/en/api-reference/protocols/index.rst @@ -8,6 +8,7 @@ Application Protocols mDNS ESP-TLS HTTP Client + Websocket Client HTTP Server HTTPS Server ASIO diff --git a/docs/zh_CN/api-reference/protocols/esp_websocket_client.rst b/docs/zh_CN/api-reference/protocols/esp_websocket_client.rst new file mode 100644 index 0000000000..c318562404 --- /dev/null +++ b/docs/zh_CN/api-reference/protocols/esp_websocket_client.rst @@ -0,0 +1 @@ +.. include:: ../../../en/api-reference/protocols/esp_websocket_client.rst diff --git a/docs/zh_CN/api-reference/protocols/index.rst b/docs/zh_CN/api-reference/protocols/index.rst index 63513cbee0..9d893c29b6 100644 --- a/docs/zh_CN/api-reference/protocols/index.rst +++ b/docs/zh_CN/api-reference/protocols/index.rst @@ -8,6 +8,7 @@ mDNS ESP-TLS HTTP Client + Websocket Client HTTP 服务器 HTTPS Server ASIO diff --git a/examples/protocols/websocket/CMakeLists.txt b/examples/protocols/websocket/CMakeLists.txt new file mode 100644 index 0000000000..f77ad23e52 --- /dev/null +++ b/examples/protocols/websocket/CMakeLists.txt @@ -0,0 +1,10 @@ +# The following four lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +# (Not part of the boilerplate) +# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection. +set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(websocket-example) diff --git a/examples/protocols/websocket/Makefile b/examples/protocols/websocket/Makefile new file mode 100644 index 0000000000..3b37d32062 --- /dev/null +++ b/examples/protocols/websocket/Makefile @@ -0,0 +1,10 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# +PROJECT_NAME := websocket-example + +EXTRA_COMPONENT_DIRS = $(IDF_PATH)/examples/common_components/protocol_examples_common + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/protocols/websocket/README.md b/examples/protocols/websocket/README.md new file mode 100644 index 0000000000..2969444fdc --- /dev/null +++ b/examples/protocols/websocket/README.md @@ -0,0 +1 @@ +# Websocket Sample application diff --git a/examples/protocols/websocket/example_test.py b/examples/protocols/websocket/example_test.py new file mode 100644 index 0000000000..ef0c3b2f2b --- /dev/null +++ b/examples/protocols/websocket/example_test.py @@ -0,0 +1,41 @@ +import re +import os +import sys +import IDF + +# this is a test case write with tiny-test-fw. +# to run test cases outside tiny-test-fw, +# we need to set environment variable `TEST_FW_PATH`, +# then get and insert `TEST_FW_PATH` to sys path before import FW module +test_fw_path = os.getenv("TEST_FW_PATH") +if test_fw_path and test_fw_path not in sys.path: + sys.path.insert(0, test_fw_path) + + +@IDF.idf_example_test(env_tag="Example_WIFI", ignore=True) +def test_examples_protocol_websocket(env, extra_data): + """ + steps: | + 1. join AP + 2. connect to ws://echo.websocket.org + 3. send and receive data + """ + dut1 = env.get_dut("websocket", "examples/protocols/websocket") + # check and log bin size + binary_file = os.path.join(dut1.app.binary_path, "websocket-example.bin") + bin_size = os.path.getsize(binary_file) + IDF.log_performance("websocket_bin_size", "{}KB".format(bin_size // 1024)) + IDF.check_performance("websocket_bin_size", bin_size // 1024) + # start test + dut1.start_app() + dut1.expect("Waiting for wifi ...") + dut1.expect("Connection established...", timeout=30) + dut1.expect("WEBSOCKET_EVENT_CONNECTED") + for i in range(0, 10): + dut1.expect(re.compile(r"Sending hello (\d)")) + dut1.expect(re.compile(r"Received=hello (\d)")) + dut1.expect("Websocket Stopped") + + +if __name__ == '__main__': + test_examples_protocol_websocket() diff --git a/examples/protocols/websocket/main/CMakeLists.txt b/examples/protocols/websocket/main/CMakeLists.txt new file mode 100644 index 0000000000..caf642155c --- /dev/null +++ b/examples/protocols/websocket/main/CMakeLists.txt @@ -0,0 +1,4 @@ +set(COMPONENT_SRCS "websocket_example.c") +set(COMPONENT_ADD_INCLUDEDIRS ".") + +register_component() diff --git a/examples/protocols/websocket/main/Kconfig.projbuild b/examples/protocols/websocket/main/Kconfig.projbuild new file mode 100644 index 0000000000..6af61c8f94 --- /dev/null +++ b/examples/protocols/websocket/main/Kconfig.projbuild @@ -0,0 +1,9 @@ +menu "Example Configuration" + + config WEBSOCKET_URI + string "Websocket endpoint URI" + default "ws://echo.websocket.org"; + help + URL of websocket endpoint this example connects to and sends echo + +endmenu diff --git a/examples/protocols/websocket/main/component.mk b/examples/protocols/websocket/main/component.mk new file mode 100644 index 0000000000..e69de29bb2 diff --git a/examples/protocols/websocket/main/websocket_example.c b/examples/protocols/websocket/main/websocket_example.c new file mode 100644 index 0000000000..120a5b4fdf --- /dev/null +++ b/examples/protocols/websocket/main/websocket_example.c @@ -0,0 +1,103 @@ +/* ESP HTTP Client Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + + +#include +#include "esp_wifi.h" +#include "esp_system.h" +#include "nvs_flash.h" +#include "esp_event_loop.h" +#include "protocol_examples_common.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" + + +#include "esp_log.h" +#include "esp_websocket_client.h" +#include "esp_event.h" +#include "esp_event_loop.h" + +static const char *TAG = "WEBSOCKET"; +static const char *WEBSOCKET_ECHO_ENDPOINT = CONFIG_WEBSOCKET_URI; + + +static void websocket_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) +{ + // esp_websocket_client_handle_t client = (esp_websocket_client_handle_t)handler_args; + esp_websocket_event_data_t *data = (esp_websocket_event_data_t *)event_data; + switch (event_id) { + case WEBSOCKET_EVENT_CONNECTED: + ESP_LOGI(TAG, "WEBSOCKET_EVENT_CONNECTED"); + + + break; + case WEBSOCKET_EVENT_DISCONNECTED: + ESP_LOGI(TAG, "WEBSOCKET_EVENT_DISCONNECTED"); + break; + + case WEBSOCKET_EVENT_DATA: + ESP_LOGI(TAG, "WEBSOCKET_EVENT_DATA"); + ESP_LOGW(TAG, "Received=%.*s\r\n", data->data_len, (char*)data->data_ptr); + break; + case WEBSOCKET_EVENT_ERROR: + ESP_LOGI(TAG, "WEBSOCKET_EVENT_ERROR"); + break; + } +} + +static void websocket_app_start(void) +{ + ESP_LOGI(TAG, "Connectiong to %s...", WEBSOCKET_ECHO_ENDPOINT); + + const esp_websocket_client_config_t websocket_cfg = { + .uri = WEBSOCKET_ECHO_ENDPOINT, // or wss://echo.websocket.org for websocket secure + }; + + esp_websocket_client_handle_t client = esp_websocket_client_init(&websocket_cfg); + esp_websocket_register_events(client, WEBSOCKET_EVENT_ANY, websocket_event_handler, (void *)client); + + esp_websocket_client_start(client); + char data[32]; + int i = 0; + while (i < 10) { + if (esp_websocket_client_is_connected(client)) { + int len = sprintf(data, "hello %04d", i++); + ESP_LOGI(TAG, "Sending %s", data); + esp_websocket_client_send(client, data, len, portMAX_DELAY); + } + vTaskDelay(1000 / portTICK_RATE_MS); + } + esp_websocket_client_stop(client); + ESP_LOGI(TAG, "Websocket Stopped"); + esp_websocket_client_destroy(client); +} + +void app_main() +{ + ESP_LOGI(TAG, "[APP] Startup.."); + ESP_LOGI(TAG, "[APP] Free memory: %d bytes", esp_get_free_heap_size()); + ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version()); + esp_log_level_set("*", ESP_LOG_INFO); + esp_log_level_set("WEBSOCKET_CLIENT", ESP_LOG_DEBUG); + esp_log_level_set("TRANS_TCP", ESP_LOG_DEBUG); + + ESP_ERROR_CHECK(nvs_flash_init()); + tcpip_adapter_init(); + ESP_ERROR_CHECK(esp_event_loop_create_default()); + + /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig. + * Read "Establishing Wi-Fi or Ethernet Connection" section in + * examples/protocols/README.md for more information about this function. + */ + ESP_ERROR_CHECK(example_connect()); + + websocket_app_start(); +} From 0150982ae392e7d12a2a066157602ada77f2f13a Mon Sep 17 00:00:00 2001 From: Roland Dobai Date: Tue, 18 Jun 2019 17:37:07 +0200 Subject: [PATCH 086/486] tools: Fix Kconfig checker for comments and source after help --- tools/check_kconfigs.py | 25 ++++++++++++++++++++++--- tools/test_check_kconfigs.py | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/tools/check_kconfigs.py b/tools/check_kconfigs.py index 23a46e149c..bdc3953e46 100755 --- a/tools/check_kconfigs.py +++ b/tools/check_kconfigs.py @@ -84,6 +84,23 @@ class BaseChecker(object): pass +class SourceChecker(BaseChecker): + # allow to source only files which will be also checked by the script + # Note: The rules are complex and the LineRuleChecker cannot be used + def process_line(self, line, line_number): + m = re.search(r'^\s*source(\s*)"([^"]+)"', line) + if m: + if len(m.group(1)) == 0: + raise InputError(self.path_in_idf, line_number, '"source" has to been followed by space', + line.replace('source', 'source ')) + path = m.group(2) + if path in ['$COMPONENT_KCONFIGS_PROJBUILD', '$COMPONENT_KCONFIGS']: + pass + elif not path.endswith('/Kconfig.in') and path != 'Kconfig.in': + raise InputError(self.path_in_idf, line_number, "only Kconfig.in can be sourced", + line.replace(path, os.path.join(os.path.dirname(path), 'Kconfig.in'))) + + class LineRuleChecker(BaseChecker): """ checks LINE_ERROR_RULES for each line @@ -134,6 +151,7 @@ class IndentAndNameChecker(BaseChecker): |(menuconfig) |(help) |(if) + |(source) ) ''', re.X) @@ -193,7 +211,7 @@ class IndentAndNameChecker(BaseChecker): print('level+', new_item, ': ', self.level_stack, end=' -> ') # "config" and "menuconfig" don't have a closing pair. So if new_item is an item which need to be indented # outside the last "config" or "menuconfig" then we need to find to a parent where it belongs - if new_item in ['config', 'menuconfig', 'menu', 'choice', 'if']: + if new_item in ['config', 'menuconfig', 'menu', 'choice', 'if', 'source']: # item is not belonging to a previous "config" or "menuconfig" so need to indent to parent for i, item in enumerate(reversed(self.level_stack)): if item in ['menu', 'mainmenu', 'choice', 'if']: @@ -328,7 +346,7 @@ class IndentAndNameChecker(BaseChecker): new_item = m.group(1) current_level = self.update_level_for_dec_pattern(new_item) if new_item not in ['endif']: - # endif doesn't require to check the prefix because the items in inside if/endif belong to the + # endif doesn't require to check the prefix because the items inside if/endif belong to the # same prefix level self.check_common_prefix(line, line_number) @@ -384,11 +402,12 @@ def main(): with open(full_path, 'r', encoding='utf-8') as f, \ open(suggestions_full_path, 'w', encoding='utf-8', newline='\n') as f_o, \ LineRuleChecker(path_in_idf) as line_checker, \ + SourceChecker(path_in_idf) as source_checker, \ IndentAndNameChecker(path_in_idf, debug=args.verbose) as indent_and_name_checker: try: for line_number, line in enumerate(f, start=1): try: - for checker in [line_checker, indent_and_name_checker]: + for checker in [line_checker, indent_and_name_checker, source_checker]: checker.process_line(line, line_number) # The line is correct therefore we echo it to the output file f_o.write(line) diff --git a/tools/test_check_kconfigs.py b/tools/test_check_kconfigs.py index 737ae46570..a38f16ab91 100755 --- a/tools/test_check_kconfigs.py +++ b/tools/test_check_kconfigs.py @@ -16,6 +16,7 @@ import unittest from check_kconfigs import LineRuleChecker +from check_kconfigs import SourceChecker from check_kconfigs import InputError from check_kconfigs import IndentAndNameChecker from check_kconfigs import CONFIG_NAME_MAX_LENGTH @@ -71,6 +72,23 @@ class TestLineRuleChecker(unittest.TestCase, ApplyLine): self.expect_error('test \\', expect=None) +class TestSourceChecker(unittest.TestCase, ApplyLine): + def setUp(self): + self.checker = SourceChecker('Kconfig') + + def tearDown(self): + pass + + def test_source_file_name(self): + self.expect_error('source "Kconfig.test"', expect='source "Kconfig.in"') + self.expect_error('source "/tmp/Kconfig.test"', expect='source "/tmp/Kconfig.in"') + self.expect_error('source "Kconfig"', expect='source "Kconfig.in"') + self.expt_success('source "Kconfig.in"') + self.expt_success('source "/tmp/Kconfig.in"') + self.expect_error('source"Kconfig.in"', expect='source "Kconfig.in"') + self.expt_success('source "/tmp/Kconfig.in" # comment') + + class TestIndentAndNameChecker(unittest.TestCase, ApplyLine): def setUp(self): self.checker = IndentAndNameChecker('Kconfig') @@ -156,6 +174,21 @@ class TestIndent(TestIndentAndNameChecker): self.expt_success('config') self.expt_success(' help') + def test_source_after_config(self): + self.expt_success('menuconfig') + self.expt_success(' help') + self.expt_success(' text') + self.expect_error(' source', expect='source') + self.expt_success('source "Kconfig.in"') + + def test_comment_after_config(self): + self.expt_success('menuconfig') + self.expt_success(' # comment') + self.expt_success(' help') + self.expt_success(' text') + self.expect_error('# comment', expect=' # comment') + self.expt_success(' # second not realcomment"') + class TestName(TestIndentAndNameChecker): def setUp(self): From bd9fed44008bcc3e5a40c0e72a047917eebee9ad Mon Sep 17 00:00:00 2001 From: baohongde Date: Tue, 6 Nov 2018 15:34:38 +0800 Subject: [PATCH 087/486] component/bt: reduce the size of DRAM DRAM.data: 506B --> 196B DRAM.bss : 26857B --> 2170B --- components/bt/bluedroid/api/esp_bt_main.c | 4 +- .../bt/bluedroid/api/esp_hf_client_api.c | 5 + components/bt/bluedroid/api/esp_spp_api.c | 6 +- .../api/include/api/esp_hf_client_api.h | 5 + components/bt/bluedroid/bta/av/bta_av_sbc.c | 23 +- .../bt/bluedroid/bta/av/include/bta_av_int.h | 22 + components/bt/bluedroid/bta/dm/bta_dm_act.c | 10 +- components/bt/bluedroid/bta/dm/bta_dm_cfg.c | 14 +- components/bt/bluedroid/bta/dm/bta_dm_pm.c | 6 +- components/bt/bluedroid/bta/dm/bta_dm_sco.c | 35 +- .../bt/bluedroid/bta/dm/include/bta_dm_int.h | 26 +- .../bt/bluedroid/bta/gatt/bta_gattc_act.c | 4 +- .../bt/bluedroid/bta/gatt/bta_gattc_cache.c | 3 + .../bt/bluedroid/bta/gatt/bta_gattc_co.c | 188 +++++---- .../bt/bluedroid/bta/gatt/bta_gatts_act.c | 4 +- .../bt/bluedroid/bta/gatt/bta_gatts_api.c | 10 +- .../bt/bluedroid/bta/include/bta/bta_api.h | 11 + .../bluedroid/bta/include/bta/bta_gatt_api.h | 13 +- .../bluedroid/bta/include/bta/bta_sdp_api.h | 13 + components/bt/bluedroid/bta/jv/bta_jv_api.c | 19 +- components/bt/bluedroid/bta/jv/bta_jv_cfg.c | 9 +- components/bt/bluedroid/bta/jv/bta_jv_main.c | 2 + components/bt/bluedroid/bta/sdp/bta_sdp_api.c | 32 ++ components/bt/bluedroid/bta/sdp/bta_sdp_cfg.c | 8 +- .../bt/bluedroid/btc/core/btc_ble_storage.c | 6 +- components/bt/bluedroid/btc/core/btc_dm.c | 141 ++++--- components/bt/bluedroid/btc/core/btc_manage.c | 6 +- components/bt/bluedroid/btc/core/btc_task.c | 104 ++++- .../bt/bluedroid/btc/include/btc/btc_dm.h | 14 + .../bt/bluedroid/btc/include/btc/btc_manage.h | 5 + .../btc/profile/esp/blufi/blufi_prf.c | 22 +- .../btc/profile/esp/blufi/blufi_protocol.c | 2 +- .../btc/profile/esp/blufi/include/blufi_int.h | 37 +- .../btc/profile/std/a2dp/bta_av_co.c | 58 +-- .../btc/profile/std/a2dp/btc_a2dp_sink.c | 171 ++++---- .../btc/profile/std/a2dp/btc_a2dp_source.c | 387 +++++++++--------- .../bluedroid/btc/profile/std/a2dp/btc_av.c | 50 ++- .../btc/profile/std/a2dp/include/btc_av_co.h | 56 +++ .../bluedroid/btc/profile/std/avrc/btc_avrc.c | 44 +- .../btc/profile/std/gap/btc_gap_ble.c | 6 + .../btc/profile/std/gatt/btc_gatt_util.c | 2 +- .../btc/profile/std/gatt/btc_gatts.c | 56 ++- .../btc/profile/std/hf_client/btc_hf_client.c | 210 +++++----- .../btc/profile/std/include/btc_avrc.h | 44 ++ .../btc/profile/std/include/btc_gap_ble.h | 8 + .../btc/profile/std/include/btc_gatts.h | 16 + .../btc/profile/std/include/btc_hf_client.h | 28 ++ .../bluedroid/btc/profile/std/spp/btc_spp.c | 25 +- .../common/include/common/bt_target.h | 6 - components/bt/bluedroid/device/controller.c | 377 ++++++++--------- .../external/sbc/encoder/srce/sbc_analysis.c | 21 + components/bt/bluedroid/main/bte_init.c | 32 +- components/bt/bluedroid/osi/alarm.c | 19 +- .../bluedroid/stack/btm/btm_ble_adv_filter.c | 51 ++- .../bluedroid/stack/btm/btm_ble_batchscan.c | 27 +- .../bt/bluedroid/stack/btm/btm_ble_gap.c | 26 +- .../bluedroid/stack/btm/btm_ble_multi_adv.c | 26 +- .../bt/bluedroid/stack/btm/btm_ble_privacy.c | 2 + components/bt/bluedroid/stack/gap/gap_api.c | 26 ++ components/bt/bluedroid/stack/gap/gap_ble.c | 2 +- .../bt/bluedroid/stack/gap/include/gap_int.h | 7 +- components/bt/bluedroid/stack/gatt/gatt_api.c | 4 +- .../bt/bluedroid/stack/gatt/gatt_attr.c | 6 +- components/bt/bluedroid/stack/gatt/gatt_cl.c | 4 +- .../bluedroid/stack/include/stack/dyn_mem.h | 40 +- .../bluedroid/stack/include/stack/gap_api.h | 12 + .../bluedroid/stack/include/stack/gatt_api.h | 4 +- .../bluedroid/stack/include/stack/port_api.h | 11 + .../bt/bluedroid/stack/l2cap/l2c_main.c | 3 +- .../bt/bluedroid/stack/rfcomm/port_api.c | 25 +- .../stack/smp/include/p_256_ecc_pp.h | 9 + .../bt/bluedroid/stack/smp/include/smp_int.h | 2 +- .../bt/bluedroid/stack/smp/p_256_ecc_pp.c | 10 +- components/bt/bluedroid/stack/smp/smp_act.c | 4 + components/bt/bluedroid/stack/smp/smp_api.c | 6 + components/bt/bluedroid/stack/smp/smp_cmac.c | 2 +- components/bt/bluedroid/stack/smp/smp_keys.c | 2 + components/bt/bluedroid/stack/smp/smp_utils.c | 5 +- 78 files changed, 1705 insertions(+), 1036 deletions(-) diff --git a/components/bt/bluedroid/api/esp_bt_main.c b/components/bt/bluedroid/api/esp_bt_main.c index 0b1bcc6ab0..8097956d10 100644 --- a/components/bt/bluedroid/api/esp_bt_main.c +++ b/components/bt/bluedroid/api/esp_bt_main.c @@ -132,6 +132,8 @@ esp_err_t esp_bluedroid_init(void) osi_mem_dbg_init(); #endif + btc_init(); + future_p = btc_main_get_future_p(BTC_MAIN_INIT_FUTURE); *future_p = future_new(); if (*future_p == NULL) { @@ -139,8 +141,6 @@ esp_err_t esp_bluedroid_init(void) return ESP_ERR_NO_MEM; } - btc_init(); - msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_MAIN_INIT; msg.act = BTC_MAIN_ACT_INIT; diff --git a/components/bt/bluedroid/api/esp_hf_client_api.c b/components/bt/bluedroid/api/esp_hf_client_api.c index fbc5d475cc..7db990206a 100644 --- a/components/bt/bluedroid/api/esp_hf_client_api.c +++ b/components/bt/bluedroid/api/esp_hf_client_api.c @@ -466,6 +466,11 @@ void esp_hf_client_pcm_resample_init(uint32_t src_sps, uint32_t bits, uint32_t c BTA_DmPcmInitSamples(src_sps, bits, channels); } +void esp_hf_client_pcm_resample_deinit(void) +{ + BTA_DmPcmDeinitSamples(); +} + int32_t esp_hf_client_pcm_resample(void *src, uint32_t in_bytes, void *dst) { return BTA_DmPcmResample(src, in_bytes, dst); diff --git a/components/bt/bluedroid/api/esp_spp_api.c b/components/bt/bluedroid/api/esp_spp_api.c index 46878d659e..57c2e317ae 100644 --- a/components/bt/bluedroid/api/esp_spp_api.c +++ b/components/bt/bluedroid/api/esp_spp_api.c @@ -23,9 +23,9 @@ #if (defined BTC_SPP_INCLUDED && BTC_SPP_INCLUDED == TRUE) -static const uint8_t UUID_SPP[16] = {0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00, - 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB - }; +static const uint8_t UUID_SPP[16] = {0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB + }; static tSDP_UUID sdp_uuid; esp_err_t esp_spp_register_callback(esp_spp_cb_t *callback) { diff --git a/components/bt/bluedroid/api/include/api/esp_hf_client_api.h b/components/bt/bluedroid/api/include/api/esp_hf_client_api.h index dfc06ed5d1..8e3dc956b7 100644 --- a/components/bt/bluedroid/api/include/api/esp_hf_client_api.h +++ b/components/bt/bluedroid/api/include/api/esp_hf_client_api.h @@ -615,6 +615,11 @@ void esp_hf_client_outgoing_data_ready(void); */ void esp_hf_client_pcm_resample_init(uint32_t src_sps, uint32_t bits, uint32_t channels); +/** + * @brief Deinitialize the down sampling converter. + */ +void esp_hf_client_pcm_resample_deinit(void); + /** * @brief Down sampling utility to convert high sampling rate into 8K/16bits 1-channel mode PCM * samples. This can only be used in the case that Voice Over HCI is enabled. diff --git a/components/bt/bluedroid/bta/av/bta_av_sbc.c b/components/bt/bluedroid/bta/av/bta_av_sbc.c index 30f178efb3..4e034c4246 100644 --- a/components/bt/bluedroid/bta/av/bta_av_sbc.c +++ b/components/bt/bluedroid/bta/av/bta_av_sbc.c @@ -31,24 +31,13 @@ #include "common/bt_defs.h" #if defined(BTA_AV_INCLUDED) && (BTA_AV_INCLUDED == TRUE) +#include "bta_av_int.h" -typedef int (tBTA_AV_SBC_ACT)(void *p_src, void *p_dst, - UINT32 src_samples, UINT32 dst_samples, - UINT32 *p_ret); - -typedef struct { - INT32 cur_pos; /* current position */ - UINT32 src_sps; /* samples per second (source audio data) */ - UINT32 dst_sps; /* samples per second (converted audio data) */ - tBTA_AV_SBC_ACT *p_act; /* the action function to do the conversion */ - UINT16 bits; /* number of bits per pcm sample */ - UINT16 n_channels; /* number of channels (i.e. mono(1), stereo(2)...) */ - INT16 worker1; - INT16 worker2; - UINT8 div; -} tBTA_AV_SBC_UPS_CB; - -tBTA_AV_SBC_UPS_CB bta_av_sbc_ups_cb; +#if BTA_DYNAMIC_MEMORY == FALSE +static tBTA_AV_SBC_UPS_CB bta_av_sbc_ups_cb; +#else +tBTA_AV_SBC_UPS_CB *bta_av_sbc_ups_cb_ptr; +#endif /******************************************************************************* ** diff --git a/components/bt/bluedroid/bta/av/include/bta_av_int.h b/components/bt/bluedroid/bta/av/include/bta_av_int.h index 16a70f3ef3..9fb6c06c52 100644 --- a/components/bt/bluedroid/bta/av/include/bta_av_int.h +++ b/components/bt/bluedroid/bta/av/include/bta_av_int.h @@ -531,11 +531,32 @@ typedef struct { UINT8 video_streams; /* handle mask of streaming video channels */ } tBTA_AV_CB; +/* type for dealing with SBC data frames and codec capabilities functions */ +typedef int (tBTA_AV_SBC_ACT)(void *p_src, void *p_dst, + UINT32 src_samples, UINT32 dst_samples, + UINT32 *p_ret); +/* type for AV up sample control block */ +typedef struct { + INT32 cur_pos; /* current position */ + UINT32 src_sps; /* samples per second (source audio data) */ + UINT32 dst_sps; /* samples per second (converted audio data) */ + tBTA_AV_SBC_ACT *p_act; /* the action function to do the conversion */ + UINT16 bits; /* number of bits per pcm sample */ + UINT16 n_channels; /* number of channels (i.e. mono(1), stereo(2)...) */ + INT16 worker1; + INT16 worker2; + UINT8 div; +} tBTA_AV_SBC_UPS_CB; /***************************************************************************** ** Global data *****************************************************************************/ +/* control block declaration up sample */ +#if BTA_DYNAMIC_MEMORY == TRUE +extern tBTA_AV_SBC_UPS_CB *bta_av_sbc_ups_cb_ptr; +#define bta_av_sbc_ups_cb (*bta_av_sbc_ups_cb_ptr) +#endif /* control block declaration */ #if BTA_DYNAMIC_MEMORY == FALSE @@ -670,3 +691,4 @@ extern void bta_av_reg_vdp (tAVDT_CS *p_cs, char *p_service_name, void *p_data); #endif ///BTA_AV_INCLUDED == TRUE #endif /* BTA_AV_INT_H */ + diff --git a/components/bt/bluedroid/bta/dm/bta_dm_act.c b/components/bt/bluedroid/bta/dm/bta_dm_act.c index 49cce8749a..efb685ecd6 100644 --- a/components/bt/bluedroid/bta/dm/bta_dm_act.c +++ b/components/bt/bluedroid/bta/dm/bta_dm_act.c @@ -233,10 +233,12 @@ const tBTM_APPL_INFO bta_security = { #endif ///SMP_INCLUDED == TRUE #if (SDP_INCLUDED == TRUE) -#define MAX_DISC_RAW_DATA_BUF (1024) +#if BTA_DYNAMIC_MEMORY == FALSE UINT8 g_disc_raw_data_buf[MAX_DISC_RAW_DATA_BUF]; +#else +UINT8 *g_disc_raw_data_buf; +#endif #endif ///SDP_INCLUDED == TRUE -extern DEV_CLASS local_device_default_class; /******************************************************************************* ** @@ -2303,7 +2305,7 @@ static void bta_dm_find_services ( BD_ADDR bd_addr) APPL_TRACE_DEBUG("%s search UUID = %04x", __func__, uuid.uu.uuid16); SDP_InitDiscoveryDb (bta_dm_search_cb.p_sdp_db, BTA_DM_SDP_DB_SIZE, 1, &uuid, 0, NULL); - memset(g_disc_raw_data_buf, 0, sizeof(g_disc_raw_data_buf)); + memset(g_disc_raw_data_buf, 0, MAX_DISC_RAW_DATA_BUF); bta_dm_search_cb.p_sdp_db->raw_data = g_disc_raw_data_buf; bta_dm_search_cb.p_sdp_db->raw_size = MAX_DISC_RAW_DATA_BUF; @@ -2496,7 +2498,7 @@ static void bta_dm_discover_device(BD_ADDR remote_bd_addr) if (transport == BT_TRANSPORT_LE) { if (bta_dm_search_cb.services_to_search & BTA_BLE_SERVICE_MASK) { //set the raw data buffer here - memset(g_disc_raw_data_buf, 0, sizeof(g_disc_raw_data_buf)); + memset(g_disc_raw_data_buf, 0, MAX_DISC_RAW_DATA_BUF); bta_dm_search_cb.p_ble_rawdata = g_disc_raw_data_buf; bta_dm_search_cb.ble_raw_size = MAX_DISC_RAW_DATA_BUF; diff --git a/components/bt/bluedroid/bta/dm/bta_dm_cfg.c b/components/bt/bluedroid/bta/dm/bta_dm_cfg.c index 14462e4409..9e018de98d 100644 --- a/components/bt/bluedroid/bta/dm/bta_dm_cfg.c +++ b/components/bt/bluedroid/bta/dm/bta_dm_cfg.c @@ -112,9 +112,9 @@ const tBTA_DM_RM bta_dm_rm_cfg[] = { }; -tBTA_DM_CFG *p_bta_dm_cfg = (tBTA_DM_CFG *) &bta_dm_cfg; +tBTA_DM_CFG *const p_bta_dm_cfg = (tBTA_DM_CFG *) &bta_dm_cfg; -tBTA_DM_RM *p_bta_dm_rm_cfg = (tBTA_DM_RM *) &bta_dm_rm_cfg; +tBTA_DM_RM *const p_bta_dm_rm_cfg = (tBTA_DM_RM *) &bta_dm_rm_cfg; #if BLE_INCLUDED == TRUE # define BTA_DM_NUM_PM_ENTRY 8 /* number of entries in bta_dm_pm_cfg except the first */ @@ -375,12 +375,12 @@ tBTA_DM_SSR_SPEC bta_dm_ssr_spec[] = { {360, 160, 2} /* BTA_DM_PM_SSR3 - HD */ }; -tBTA_DM_SSR_SPEC *p_bta_dm_ssr_spec = (tBTA_DM_SSR_SPEC *) &bta_dm_ssr_spec; +tBTA_DM_SSR_SPEC *const p_bta_dm_ssr_spec = (tBTA_DM_SSR_SPEC *) &bta_dm_ssr_spec; #endif -tBTA_DM_PM_CFG *p_bta_dm_pm_cfg = (tBTA_DM_PM_CFG *) &bta_dm_pm_cfg; -tBTA_DM_PM_SPEC *p_bta_dm_pm_spec = (tBTA_DM_PM_SPEC *) &bta_dm_pm_spec; -tBTM_PM_PWR_MD *p_bta_dm_pm_md = (tBTM_PM_PWR_MD *) &bta_dm_pm_md; +tBTA_DM_PM_CFG *const p_bta_dm_pm_cfg = (tBTA_DM_PM_CFG *) &bta_dm_pm_cfg; +tBTA_DM_PM_SPEC *const p_bta_dm_pm_spec = (tBTA_DM_PM_SPEC *) &bta_dm_pm_spec; +tBTM_PM_PWR_MD *const p_bta_dm_pm_md = (tBTM_PM_PWR_MD *) &bta_dm_pm_md; #endif /* #if (BTA_DM_PM_INCLUDED == TRUE) */ @@ -441,4 +441,4 @@ tBTA_DM_EIR_CONF bta_dm_eir_cfg = { NULL #endif /* #if (BTC_GAP_BT_INCLUDED == TRUE) */ }; -tBTA_DM_EIR_CONF *p_bta_dm_eir_cfg = (tBTA_DM_EIR_CONF *) &bta_dm_eir_cfg; +tBTA_DM_EIR_CONF *const p_bta_dm_eir_cfg = (tBTA_DM_EIR_CONF *) &bta_dm_eir_cfg; diff --git a/components/bt/bluedroid/bta/dm/bta_dm_pm.c b/components/bt/bluedroid/bta/dm/bta_dm_pm.c index cf55b1c341..5b0978bf59 100644 --- a/components/bt/bluedroid/bta/dm/bta_dm_pm.c +++ b/components/bt/bluedroid/bta/dm/bta_dm_pm.c @@ -32,7 +32,11 @@ #include "stack/btm_api.h" #include "osi/allocator.h" -tBTA_DM_CONNECTED_SRVCS bta_dm_conn_srvcs; +#if BTA_DYNAMIC_MEMORY == FALSE +tBTA_DM_CONNECTED_SRVCS bta_dm_conn_srvcs; +#else +tBTA_DM_CONNECTED_SRVCS *bta_dm_conn_srvcs_ptr; +#endif #if (BTA_DM_PM_INCLUDED == TRUE) static void bta_dm_pm_cback(tBTA_SYS_CONN_STATUS status, UINT8 id, UINT8 app_id, BD_ADDR peer_addr); diff --git a/components/bt/bluedroid/bta/dm/bta_dm_sco.c b/components/bt/bluedroid/bta/dm/bta_dm_sco.c index 9acfa9544a..df91799a87 100644 --- a/components/bt/bluedroid/bta/dm/bta_dm_sco.c +++ b/components/bt/bluedroid/bta/dm/bta_dm_sco.c @@ -28,6 +28,7 @@ #include #include "bta/bta_api.h" #include "bta/bta_sys.h" +#include "osi/allocator.h" #if (BTM_SCO_HCI_INCLUDED == TRUE) @@ -67,7 +68,7 @@ typedef struct { UINT32 divisor; } tBTA_DM_PCM_RESAMPLE_CB; -tBTA_DM_PCM_RESAMPLE_CB bta_dm_pcm_cb; +static tBTA_DM_PCM_RESAMPLE_CB* p_bta_dm_pcm_cb; /***************************************************************************** ** Macro Definition @@ -560,7 +561,11 @@ INT32 Convert_16S_ToBT_NoFilter (void *pSrc, void *pDst, UINT32 dwSrcSamples, UI *******************************************************************************/ void BTA_DmPcmInitSamples (UINT32 src_sps, UINT32 bits, UINT32 n_channels) { - tBTA_DM_PCM_RESAMPLE_CB *p_cb = &bta_dm_pcm_cb; + if ((p_bta_dm_pcm_cb = (tBTA_DM_PCM_RESAMPLE_CB *)osi_malloc(sizeof(tBTA_DM_PCM_RESAMPLE_CB))) == NULL) { + APPL_TRACE_ERROR("%s malloc failed!", __func__); + return; + } + tBTA_DM_PCM_RESAMPLE_CB *p_cb = p_bta_dm_pcm_cb; p_cb->cur_pos = src_sps / 2; p_cb->src_sps = src_sps; @@ -615,6 +620,20 @@ void BTA_DmPcmInitSamples (UINT32 src_sps, UINT32 bits, UINT32 n_channels) } +/******************************************************************************* +** +** Function BTA_DmPcmDeinitSamples +** +** Description Deinitialize the down sample converter. +** +** Returns none +** +*******************************************************************************/ +void BTA_DmPcmDeinitSamples(void) { + osi_free(p_bta_dm_pcm_cb); + p_bta_dm_pcm_cb = NULL; +} + /************************************************************************************** ** Function BTA_DmPcmResample ** @@ -636,14 +655,14 @@ INT32 BTA_DmPcmResample (void *p_src, UINT32 in_bytes, void *p_dst) UINT32 out_sample; #if BTA_DM_SCO_DEBUG - APPL_TRACE_DEBUG("bta_pcm_resample : insamples %d", (in_bytes / bta_dm_pcm_cb.divisor)); + APPL_TRACE_DEBUG("bta_pcm_resample : insamples %d", (in_bytes / p_bta_dm_pcm_cb->divisor)); #endif - if (bta_dm_pcm_cb.can_be_filtered) { - out_sample = (*bta_dm_pcm_cb.filter) (p_src, p_dst, (in_bytes / bta_dm_pcm_cb.divisor), - bta_dm_pcm_cb.src_sps, (INT32 *) &bta_dm_pcm_cb.cur_pos, bta_dm_pcm_cb.overlap_area); + if (p_bta_dm_pcm_cb->can_be_filtered) { + out_sample = (*p_bta_dm_pcm_cb->filter) (p_src, p_dst, (in_bytes / p_bta_dm_pcm_cb->divisor), + p_bta_dm_pcm_cb->src_sps, (INT32 *) &(p_bta_dm_pcm_cb->cur_pos), p_bta_dm_pcm_cb->overlap_area); } else { - out_sample = (*bta_dm_pcm_cb.nofilter) (p_src, p_dst, - (in_bytes / bta_dm_pcm_cb.divisor), bta_dm_pcm_cb.src_sps); + out_sample = (*p_bta_dm_pcm_cb->nofilter) (p_src, p_dst, + (in_bytes / p_bta_dm_pcm_cb->divisor), p_bta_dm_pcm_cb->src_sps); } #if BTA_DM_SCO_DEBUG diff --git a/components/bt/bluedroid/bta/dm/include/bta_dm_int.h b/components/bt/bluedroid/bta/dm/include/bta_dm_int.h index 2c21b0c574..83fdb3bab7 100644 --- a/components/bt/bluedroid/bta/dm/include/bta_dm_int.h +++ b/components/bt/bluedroid/bta/dm/include/bta_dm_int.h @@ -976,7 +976,6 @@ typedef struct { } tBTA_DM_CONNECTED_SRVCS; -extern tBTA_DM_CONNECTED_SRVCS bta_dm_conn_srvcs; #if (BTA_DM_PM_INCLUDED == TRUE) @@ -1150,8 +1149,8 @@ typedef struct { } tBTA_DM_RM ; -extern tBTA_DM_CFG *p_bta_dm_cfg; -extern tBTA_DM_RM *p_bta_dm_rm_cfg; +extern tBTA_DM_CFG *const p_bta_dm_cfg; +extern tBTA_DM_RM *const p_bta_dm_rm_cfg; typedef struct { @@ -1192,11 +1191,11 @@ typedef struct { } tBTA_DM_LMP_VER_INFO; #if (BTA_DM_PM_INCLUDED == TRUE) -extern tBTA_DM_PM_CFG *p_bta_dm_pm_cfg; -extern tBTA_DM_PM_SPEC *p_bta_dm_pm_spec; -extern tBTM_PM_PWR_MD *p_bta_dm_pm_md; +extern tBTA_DM_PM_CFG *const p_bta_dm_pm_cfg; +extern tBTA_DM_PM_SPEC *const p_bta_dm_pm_spec; +extern tBTM_PM_PWR_MD *const p_bta_dm_pm_md; #if (BTM_SSR_INCLUDED == TRUE) -extern tBTA_DM_SSR_SPEC *p_bta_dm_ssr_spec; +extern tBTA_DM_SSR_SPEC *const p_bta_dm_ssr_spec; #endif #endif /* #if (BTA_DM_PM_INCLUDED == TRUE) */ @@ -1228,6 +1227,19 @@ extern tBTA_DM_DI_CB *bta_dm_di_cb_ptr; #define bta_dm_di_cb (*bta_dm_di_cb_ptr) #endif +#if BTA_DYNAMIC_MEMORY == FALSE +extern tBTA_DM_CONNECTED_SRVCS bta_dm_conn_srvcs; +#else +extern tBTA_DM_CONNECTED_SRVCS *bta_dm_conn_srvcs_ptr; +#define bta_dm_conn_srvcs (*bta_dm_conn_srvcs_ptr) +#endif + +/* Discovery raw data buffer */ +#define MAX_DISC_RAW_DATA_BUF (1024) +#if BTA_DYNAMIC_MEMORY == TRUE +extern UINT8 *g_disc_raw_data_buf; +#endif + extern BOOLEAN bta_dm_sm_execute(BT_HDR *p_msg); extern void bta_dm_sm_disable( void ); extern void bta_dm_sm_deinit(void); diff --git a/components/bt/bluedroid/bta/gatt/bta_gattc_act.c b/components/bt/bluedroid/bta/gatt/bta_gattc_act.c index e6df3e5189..9f8f2b5198 100644 --- a/components/bt/bluedroid/bta/gatt/bta_gattc_act.c +++ b/components/bt/bluedroid/bta/gatt/bta_gattc_act.c @@ -68,7 +68,7 @@ static void bta_gattc_cong_cback (UINT16 conn_id, BOOLEAN congested); static void bta_gattc_req_cback (UINT16 conn_id, UINT32 trans_id, tGATTS_REQ_TYPE type, tGATTS_DATA *p_data); static tBTA_GATTC_FIND_SERVICE_CB bta_gattc_register_service_change_notify(UINT16 conn_id, BD_ADDR remote_bda); -static tGATT_CBACK bta_gattc_cl_cback = { +static const tGATT_CBACK bta_gattc_cl_cback = { bta_gattc_conn_cback, bta_gattc_cmpl_cback, bta_gattc_disc_res_cback, @@ -79,7 +79,7 @@ static tGATT_CBACK bta_gattc_cl_cback = { }; /* opcode(tGATTC_OPTYPE) order has to be comply with internal event order */ -static UINT16 bta_gattc_opcode_to_int_evt[] = { +static const UINT16 bta_gattc_opcode_to_int_evt[] = { BTA_GATTC_API_READ_EVT, BTA_GATTC_API_WRITE_EVT, BTA_GATTC_API_EXEC_EVT, diff --git a/components/bt/bluedroid/bta/gatt/bta_gattc_cache.c b/components/bt/bluedroid/bta/gatt/bta_gattc_cache.c index 355d619a09..1da1000ae7 100644 --- a/components/bt/bluedroid/bta/gatt/bta_gattc_cache.c +++ b/components/bt/bluedroid/bta/gatt/bta_gattc_cache.c @@ -26,6 +26,7 @@ #include "common/bt_target.h" #if defined(GATTC_INCLUDED) && (GATTC_INCLUDED == TRUE) +//#if( defined GATTC_CACHE_NVS ) && (GATTC_CACHE_NVS == TRUE) #include #include "bta/utl.h" @@ -2190,5 +2191,7 @@ void bta_gattc_cache_reset(BD_ADDR server_bda) bta_gattc_co_cache_reset(server_bda); //unlink(fname); } + +//#endif /* GATTC_CACHE_NVS */ #endif /* BTA_GATT_INCLUDED */ diff --git a/components/bt/bluedroid/bta/gatt/bta_gattc_co.c b/components/bt/bluedroid/bta/gatt/bta_gattc_co.c index c11fb95895..98444458ae 100644 --- a/components/bt/bluedroid/bta/gatt/bta_gattc_co.c +++ b/components/bt/bluedroid/bta/gatt/bta_gattc_co.c @@ -33,6 +33,7 @@ #if( defined BLE_INCLUDED ) && (BLE_INCLUDED == TRUE) #if( defined BTA_GATT_INCLUDED ) && (GATTC_INCLUDED == TRUE) +// #if( defined GATTC_CACHE_NVS ) && (GATTC_CACHE_NVS == TRUE) #define GATT_CACHE_PREFIX "gatt_" #define INVALID_ADDR_NUM 0xff @@ -77,7 +78,6 @@ static void cacheReset(BD_ADDR bda) static const char *cache_key = "gattc_cache_key"; static const char *cache_addr = "cache_addr_tab"; -nvs_handle_t nvs_fp; typedef struct { //save the service data in the list according to the address @@ -96,7 +96,7 @@ typedef struct { cache_addr_info_t cache_addr[MAX_DEVICE_IN_CACHE]; }cache_env_t; -cache_env_t cache_env; +cache_env_t *cache_env = NULL; static void getFilename(char *buffer, hash_key_t hash) { @@ -108,9 +108,9 @@ static void cacheClose(BD_ADDR bda) { UINT8 index = 0; if ((index = bta_gattc_co_find_addr_in_cache(bda)) != INVALID_ADDR_NUM) { - if (cache_env.cache_addr[index].is_open) { - nvs_close(cache_env.cache_addr[index].cache_fp); - cache_env.cache_addr[index].is_open = FALSE; + if (cache_env->cache_addr[index].is_open) { + nvs_close(cache_env->cache_addr[index].cache_fp); + cache_env->cache_addr[index].is_open = FALSE; } } } @@ -124,18 +124,18 @@ static bool cacheOpen(BD_ADDR bda, bool to_save, UINT8 *index) hash_key_t hash_key = {0}; if (((*index = bta_gattc_co_find_addr_in_cache(bda)) != INVALID_ADDR_NUM) || ((assoc_addr = bta_gattc_co_cache_find_src_addr(bda, index)) != NULL)) { - if (cache_env.cache_addr[*index].is_open) { + if (cache_env->cache_addr[*index].is_open) { return TRUE; } else { - memcpy(hash_key, cache_env.cache_addr[*index].hash_key, sizeof(hash_key_t)); + memcpy(hash_key, cache_env->cache_addr[*index].hash_key, sizeof(hash_key_t)); getFilename(fname, hash_key); - if ((status = nvs_open(fname, NVS_READWRITE, &cache_env.cache_addr[*index].cache_fp)) == ESP_OK) { + if ((status = nvs_open(fname, NVS_READWRITE, &cache_env->cache_addr[*index].cache_fp)) == ESP_OK) { // Set the open flag to TRUE when success to open the hash file. - cache_env.cache_addr[*index].is_open = TRUE; + cache_env->cache_addr[*index].is_open = TRUE; } } } - + return ((status == ESP_OK) ? true : false); } @@ -144,67 +144,67 @@ static void cacheReset(BD_ADDR bda) char fname[255] = {0}; getFilename(fname, bda); UINT8 index = 0; - //cache_env.cache_addr + //cache_env->cache_addr if ((index = bta_gattc_co_find_addr_in_cache(bda)) != INVALID_ADDR_NUM) { //clear the association address pending in the source address. bta_gattc_co_cache_clear_assoc_addr(bda); - if (cache_env.cache_addr[index].is_open) { - nvs_erase_all(cache_env.cache_addr[index].cache_fp); - nvs_close(cache_env.cache_addr[index].cache_fp); - cache_env.cache_addr[index].is_open = FALSE; + if (cache_env->cache_addr[index].is_open) { + nvs_erase_all(cache_env->cache_addr[index].cache_fp); + nvs_close(cache_env->cache_addr[index].cache_fp); + cache_env->cache_addr[index].is_open = FALSE; } else { cacheOpen(bda, false, &index); - if (cache_env.cache_addr[index].is_open) { - nvs_erase_all(cache_env.cache_addr[index].cache_fp); - nvs_close(cache_env.cache_addr[index].cache_fp); - cache_env.cache_addr[index].is_open = FALSE; + if (cache_env->cache_addr[index].is_open) { + nvs_erase_all(cache_env->cache_addr[index].cache_fp); + nvs_close(cache_env->cache_addr[index].cache_fp); + cache_env->cache_addr[index].is_open = FALSE; } else { APPL_TRACE_ERROR("%s cacheOpen failed", __func__); return; } } - if(cache_env.num_addr == 0) { + if(cache_env->num_addr == 0) { APPL_TRACE_ERROR("%s cache addr list error", __func__); return; } - UINT8 num = cache_env.num_addr; + UINT8 num = cache_env->num_addr; //delete the server_bda in the addr_info list. for(UINT8 i = index; i < (num - 1); i++) { - memcpy(&cache_env.cache_addr[i], &cache_env.cache_addr[i+1], sizeof(cache_addr_info_t)); + memcpy(&cache_env->cache_addr[i], &cache_env->cache_addr[i+1], sizeof(cache_addr_info_t)); } //reduced the number address counter also - cache_env.num_addr--; + cache_env->num_addr--; //update addr list to nvs flash - if(cache_env.num_addr > 0) { + if(cache_env->num_addr > 0) { //update UINT8 *p_buf = osi_malloc(MAX_ADDR_LIST_CACHE_BUF); if(!p_buf) { - APPL_TRACE_ERROR("%s malloc error", __func__); - return; + APPL_TRACE_ERROR("%s malloc error", __func__); + return; } - UINT16 length = cache_env.num_addr*(sizeof(BD_ADDR) + sizeof(hash_key_t)); - for (UINT8 i = 0; i < cache_env.num_addr; i++) { + UINT16 length = cache_env->num_addr*(sizeof(BD_ADDR) + sizeof(hash_key_t)); + for (UINT8 i = 0; i < cache_env->num_addr; i++) { //copy the address to the buffer. - memcpy(p_buf + i*(sizeof(BD_ADDR) + sizeof(hash_key_t)), cache_env.cache_addr[i].addr, sizeof(BD_ADDR)); + memcpy(p_buf + i*(sizeof(BD_ADDR) + sizeof(hash_key_t)), cache_env->cache_addr[i].addr, sizeof(BD_ADDR)); //copy the hash key to the buffer. memcpy(p_buf + i*(sizeof(BD_ADDR) + sizeof(hash_key_t)) + sizeof(BD_ADDR), - cache_env.cache_addr[i].hash_key, sizeof(hash_key_t)); + cache_env->cache_addr[i].hash_key, sizeof(hash_key_t)); } - if (cache_env.is_open) { - if (nvs_set_blob(cache_env.addr_fp, cache_key, p_buf, length) != ESP_OK) { + if (cache_env->is_open) { + if (nvs_set_blob(cache_env->addr_fp, cache_key, p_buf, length) != ESP_OK) { APPL_TRACE_WARNING("%s, nvs set blob failed", __func__); } } osi_free(p_buf); - + } else { //erase - if (cache_env.is_open) { - nvs_erase_all(cache_env.addr_fp); - nvs_close(cache_env.addr_fp); - cache_env.is_open = FALSE; + if (cache_env->is_open) { + nvs_erase_all(cache_env->addr_fp); + nvs_close(cache_env->addr_fp); + cache_env->is_open = FALSE; } else { APPL_TRACE_WARNING("cache_env status is error"); } @@ -267,10 +267,10 @@ tBTA_GATT_STATUS bta_gattc_co_cache_load(tBTA_GATTC_NV_ATTR *attr, UINT8 index) tBTA_GATT_STATUS status = BTA_GATT_ERROR; size_t length = 0; // Read the size of memory space required for blob - nvs_get_blob(cache_env.cache_addr[index].cache_fp, cache_key, NULL, &length); + nvs_get_blob(cache_env->cache_addr[index].cache_fp, cache_key, NULL, &length); // Read previously saved blob if available - esp_err_t err_code = nvs_get_blob(cache_env.cache_addr[index].cache_fp, cache_key, attr, &length); #if (!CONFIG_BT_STACK_NO_LOG) + esp_err_t err_code = nvs_get_blob(cache_env->cache_addr[index].cache_fp, cache_key, attr, &length); num_attr = length / sizeof(tBTA_GATTC_NV_ATTR); #endif status = (err_code == ESP_OK && length != 0) ? BTA_GATT_OK : BTA_GATT_ERROR; @@ -288,7 +288,7 @@ size_t bta_gattc_get_cache_attr_length(UINT8 index) } // Read the size of memory space required for blob - nvs_get_blob(cache_env.cache_addr[index].cache_fp, cache_key, NULL, &length); + nvs_get_blob(cache_env->cache_addr[index].cache_fp, cache_key, NULL, &length); return length; } @@ -320,7 +320,7 @@ void bta_gattc_co_cache_save (BD_ADDR server_bda, UINT16 num_attr, bta_gattc_co_cache_addr_save(server_bda, hash_key); if (cacheOpen(server_bda, TRUE, &index)) { - esp_err_t err_code = nvs_set_blob(cache_env.cache_addr[index].cache_fp, cache_key, + esp_err_t err_code = nvs_set_blob(cache_env->cache_addr[index].cache_fp, cache_key, p_attr_list, sizeof(tBTA_GATTC_NV_ATTR)*num_attr); status = (err_code == ESP_OK) ? BTA_GATT_OK : BTA_GATT_ERROR; } else { @@ -380,12 +380,18 @@ void bta_gattc_co_cache_addr_init(void) nvs_handle_t fp; esp_err_t err_code; UINT8 num_addr; - UINT8 *p_buf = osi_malloc(MAX_ADDR_LIST_CACHE_BUF); size_t length = MAX_ADDR_LIST_CACHE_BUF; + UINT8 *p_buf = osi_malloc(MAX_ADDR_LIST_CACHE_BUF); + + cache_env = (cache_env_t *)osi_malloc(sizeof(cache_env_t)); + if (cache_env == NULL || p_buf == NULL) { + APPL_TRACE_ERROR("%s malloc failed!", __func__); + return; + } if ((err_code = nvs_open(cache_addr, NVS_READWRITE, &fp)) == ESP_OK) { - cache_env.addr_fp = fp; - cache_env.is_open = TRUE; + cache_env->addr_fp = fp; + cache_env->is_open = TRUE; // Read previously saved blob if available if ((err_code = nvs_get_blob(fp, cache_key, p_buf, &length)) != ESP_OK) { if(err_code != ESP_ERR_NVS_NOT_FOUND) { @@ -395,18 +401,18 @@ void bta_gattc_co_cache_addr_init(void) return; } num_addr = length / (sizeof(BD_ADDR) + sizeof(hash_key_t)); - cache_env.num_addr = num_addr; + cache_env->num_addr = num_addr; //read the address from nvs flash to cache address list. for (UINT8 i = 0; i < num_addr; i++) { - memcpy(cache_env.cache_addr[i].addr, p_buf + i*(sizeof(BD_ADDR) + sizeof(hash_key_t)), sizeof(BD_ADDR)); - memcpy(cache_env.cache_addr[i].hash_key, + memcpy(cache_env->cache_addr[i].addr, p_buf + i*(sizeof(BD_ADDR) + sizeof(hash_key_t)), sizeof(BD_ADDR)); + memcpy(cache_env->cache_addr[i].hash_key, p_buf + i*(sizeof(BD_ADDR) + sizeof(hash_key_t)) + sizeof(BD_ADDR), sizeof(hash_key_t)); - APPL_TRACE_DEBUG("cache_addr[%x] = %x:%x:%x:%x:%x:%x", i, cache_env.cache_addr[i].addr[0], cache_env.cache_addr[i].addr[1], cache_env.cache_addr[i].addr[2], - cache_env.cache_addr[i].addr[3], cache_env.cache_addr[i].addr[4], cache_env.cache_addr[i].addr[5]); - APPL_TRACE_DEBUG("hash_key[%x] = %x%x%x%x", i, cache_env.cache_addr[i].hash_key[0], cache_env.cache_addr[i].hash_key[1], - cache_env.cache_addr[i].hash_key[2], cache_env.cache_addr[i].hash_key[3]); - bta_gattc_co_cache_new_assoc_list(cache_env.cache_addr[i].addr, i); + APPL_TRACE_DEBUG("cache_addr[%x] = %x:%x:%x:%x:%x:%x", i, cache_env->cache_addr[i].addr[0], cache_env->cache_addr[i].addr[1], cache_env->cache_addr[i].addr[2], + cache_env->cache_addr[i].addr[3], cache_env->cache_addr[i].addr[4], cache_env->cache_addr[i].addr[5]); + APPL_TRACE_DEBUG("hash_key[%x] = %x%x%x%x", i, cache_env->cache_addr[i].hash_key[0], cache_env->cache_addr[i].hash_key[1], + cache_env->cache_addr[i].hash_key[2], cache_env->cache_addr[i].hash_key[3]); + bta_gattc_co_cache_new_assoc_list(cache_env->cache_addr[i].addr, i); } } else { APPL_TRACE_ERROR("%s, Line = %d, nvs flash open fail, err_code = %x", __func__, __LINE__, err_code); @@ -420,14 +426,14 @@ void bta_gattc_co_cache_addr_init(void) void bta_gattc_co_cache_addr_deinit(void) { - if(!cache_env.is_open) { + if(!cache_env->is_open) { return; - } - nvs_close(cache_env.addr_fp); - cache_env.is_open = false; - - for(UINT8 i = 0; i< cache_env.num_addr; i++) { - cache_addr_info_t *addr_info = &cache_env.cache_addr[i]; + } + nvs_close(cache_env->addr_fp); + cache_env->is_open = false; + + for(UINT8 i = 0; i< cache_env->num_addr; i++) { + cache_addr_info_t *addr_info = &cache_env->cache_addr[i]; if(addr_info) { nvs_close(addr_info->cache_fp); addr_info->is_open = false; @@ -436,13 +442,16 @@ void bta_gattc_co_cache_addr_deinit(void) } } } + + osi_free(cache_env); + cache_env = NULL; } BOOLEAN bta_gattc_co_addr_in_cache(BD_ADDR bda) { UINT8 addr_index = 0; - UINT8 num = cache_env.num_addr; - cache_addr_info_t *addr_info = &cache_env.cache_addr[0]; + UINT8 num = cache_env->num_addr; + cache_addr_info_t *addr_info = &cache_env->cache_addr[0]; for (addr_index = 0; addr_index < num; addr_index++) { if (!memcmp(addr_info->addr, bda, sizeof(BD_ADDR))) { return TRUE; @@ -455,8 +464,8 @@ BOOLEAN bta_gattc_co_addr_in_cache(BD_ADDR bda) UINT8 bta_gattc_co_find_addr_in_cache(BD_ADDR bda) { UINT8 addr_index = 0; - UINT8 num = cache_env.num_addr; - cache_addr_info_t *addr_info = &cache_env.cache_addr[0]; + UINT8 num = cache_env->num_addr; + cache_addr_info_t *addr_info = &cache_env->cache_addr[0]; for (addr_index = 0; addr_index < num; addr_index++, addr_info++) { if (!memcmp(addr_info->addr, bda, sizeof(BD_ADDR))) { @@ -470,8 +479,8 @@ UINT8 bta_gattc_co_find_addr_in_cache(BD_ADDR bda) UINT8 bta_gattc_co_find_hash_in_cache(hash_key_t hash_key) { UINT8 index = 0; - UINT8 num = cache_env.num_addr; - cache_addr_info_t *addr_info = &cache_env.cache_addr[0]; + UINT8 num = cache_env->num_addr; + cache_addr_info_t *addr_info = &cache_env->cache_addr[0]; for (index = 0; index < num; index++) { if (!memcmp(addr_info->hash_key, hash_key, sizeof(hash_key_t))) { return index; @@ -483,21 +492,21 @@ UINT8 bta_gattc_co_find_hash_in_cache(hash_key_t hash_key) UINT8 bta_gattc_co_get_addr_num(void) { - return cache_env.num_addr; + return cache_env->num_addr; } void bta_gattc_co_get_addr_list(BD_ADDR *addr_list) { - UINT8 num = cache_env.num_addr; + UINT8 num = cache_env->num_addr; for (UINT8 i = 0; i < num; i++) { - memcpy(addr_list[i], cache_env.cache_addr[i].addr, sizeof(BD_ADDR)); + memcpy(addr_list[i], cache_env->cache_addr[i].addr, sizeof(BD_ADDR)); } } void bta_gattc_co_cache_addr_save(BD_ADDR bd_addr, hash_key_t hash_key) { esp_err_t err_code; - UINT8 num = ++cache_env.num_addr; + UINT8 num = ++cache_env->num_addr; UINT8 index = 0; UINT8 *p_buf = osi_malloc(MAX_ADDR_LIST_CACHE_BUF); // check the address list has the same hash key or not @@ -506,39 +515,39 @@ void bta_gattc_co_cache_addr_save(BD_ADDR bd_addr, hash_key_t hash_key) if ((index = bta_gattc_co_find_addr_in_cache(bd_addr)) != INVALID_ADDR_NUM) { APPL_TRACE_DEBUG("%s(), the hash bd_addr already in the cache list, index = %x", __func__, index); //if the bd_addr already in the address list, update the hash key in it. - memcpy(cache_env.cache_addr[index].addr, bd_addr, sizeof(BD_ADDR)); - memcpy(cache_env.cache_addr[index].hash_key, hash_key, sizeof(hash_key_t)); + memcpy(cache_env->cache_addr[index].addr, bd_addr, sizeof(BD_ADDR)); + memcpy(cache_env->cache_addr[index].hash_key, hash_key, sizeof(hash_key_t)); } else { //if the bd_addr didn't in the address list, added the bd_addr to the last of the address list. - memcpy(cache_env.cache_addr[num - 1].hash_key, hash_key, sizeof(hash_key_t)); - memcpy(cache_env.cache_addr[num - 1].addr, bd_addr, sizeof(BD_ADDR)); + memcpy(cache_env->cache_addr[num - 1].hash_key, hash_key, sizeof(hash_key_t)); + memcpy(cache_env->cache_addr[num - 1].addr, bd_addr, sizeof(BD_ADDR)); } } else { APPL_TRACE_DEBUG("%s(), num = %d", __func__, num); - memcpy(cache_env.cache_addr[num - 1].addr, bd_addr, sizeof(BD_ADDR)); - memcpy(cache_env.cache_addr[num - 1].hash_key, hash_key, sizeof(hash_key_t)); + memcpy(cache_env->cache_addr[num - 1].addr, bd_addr, sizeof(BD_ADDR)); + memcpy(cache_env->cache_addr[num - 1].hash_key, hash_key, sizeof(hash_key_t)); } - nvs_handle_t *fp = &cache_env.addr_fp; + nvs_handle_t *fp = &cache_env->addr_fp; UINT16 length = num*(sizeof(BD_ADDR) + sizeof(hash_key_t)); for (UINT8 i = 0; i < num; i++) { //copy the address to the buffer. - memcpy(p_buf + i*(sizeof(BD_ADDR) + sizeof(hash_key_t)), cache_env.cache_addr[i].addr, sizeof(BD_ADDR)); + memcpy(p_buf + i*(sizeof(BD_ADDR) + sizeof(hash_key_t)), cache_env->cache_addr[i].addr, sizeof(BD_ADDR)); //copy the hash key to the buffer. memcpy(p_buf + i*(sizeof(BD_ADDR) + sizeof(hash_key_t)) + sizeof(BD_ADDR), - cache_env.cache_addr[i].hash_key, sizeof(hash_key_t)); + cache_env->cache_addr[i].hash_key, sizeof(hash_key_t)); } - if (cache_env.is_open) { - if ((err_code = nvs_set_blob(cache_env.addr_fp, cache_key, p_buf, length)) != ESP_OK) { + if (cache_env->is_open) { + if ((err_code = nvs_set_blob(cache_env->addr_fp, cache_key, p_buf, length)) != ESP_OK) { APPL_TRACE_WARNING("%s(), nvs set blob fail, err %d", __func__, err_code); } } else { if ((err_code = nvs_open(cache_addr, NVS_READWRITE , fp)) == ESP_OK) { - cache_env.is_open = true; - if (( err_code = nvs_set_blob(cache_env.addr_fp, cache_key, p_buf, length)) != ESP_OK) { + cache_env->is_open = true; + if (( err_code = nvs_set_blob(cache_env->addr_fp, cache_key, p_buf, length)) != ESP_OK) { APPL_TRACE_WARNING("%s(), nvs set blob fail, err %d", __func__, err_code); } } else { @@ -553,7 +562,7 @@ void bta_gattc_co_cache_addr_save(BD_ADDR bd_addr, hash_key_t hash_key) BOOLEAN bta_gattc_co_cache_new_assoc_list(BD_ADDR src_addr, UINT8 index) { - cache_addr_info_t *addr_info = &cache_env.cache_addr[index]; + cache_addr_info_t *addr_info = &cache_env->cache_addr[index]; addr_info->assoc_addr = list_new(osi_free_func); return (addr_info->assoc_addr != NULL ? TRUE : FALSE); } @@ -565,7 +574,7 @@ BOOLEAN bta_gattc_co_cache_append_assoc_addr(BD_ADDR src_addr, BD_ADDR assoc_add UINT8 *p_assoc_buf = osi_malloc(sizeof(BD_ADDR)); memcpy(p_assoc_buf, assoc_addr, sizeof(BD_ADDR)); if ((addr_index = bta_gattc_co_find_addr_in_cache(src_addr)) != INVALID_ADDR_NUM) { - addr_info = &cache_env.cache_addr[addr_index]; + addr_info = &cache_env->cache_addr[addr_index]; if (addr_info->assoc_addr == NULL) { addr_info->assoc_addr =list_new(NULL); } @@ -580,7 +589,7 @@ BOOLEAN bta_gattc_co_cache_remove_assoc_addr(BD_ADDR src_addr, BD_ADDR assoc_add UINT8 addr_index = 0; cache_addr_info_t *addr_info; if ((addr_index = bta_gattc_co_find_addr_in_cache(src_addr)) != INVALID_ADDR_NUM) { - addr_info = &cache_env.cache_addr[addr_index]; + addr_info = &cache_env->cache_addr[addr_index]; if (addr_info->assoc_addr != NULL) { for (list_node_t *sn = list_begin(addr_info->assoc_addr); sn != list_end(addr_info->assoc_addr); sn = list_next(sn)) { @@ -600,8 +609,8 @@ BOOLEAN bta_gattc_co_cache_remove_assoc_addr(BD_ADDR src_addr, BD_ADDR assoc_add UINT8* bta_gattc_co_cache_find_src_addr(BD_ADDR assoc_addr, UINT8 *index) { - UINT8 num = cache_env.num_addr; - cache_addr_info_t *addr_info = &cache_env.cache_addr[0]; + UINT8 num = cache_env->num_addr; + cache_addr_info_t *addr_info = &cache_env->cache_addr[0]; UINT8 *addr_data; //Check the assoc_addr list is NULL or not if (addr_info->assoc_addr == NULL) { @@ -610,7 +619,7 @@ UINT8* bta_gattc_co_cache_find_src_addr(BD_ADDR assoc_addr, UINT8 *index) } for (int i = 0; i < num; i++) { - for (const list_node_t *node = list_begin(addr_info->assoc_addr); node != list_end(addr_info->assoc_addr); + for (const list_node_t *node = list_begin(addr_info->assoc_addr); node != list_end(addr_info->assoc_addr); node = list_next(node)) { addr_data = (UINT8 *)list_node(node); if (!memcmp(addr_data, assoc_addr, sizeof(BD_ADDR))) { @@ -635,7 +644,7 @@ BOOLEAN bta_gattc_co_cache_clear_assoc_addr(BD_ADDR src_addr) UINT8 addr_index = 0; cache_addr_info_t *addr_info; if ((addr_index = bta_gattc_co_find_addr_in_cache(src_addr)) != INVALID_ADDR_NUM) { - addr_info = &cache_env.cache_addr[addr_index]; + addr_info = &cache_env->cache_addr[addr_index]; if (addr_info->assoc_addr != NULL) { list_clear(addr_info->assoc_addr); } else { @@ -647,6 +656,7 @@ BOOLEAN bta_gattc_co_cache_clear_assoc_addr(BD_ADDR src_addr) return FALSE; } +// #endif /* #if( defined GATTC_CACHE_NVS ) && (GATTC_CACHE_NVS == TRUE) */ #endif /* #if( defined BLE_INCLUDED ) && (BLE_INCLUDED == TRUE) */ #endif /* #if( defined BTA_GATT_INCLUDED ) && (BTA_GATT_INCLUDED == TRUE) */ diff --git a/components/bt/bluedroid/bta/gatt/bta_gatts_act.c b/components/bt/bluedroid/bta/gatt/bta_gatts_act.c index 95cb668c6b..4dd804a47a 100644 --- a/components/bt/bluedroid/bta/gatt/bta_gatts_act.c +++ b/components/bt/bluedroid/bta/gatt/bta_gatts_act.c @@ -49,7 +49,7 @@ static void bta_gatts_send_request_cback (UINT16 conn_id, tGATTS_REQ_TYPE req_type, tGATTS_DATA *p_data); static void bta_gatts_cong_cback (UINT16 conn_id, BOOLEAN congested); -static tGATT_CBACK bta_gatts_cback = { +static const tGATT_CBACK bta_gatts_cback = { bta_gatts_conn_cback, NULL, NULL, @@ -59,7 +59,7 @@ static tGATT_CBACK bta_gatts_cback = { bta_gatts_cong_cback }; -tGATT_APPL_INFO bta_gatts_nv_cback = { +const tGATT_APPL_INFO bta_gatts_nv_cback = { bta_gatts_nv_save_cback, bta_gatts_nv_srv_chg_cback }; diff --git a/components/bt/bluedroid/bta/gatt/bta_gatts_api.c b/components/bt/bluedroid/bta/gatt/bta_gatts_api.c index 87e559ab72..aefe62d65a 100644 --- a/components/bt/bluedroid/bta/gatt/bta_gatts_api.c +++ b/components/bt/bluedroid/bta/gatt/bta_gatts_api.c @@ -82,7 +82,7 @@ void BTA_GATTS_Disable(void) ** Returns None ** *******************************************************************************/ -void BTA_GATTS_AppRegister(tBT_UUID *p_app_uuid, tBTA_GATTS_CBACK *p_cback) +void BTA_GATTS_AppRegister(const tBT_UUID * p_app_uuid, tBTA_GATTS_CBACK *p_cback) { tBTA_GATTS_API_REG *p_buf; @@ -149,7 +149,7 @@ void BTA_GATTS_AppDeregister(tBTA_GATTS_IF server_if) ** Returns void ** *******************************************************************************/ -void BTA_GATTS_CreateService(tBTA_GATTS_IF server_if, tBT_UUID *p_service_uuid, UINT8 inst, +void BTA_GATTS_CreateService(tBTA_GATTS_IF server_if, const tBT_UUID * p_service_uuid, UINT8 inst, UINT16 num_handle, BOOLEAN is_primary) { tBTA_GATTS_API_CREATE_SRVC *p_buf; @@ -214,8 +214,8 @@ void BTA_GATTS_AddIncludeService(UINT16 service_id, UINT16 included_service_id) ** Returns None ** *******************************************************************************/ -void BTA_GATTS_AddCharacteristic (UINT16 service_id, tBT_UUID *p_char_uuid, - tBTA_GATT_PERM perm, tBTA_GATT_CHAR_PROP property, tGATT_ATTR_VAL *attr_val, +void BTA_GATTS_AddCharacteristic (UINT16 service_id, const tBT_UUID * p_char_uuid, + tBTA_GATT_PERM perm, tBTA_GATT_CHAR_PROP property, tGATT_ATTR_VAL *attr_val, tBTA_GATTS_ATTR_CONTROL *control) { tBTA_GATTS_API_ADD_CHAR *p_buf; @@ -270,7 +270,7 @@ void BTA_GATTS_AddCharacteristic (UINT16 service_id, tBT_UUID *p_char_uuid, *******************************************************************************/ void BTA_GATTS_AddCharDescriptor (UINT16 service_id, tBTA_GATT_PERM perm, - tBT_UUID *p_descr_uuid, tBTA_GATT_ATTR_VAL *attr_val, + const tBT_UUID * p_descr_uuid, tBTA_GATT_ATTR_VAL *attr_val, tBTA_GATTS_ATTR_CONTROL *control) { tBTA_GATTS_API_ADD_DESCR *p_buf; diff --git a/components/bt/bluedroid/bta/include/bta/bta_api.h b/components/bt/bluedroid/bta/include/bta/bta_api.h index a7dc0185f3..aed71d7e46 100644 --- a/components/bt/bluedroid/bta/include/bta/bta_api.h +++ b/components/bt/bluedroid/bta/include/bta/bta_api.h @@ -1809,6 +1809,17 @@ extern void bta_dmexecutecallback (tBTA_DM_EXEC_CBACK *p_callback, void *p_param *******************************************************************************/ extern void BTA_DmPcmInitSamples (UINT32 src_sps, UINT32 bits, UINT32 n_channels); +/******************************************************************************* +** +** Function BTA_DmPcmDeinitSamples +** +** Description Deinitialize the down sample converter. +** +** Returns none +** +*******************************************************************************/ +extern void BTA_DmPcmDeinitSamples(void); + /************************************************************************************** ** Function BTA_DmPcmResample ** diff --git a/components/bt/bluedroid/bta/include/bta/bta_gatt_api.h b/components/bt/bluedroid/bta/include/bta/bta_gatt_api.h index cb69110b60..664536ddd1 100644 --- a/components/bt/bluedroid/bta/include/bta/bta_gatt_api.h +++ b/components/bt/bluedroid/bta/include/bta/bta_gatt_api.h @@ -30,7 +30,6 @@ #include "osi/list.h" #ifndef BTA_GATT_INCLUDED -#warning BTA_GATT_INCLUDED not defined #define BTA_GATT_INCLUDED FALSE #endif @@ -881,7 +880,7 @@ extern const tBTA_GATTC_CHARACTERISTIC* BTA_GATTC_GetCharacteristic(UINT16 conn_ *******************************************************************************/ extern const tBTA_GATTC_DESCRIPTOR* BTA_GATTC_GetDescriptor(UINT16 conn_id, UINT16 handle); -extern void BTA_GATTC_GetServiceWithUUID(UINT16 conn_id, tBT_UUID *svc_uuid, +extern void BTA_GATTC_GetServiceWithUUID(UINT16 conn_id, tBT_UUID *svc_uuid, btgatt_db_element_t **db, int *count); extern void BTA_GATTC_GetAllChar(UINT16 conn_id, UINT16 start_handle, UINT16 end_handle, @@ -1234,7 +1233,7 @@ extern void BTA_GATTS_Disable(void); ** Returns None ** *******************************************************************************/ -extern void BTA_GATTS_AppRegister(tBT_UUID *p_app_uuid, tBTA_GATTS_CBACK *p_cback); +extern void BTA_GATTS_AppRegister(const tBT_UUID * p_app_uuid, tBTA_GATTS_CBACK *p_cback); /******************************************************************************* @@ -1269,7 +1268,7 @@ extern void BTA_GATTS_AppDeregister(tBTA_GATTS_IF server_if); ** Returns void ** *******************************************************************************/ -extern void BTA_GATTS_CreateService(tBTA_GATTS_IF server_if, tBT_UUID *p_service_uuid, +extern void BTA_GATTS_CreateService(tBTA_GATTS_IF server_if, const tBT_UUID * p_service_uuid, UINT8 inst, UINT16 num_handle, BOOLEAN is_primary); /******************************************************************************* @@ -1304,8 +1303,8 @@ extern void BTA_GATTS_AddIncludeService(UINT16 service_id, UINT16 included_servi ** Returns None ** *******************************************************************************/ -extern void BTA_GATTS_AddCharacteristic (UINT16 service_id, tBT_UUID *p_char_uuid, - tBTA_GATT_PERM perm, tBTA_GATT_CHAR_PROP property, tGATT_ATTR_VAL *attr_val, +extern void BTA_GATTS_AddCharacteristic (UINT16 service_id, const tBT_UUID * p_char_uuid, + tBTA_GATT_PERM perm, tBTA_GATT_CHAR_PROP property, tGATT_ATTR_VAL *attr_val, tBTA_GATTS_ATTR_CONTROL *control); /******************************************************************************* @@ -1327,7 +1326,7 @@ extern void BTA_GATTS_AddCharacteristic (UINT16 service_id, tBT_UUID *p_char_u *******************************************************************************/ extern void BTA_GATTS_AddCharDescriptor (UINT16 service_id, tBTA_GATT_PERM perm, - tBT_UUID *p_descr_uuid, tBTA_GATT_ATTR_VAL *attr_val, + const tBT_UUID * p_descr_uuid, tBTA_GATT_ATTR_VAL *attr_val, tBTA_GATTS_ATTR_CONTROL *control); /******************************************************************************* diff --git a/components/bt/bluedroid/bta/include/bta/bta_sdp_api.h b/components/bt/bluedroid/bta/include/bta/bta_sdp_api.h index b88c0c263c..1d32b9204f 100644 --- a/components/bt/bluedroid/bta/include/bta/bta_sdp_api.h +++ b/components/bt/bluedroid/bta/include/bta/bta_sdp_api.h @@ -96,6 +96,19 @@ extern "C" *******************************************************************************/ extern tBTA_SDP_STATUS BTA_SdpEnable(tBTA_SDP_DM_CBACK *p_cback); +/******************************************************************************* +** +** Function BTA_SdpDisable +** +** Description Disable the SDP search I/F service. +** Free buffer for SDP configuration structure. +** +** Returns BTA_SDP_SUCCESS if successful. +** BTA_SDP_FAIL if internal failure. +** +*******************************************************************************/ +extern tBTA_SDP_STATUS BTA_SdpDisable(void); + /******************************************************************************* ** ** Function BTA_SdpSearch diff --git a/components/bt/bluedroid/bta/jv/bta_jv_api.c b/components/bt/bluedroid/bta/jv/bta_jv_api.c index 5430d36bcb..98465701db 100644 --- a/components/bt/bluedroid/bta/jv/bta_jv_api.c +++ b/components/bt/bluedroid/bta/jv/bta_jv_api.c @@ -67,8 +67,17 @@ tBTA_JV_STATUS BTA_JvEnable(tBTA_JV_DM_CBACK *p_cback) tBTA_JV_STATUS status = BTA_JV_FAILURE; tBTA_JV_API_ENABLE *p_buf; int i; - APPL_TRACE_API( "BTA_JvEnable"); + +#if BTA_DYNAMIC_MEMORY == TRUE + /* Malloc buffer for JV configuration structure */ + p_bta_jv_cfg->p_sdp_raw_data = (UINT8 *)osi_malloc(p_bta_jv_cfg->sdp_raw_size); + p_bta_jv_cfg->p_sdp_db = (tSDP_DISCOVERY_DB *)osi_malloc(p_bta_jv_cfg->sdp_db_size); + if (p_bta_jv_cfg->p_sdp_raw_data == NULL || p_bta_jv_cfg->p_sdp_db == NULL) { + return BTA_JV_NO_DATA; + } +#endif + if (p_cback && FALSE == bta_sys_is_register(BTA_ID_JV)) { memset(&bta_jv_cb, 0, sizeof(tBTA_JV_CB)); /* set handle to invalid value by default */ @@ -110,6 +119,14 @@ void BTA_JvDisable(void) p_buf->event = BTA_JV_API_DISABLE_EVT; bta_sys_sendmsg(p_buf); } + +#if BTA_DYNAMIC_MEMORY == TRUE + /* Free buffer for JV configuration structure */ + osi_free(p_bta_jv_cfg->p_sdp_raw_data); + osi_free(p_bta_jv_cfg->p_sdp_db); + p_bta_jv_cfg->p_sdp_raw_data = NULL; + p_bta_jv_cfg->p_sdp_db = NULL; +#endif } /******************************************************************************* diff --git a/components/bt/bluedroid/bta/jv/bta_jv_cfg.c b/components/bt/bluedroid/bta/jv/bta_jv_cfg.c index f617dfc513..9020efb8ab 100644 --- a/components/bt/bluedroid/bta/jv/bta_jv_cfg.c +++ b/components/bt/bluedroid/bta/jv/bta_jv_cfg.c @@ -43,15 +43,22 @@ * between BTA_JvEnable and BTA_JvDisable * p_bta_jv_cfg->p_sdp_raw_data can be allocated before calling BTA_JvStartDiscovery * it can be de-allocated after the last call to access the database */ +#if BTA_DYNAMIC_MEMORY == FALSE static UINT8 bta_jv_sdp_raw_data[BTA_JV_SDP_RAW_DATA_SIZE]; static UINT8 __attribute__ ((aligned(4))) bta_jv_sdp_db_data[BTA_JV_SDP_DB_SIZE]; +#endif /* JV configuration structure */ -const tBTA_JV_CFG bta_jv_cfg = { +/*const */tBTA_JV_CFG bta_jv_cfg = { BTA_JV_SDP_RAW_DATA_SIZE, /* The size of p_sdp_raw_data */ BTA_JV_SDP_DB_SIZE, /* The size of p_sdp_db_data */ +#if BTA_DYNAMIC_MEMORY == FALSE bta_jv_sdp_raw_data, /* The data buffer to keep raw data */ (tSDP_DISCOVERY_DB *)bta_jv_sdp_db_data /* The data buffer to keep SDP database */ +#else + NULL, + NULL +#endif }; tBTA_JV_CFG *p_bta_jv_cfg = (tBTA_JV_CFG *) &bta_jv_cfg; diff --git a/components/bt/bluedroid/bta/jv/bta_jv_main.c b/components/bt/bluedroid/bta/jv/bta_jv_main.c index 9523d6e893..b09ebcd469 100644 --- a/components/bt/bluedroid/bta/jv/bta_jv_main.c +++ b/components/bt/bluedroid/bta/jv/bta_jv_main.c @@ -35,6 +35,8 @@ #if BTA_DYNAMIC_MEMORY == FALSE tBTA_JV_CB bta_jv_cb; +#else +tBTA_JV_CB *bta_jv_cb_ptr; #endif /* state machine action enumeration list */ diff --git a/components/bt/bluedroid/bta/sdp/bta_sdp_api.c b/components/bt/bluedroid/bta/sdp/bta_sdp_api.c index 1d027cb79e..e26f93910c 100644 --- a/components/bt/bluedroid/bta/sdp/bta_sdp_api.c +++ b/components/bt/bluedroid/bta/sdp/bta_sdp_api.c @@ -61,6 +61,15 @@ tBTA_SDP_STATUS BTA_SdpEnable(tBTA_SDP_DM_CBACK *p_cback) tBTA_SDP_API_ENABLE *p_buf; APPL_TRACE_API("%s\n", __FUNCTION__); + +#if BTA_DYNAMIC_MEMORY == TRUE + /* Malloc buffer for SDP configuration structure */ + p_bta_sdp_cfg->p_sdp_db = (tSDP_DISCOVERY_DB *)osi_malloc(p_bta_sdp_cfg->sdp_db_size); + if (p_bta_sdp_cfg->p_sdp_db == NULL) { + return BTA_SDP_FAILURE; + } +#endif + if (p_cback && FALSE == bta_sys_is_register(BTA_ID_SDP)) { memset(&bta_sdp_cb, 0, sizeof(tBTA_SDP_CB)); @@ -78,6 +87,29 @@ tBTA_SDP_STATUS BTA_SdpEnable(tBTA_SDP_DM_CBACK *p_cback) return (status); } + +/******************************************************************************* +** +** Function BTA_SdpDisable +** +** Description Disable the SDP search I/F service. +** Free buffer for SDP configuration structure. +** +** Returns BTA_SDP_SUCCESS if successful. +** BTA_SDP_FAIL if internal failure. +** +*******************************************************************************/ +tBTA_SDP_STATUS BTA_SdpDisable(void) +{ + tBTA_SDP_STATUS status = BTA_SDP_SUCCESS; +#if BTA_DYNAMIC_MEMORY == TRUE + /* Free buffer for SDP configuration structure */ + osi_free(p_bta_sdp_cfg->p_sdp_db); + p_bta_sdp_cfg->p_sdp_db = NULL; +#endif + return (status); +} + /******************************************************************************* ** ** Function BTA_SdpSearch diff --git a/components/bt/bluedroid/bta/sdp/bta_sdp_cfg.c b/components/bt/bluedroid/bta/sdp/bta_sdp_cfg.c index fff5e321a2..322b25caec 100644 --- a/components/bt/bluedroid/bta/sdp/bta_sdp_cfg.c +++ b/components/bt/bluedroid/bta/sdp/bta_sdp_cfg.c @@ -30,12 +30,18 @@ #define BTA_SDP_DB_SIZE 1500 #endif +#if BTA_DYNAMIC_MEMORY == FALSE static UINT8 __attribute__ ((aligned(4))) bta_sdp_db_data[BTA_SDP_DB_SIZE]; +#endif /* SDP configuration structure */ -const tBTA_SDP_CFG bta_sdp_cfg = { +tBTA_SDP_CFG bta_sdp_cfg = { BTA_SDP_DB_SIZE, +#if BTA_DYNAMIC_MEMORY == FALSE (tSDP_DISCOVERY_DB *)bta_sdp_db_data /* The data buffer to keep SDP database */ +#else + NULL +#endif }; tBTA_SDP_CFG *p_bta_sdp_cfg = (tBTA_SDP_CFG *) &bta_sdp_cfg; diff --git a/components/bt/bluedroid/btc/core/btc_ble_storage.c b/components/bt/bluedroid/btc/core/btc_ble_storage.c index c8590ce803..9730dc612c 100644 --- a/components/bt/bluedroid/btc/core/btc_ble_storage.c +++ b/components/bt/bluedroid/btc/core/btc_ble_storage.c @@ -774,7 +774,7 @@ static bt_status_t _btc_storage_in_fetch_bonded_ble_device(const char *remote_bd BTC_TRACE_ERROR("%s, device_type = %x", __func__, device_type); return BT_STATUS_FAIL; } - + string_to_bdaddr(remote_bd_addr, &bd_addr); bdcpy(bta_bd_addr, bd_addr.address); @@ -817,7 +817,7 @@ static bt_status_t btc_storage_in_fetch_bonded_ble_devices(int add) for (const btc_config_section_iter_t *iter = btc_config_section_begin(); iter != btc_config_section_end(); iter = btc_config_section_next(iter)) { const char *name = btc_config_section_name(iter); - + if (!string_is_bdaddr(name) || !btc_config_get_int(name, BTC_BLE_STORAGE_DEV_TYPE_STR, (int *)&device_type) || ((device_type & BT_DEVICE_TYPE_BLE) != BT_DEVICE_TYPE_BLE)) { @@ -927,4 +927,4 @@ int btc_storage_get_num_ble_bond_devices(void) return num_dev; } #endif ///SMP_INCLUDED == TRUE - + diff --git a/components/bt/bluedroid/btc/core/btc_dm.c b/components/bt/bluedroid/btc/core/btc_dm.c index f3b9caeb42..ecc8f383a6 100644 --- a/components/bt/bluedroid/btc/core/btc_dm.c +++ b/components/bt/bluedroid/btc/core/btc_dm.c @@ -41,10 +41,10 @@ /****************************************************************************** ** Static variables ******************************************************************************/ -static tBTA_SERVICE_MASK btc_enabled_services = 0; -#if (SMP_INCLUDED == TRUE) -static btc_dm_pairing_cb_t pairing_cb; -static btc_dm_local_key_cb_t ble_local_key_cb; +#if BTC_DYNAMIC_MENDRY == FALSE +btc_dm_cb_t btc_dm_cb = {0}; +#else +btc_dm_cb_t *btc_dm_cb_ptr; #endif /****************************************************************************** @@ -131,21 +131,21 @@ static void btc_disable_bluetooth_evt(void) #if (SMP_INCLUDED == TRUE) void btc_dm_load_ble_local_keys(void) { - memset(&ble_local_key_cb, 0, sizeof(btc_dm_local_key_cb_t)); + memset(&btc_dm_cb.ble_local_key_cb, 0, sizeof(btc_dm_local_key_cb_t)); - if (btc_storage_get_ble_local_key(BTC_LE_LOCAL_KEY_ER,(char*)&ble_local_key_cb.er[0], + if (btc_storage_get_ble_local_key(BTC_LE_LOCAL_KEY_ER,(char*)&btc_dm_cb.ble_local_key_cb.er[0], BT_OCTET16_LEN)== BT_STATUS_SUCCESS) { - ble_local_key_cb.is_er_rcvd = TRUE; + btc_dm_cb.ble_local_key_cb.is_er_rcvd = TRUE; BTC_TRACE_DEBUG("%s BLE ER key loaded",__func__ ); } - if ((btc_storage_get_ble_local_key(BTC_LE_LOCAL_KEY_IR,(char*)&ble_local_key_cb.id_keys.ir[0], + if ((btc_storage_get_ble_local_key(BTC_LE_LOCAL_KEY_IR,(char*)&btc_dm_cb.ble_local_key_cb.id_keys.ir[0], BT_OCTET16_LEN)== BT_STATUS_SUCCESS )&& - (btc_storage_get_ble_local_key(BTC_LE_LOCAL_KEY_IRK, (char*)&ble_local_key_cb.id_keys.irk[0], + (btc_storage_get_ble_local_key(BTC_LE_LOCAL_KEY_IRK, (char*)&btc_dm_cb.ble_local_key_cb.id_keys.irk[0], BT_OCTET16_LEN)== BT_STATUS_SUCCESS)&& - (btc_storage_get_ble_local_key(BTC_LE_LOCAL_KEY_DHK,(char*)&ble_local_key_cb.id_keys.dhk[0], + (btc_storage_get_ble_local_key(BTC_LE_LOCAL_KEY_DHK,(char*)&btc_dm_cb.ble_local_key_cb.id_keys.dhk[0], BT_OCTET16_LEN)== BT_STATUS_SUCCESS)) { - ble_local_key_cb.is_id_keys_rcvd = TRUE; + btc_dm_cb.ble_local_key_cb.is_id_keys_rcvd = TRUE; BTC_TRACE_DEBUG("%s BLE ID keys loaded", __func__); } @@ -153,15 +153,15 @@ void btc_dm_load_ble_local_keys(void) void btc_dm_get_ble_local_keys(tBTA_DM_BLE_LOCAL_KEY_MASK *p_key_mask, BT_OCTET16 er, tBTA_BLE_LOCAL_ID_KEYS *p_id_keys) { - if (ble_local_key_cb.is_er_rcvd ) { - memcpy(&er[0], &ble_local_key_cb.er[0], sizeof(BT_OCTET16)); + if (btc_dm_cb.ble_local_key_cb.is_er_rcvd ) { + memcpy(&er[0], &btc_dm_cb.ble_local_key_cb.er[0], sizeof(BT_OCTET16)); *p_key_mask |= BTA_BLE_LOCAL_KEY_TYPE_ER; } - if (ble_local_key_cb.is_id_keys_rcvd) { - memcpy(&p_id_keys->ir[0], &ble_local_key_cb.id_keys.ir[0], sizeof(BT_OCTET16)); - memcpy(&p_id_keys->irk[0], &ble_local_key_cb.id_keys.irk[0], sizeof(BT_OCTET16)); - memcpy(&p_id_keys->dhk[0], &ble_local_key_cb.id_keys.dhk[0], sizeof(BT_OCTET16)); + if (btc_dm_cb.ble_local_key_cb.is_id_keys_rcvd) { + memcpy(&p_id_keys->ir[0], &btc_dm_cb.ble_local_key_cb.id_keys.ir[0], sizeof(BT_OCTET16)); + memcpy(&p_id_keys->irk[0], &btc_dm_cb.ble_local_key_cb.id_keys.irk[0], sizeof(BT_OCTET16)); + memcpy(&p_id_keys->dhk[0], &btc_dm_cb.ble_local_key_cb.id_keys.dhk[0], sizeof(BT_OCTET16)); *p_key_mask |= BTA_BLE_LOCAL_KEY_TYPE_ID; } BTC_TRACE_DEBUG("%s *p_key_mask=0x%02x",__func__, *p_key_mask); @@ -173,7 +173,7 @@ static void btc_dm_remove_ble_bonding_keys(void) bt_bdaddr_t bd_addr; BTC_TRACE_DEBUG("%s\n",__func__); - bdcpy(bd_addr.address, pairing_cb.bd_addr); + bdcpy(bd_addr.address, btc_dm_cb.pairing_cb.bd_addr); btc_storage_remove_remote_addr_type(&bd_addr, false); btc_storage_remove_ble_dev_auth_mode(&bd_addr, false); @@ -183,64 +183,64 @@ static void btc_dm_remove_ble_bonding_keys(void) static void btc_dm_save_ble_bonding_keys(void) { - if (!(pairing_cb.ble.is_penc_key_rcvd || pairing_cb.ble.is_pid_key_rcvd || pairing_cb.ble.is_pcsrk_key_rcvd || - pairing_cb.ble.is_lenc_key_rcvd || pairing_cb.ble.is_lcsrk_key_rcvd || pairing_cb.ble.is_lidk_key_rcvd)) { + if (!(btc_dm_cb.pairing_cb.ble.is_penc_key_rcvd || btc_dm_cb.pairing_cb.ble.is_pid_key_rcvd || btc_dm_cb.pairing_cb.ble.is_pcsrk_key_rcvd || + btc_dm_cb.pairing_cb.ble.is_lenc_key_rcvd || btc_dm_cb.pairing_cb.ble.is_lcsrk_key_rcvd || btc_dm_cb.pairing_cb.ble.is_lidk_key_rcvd)) { return ; } bt_bdaddr_t bd_addr; - bdcpy(bd_addr.address, pairing_cb.bd_addr); + bdcpy(bd_addr.address, btc_dm_cb.pairing_cb.bd_addr); btc_storage_set_ble_dev_type(&bd_addr, false); - BTC_TRACE_DEBUG("%s, penc = %d, pid = %d", __func__, pairing_cb.ble.is_penc_key_rcvd, pairing_cb.ble.is_pid_key_rcvd); - if (pairing_cb.ble.is_penc_key_rcvd) { + BTC_TRACE_DEBUG("%s, penc = %d, pid = %d", __func__, btc_dm_cb.pairing_cb.ble.is_penc_key_rcvd, btc_dm_cb.pairing_cb.ble.is_pid_key_rcvd); + if (btc_dm_cb.pairing_cb.ble.is_penc_key_rcvd) { btc_storage_add_ble_bonding_key(&bd_addr, - (char *) &pairing_cb.ble.penc_key, + (char *) &btc_dm_cb.pairing_cb.ble.penc_key, BTM_LE_KEY_PENC, sizeof(tBTM_LE_PENC_KEYS)); - pairing_cb.ble.is_penc_key_rcvd = false; + btc_dm_cb.pairing_cb.ble.is_penc_key_rcvd = false; } - if (pairing_cb.ble.is_pid_key_rcvd) { + if (btc_dm_cb.pairing_cb.ble.is_pid_key_rcvd) { btc_storage_add_ble_bonding_key(&bd_addr, - (char *) &pairing_cb.ble.pid_key, + (char *) &btc_dm_cb.pairing_cb.ble.pid_key, BTM_LE_KEY_PID, sizeof(tBTM_LE_PID_KEYS)); - pairing_cb.ble.is_pid_key_rcvd = false; + btc_dm_cb.pairing_cb.ble.is_pid_key_rcvd = false; } - if (pairing_cb.ble.is_pcsrk_key_rcvd) { + if (btc_dm_cb.pairing_cb.ble.is_pcsrk_key_rcvd) { btc_storage_add_ble_bonding_key(&bd_addr, - (char *) &pairing_cb.ble.pcsrk_key, + (char *) &btc_dm_cb.pairing_cb.ble.pcsrk_key, BTM_LE_KEY_PCSRK, sizeof(tBTM_LE_PCSRK_KEYS)); - pairing_cb.ble.is_pcsrk_key_rcvd = false; + btc_dm_cb.pairing_cb.ble.is_pcsrk_key_rcvd = false; } - if (pairing_cb.ble.is_lenc_key_rcvd) { + if (btc_dm_cb.pairing_cb.ble.is_lenc_key_rcvd) { btc_storage_add_ble_bonding_key(&bd_addr, - (char *) &pairing_cb.ble.lenc_key, + (char *) &btc_dm_cb.pairing_cb.ble.lenc_key, BTM_LE_KEY_LENC, sizeof(tBTM_LE_LENC_KEYS)); - pairing_cb.ble.is_lenc_key_rcvd = false; + btc_dm_cb.pairing_cb.ble.is_lenc_key_rcvd = false; } - if (pairing_cb.ble.is_lcsrk_key_rcvd) { + if (btc_dm_cb.pairing_cb.ble.is_lcsrk_key_rcvd) { btc_storage_add_ble_bonding_key(&bd_addr, - (char *) &pairing_cb.ble.lcsrk_key, + (char *) &btc_dm_cb.pairing_cb.ble.lcsrk_key, BTM_LE_KEY_LCSRK, sizeof(tBTM_LE_LCSRK_KEYS)); - pairing_cb.ble.is_lcsrk_key_rcvd = false; + btc_dm_cb.pairing_cb.ble.is_lcsrk_key_rcvd = false; } - if (pairing_cb.ble.is_lidk_key_rcvd) { + if (btc_dm_cb.pairing_cb.ble.is_lidk_key_rcvd) { btc_storage_add_ble_bonding_key(&bd_addr, NULL, BTM_LE_KEY_LID, 0); - pairing_cb.ble.is_lidk_key_rcvd = false; + btc_dm_cb.pairing_cb.ble.is_lidk_key_rcvd = false; } } @@ -252,20 +252,19 @@ static void btc_dm_ble_auth_cmpl_evt (tBTA_DM_AUTH_CMPL *p_auth_cmpl) int addr_type; bt_bdaddr_t bdaddr; bdcpy(bdaddr.address, p_auth_cmpl->bd_addr); - bdcpy(pairing_cb.bd_addr, p_auth_cmpl->bd_addr); + bdcpy(btc_dm_cb.pairing_cb.bd_addr, p_auth_cmpl->bd_addr); if (p_auth_cmpl->success) { status = BT_STATUS_SUCCESS; BTC_TRACE_DEBUG ("%s, - p_auth_cmpl->bd_addr: %08x%04x", __func__, (p_auth_cmpl->bd_addr[0] << 24) + (p_auth_cmpl->bd_addr[1] << 16) + (p_auth_cmpl->bd_addr[2] << 8) + p_auth_cmpl->bd_addr[3], (p_auth_cmpl->bd_addr[4] << 8) + p_auth_cmpl->bd_addr[5]); - BTC_TRACE_DEBUG ("%s, - pairing_cb.bd_addr: %08x%04x", __func__, - (pairing_cb.bd_addr[0] << 24) + (pairing_cb.bd_addr[1] << 16) + (pairing_cb.bd_addr[2] << 8) + pairing_cb.bd_addr[3], - (pairing_cb.bd_addr[4] << 8) + pairing_cb.bd_addr[5]); + // Check if need to save BLE keys if((p_auth_cmpl->auth_mode & SMP_AUTH_GEN_BOND) == 0) { return; } + if (btc_storage_get_remote_addr_type(&bdaddr, &addr_type) != BT_STATUS_SUCCESS) { btc_storage_set_remote_addr_type(&bdaddr, p_auth_cmpl->addr_type, true); } @@ -487,12 +486,12 @@ static void btc_dm_sp_key_req_evt(tBTA_DM_SP_KEY_REQ *p_key_req) tBTA_SERVICE_MASK btc_get_enabled_services_mask(void) { - return btc_enabled_services; + return btc_dm_cb.btc_enabled_services; } void btc_clear_services_mask(void) { - btc_enabled_services = 0; + btc_dm_cb.btc_enabled_services = 0; } static bt_status_t btc_in_execute_service_request(tBTA_SERVICE_ID service_id, @@ -530,9 +529,9 @@ bt_status_t btc_dm_enable_service(tBTA_SERVICE_ID service_id) { tBTA_SERVICE_ID *p_id = &service_id; - btc_enabled_services |= (1 << service_id); + btc_dm_cb.btc_enabled_services |= (1 << service_id); - BTC_TRACE_DEBUG("%s: current services:0x%x", __FUNCTION__, btc_enabled_services); + BTC_TRACE_DEBUG("%s: current services:0x%x", __FUNCTION__, btc_dm_cb.btc_enabled_services); btc_dm_execute_service_request(TRUE, (char *)p_id); @@ -543,9 +542,9 @@ bt_status_t btc_dm_disable_service(tBTA_SERVICE_ID service_id) { tBTA_SERVICE_ID *p_id = &service_id; - btc_enabled_services &= (tBTA_SERVICE_MASK)(~(1 << service_id)); + btc_dm_cb.btc_enabled_services &= (tBTA_SERVICE_MASK)(~(1 << service_id)); - BTC_TRACE_DEBUG("%s: Current Services:0x%x", __FUNCTION__, btc_enabled_services); + BTC_TRACE_DEBUG("%s: Current Services:0x%x", __FUNCTION__, btc_dm_cb.btc_enabled_services); btc_dm_execute_service_request(FALSE, (char *)p_id); @@ -693,9 +692,9 @@ void btc_dm_sec_cb_handler(btc_msg_t *msg) switch (p_data->ble_key.key_type) { case BTM_LE_KEY_PENC: { BTC_TRACE_DEBUG("Rcv BTA_LE_KEY_PENC"); - pairing_cb.ble.is_penc_key_rcvd = TRUE; - pairing_cb.ble.penc_key = p_data->ble_key.p_key_value->penc_key; - memcpy(&pairing_cb.ble.penc_key, &p_data->ble_key.p_key_value->penc_key, + btc_dm_cb.pairing_cb.ble.is_penc_key_rcvd = TRUE; + btc_dm_cb.pairing_cb.ble.penc_key = p_data->ble_key.p_key_value->penc_key; + memcpy(&btc_dm_cb.pairing_cb.ble.penc_key, &p_data->ble_key.p_key_value->penc_key, sizeof(tBTM_LE_PENC_KEYS)); memcpy(¶m.ble_security.ble_key.p_key_value.penc_key, &p_data->ble_key.p_key_value->penc_key, sizeof(tBTM_LE_PENC_KEYS)); @@ -703,8 +702,8 @@ void btc_dm_sec_cb_handler(btc_msg_t *msg) } case BTM_LE_KEY_PID: { BTC_TRACE_DEBUG("Rcv BTA_LE_KEY_PID"); - pairing_cb.ble.is_pid_key_rcvd = TRUE; - memcpy(&pairing_cb.ble.pid_key, &p_data->ble_key.p_key_value->pid_key, + btc_dm_cb.pairing_cb.ble.is_pid_key_rcvd = TRUE; + memcpy(&btc_dm_cb.pairing_cb.ble.pid_key, &p_data->ble_key.p_key_value->pid_key, sizeof(tBTM_LE_PID_KEYS)); memcpy(¶m.ble_security.ble_key.p_key_value.pid_key, &p_data->ble_key.p_key_value->pid_key, sizeof(tBTM_LE_PID_KEYS)); @@ -712,8 +711,8 @@ void btc_dm_sec_cb_handler(btc_msg_t *msg) } case BTM_LE_KEY_PCSRK: { BTC_TRACE_DEBUG("Rcv BTA_LE_KEY_PCSRK"); - pairing_cb.ble.is_pcsrk_key_rcvd = TRUE; - memcpy(&pairing_cb.ble.pcsrk_key, &p_data->ble_key.p_key_value->pcsrk_key, + btc_dm_cb.pairing_cb.ble.is_pcsrk_key_rcvd = TRUE; + memcpy(&btc_dm_cb.pairing_cb.ble.pcsrk_key, &p_data->ble_key.p_key_value->pcsrk_key, sizeof(tBTM_LE_PCSRK_KEYS)); memcpy(¶m.ble_security.ble_key.p_key_value.pcsrk_key, &p_data->ble_key.p_key_value->pcsrk_key, sizeof(tBTM_LE_PCSRK_KEYS)); @@ -721,8 +720,8 @@ void btc_dm_sec_cb_handler(btc_msg_t *msg) } case BTM_LE_KEY_LENC: { BTC_TRACE_DEBUG("Rcv BTA_LE_KEY_LENC"); - pairing_cb.ble.is_lenc_key_rcvd = TRUE; - memcpy(&pairing_cb.ble.lenc_key, &p_data->ble_key.p_key_value->lenc_key, + btc_dm_cb.pairing_cb.ble.is_lenc_key_rcvd = TRUE; + memcpy(&btc_dm_cb.pairing_cb.ble.lenc_key, &p_data->ble_key.p_key_value->lenc_key, sizeof(tBTM_LE_LENC_KEYS)); memcpy(¶m.ble_security.ble_key.p_key_value.lenc_key, &p_data->ble_key.p_key_value->lenc_key, sizeof(tBTM_LE_LENC_KEYS)); @@ -730,8 +729,8 @@ void btc_dm_sec_cb_handler(btc_msg_t *msg) } case BTM_LE_KEY_LCSRK: { BTC_TRACE_DEBUG("Rcv BTA_LE_KEY_LCSRK"); - pairing_cb.ble.is_lcsrk_key_rcvd = TRUE; - memcpy(&pairing_cb.ble.lcsrk_key, &p_data->ble_key.p_key_value->lcsrk_key, + btc_dm_cb.pairing_cb.ble.is_lcsrk_key_rcvd = TRUE; + memcpy(&btc_dm_cb.pairing_cb.ble.lcsrk_key, &p_data->ble_key.p_key_value->lcsrk_key, sizeof(tBTM_LE_LCSRK_KEYS)); memcpy(¶m.ble_security.ble_key.p_key_value.lcsrk_key, &p_data->ble_key.p_key_value->lcsrk_key, sizeof(tBTM_LE_LCSRK_KEYS)); @@ -739,7 +738,7 @@ void btc_dm_sec_cb_handler(btc_msg_t *msg) } case BTM_LE_KEY_LID: { BTC_TRACE_DEBUG("Rcv BTA_LE_KEY_LID"); - pairing_cb.ble.is_lidk_key_rcvd = TRUE; + btc_dm_cb.pairing_cb.ble.is_lidk_key_rcvd = TRUE; break; } default: @@ -778,20 +777,20 @@ void btc_dm_sec_cb_handler(btc_msg_t *msg) ble_msg.act = ESP_GAP_BLE_LOCAL_IR_EVT; memcpy(¶m.ble_security.ble_id_keys, &p_data->ble_id_keys, sizeof(tBTA_BLE_LOCAL_ID_KEYS)); BTC_TRACE_DEBUG("BTA_DM_BLE_LOCAL_IR_EVT. "); - ble_local_key_cb.is_id_keys_rcvd = TRUE; - memcpy(&ble_local_key_cb.id_keys.irk[0], + btc_dm_cb.ble_local_key_cb.is_id_keys_rcvd = TRUE; + memcpy(&btc_dm_cb.ble_local_key_cb.id_keys.irk[0], &p_data->ble_id_keys.irk[0], sizeof(BT_OCTET16)); - memcpy(&ble_local_key_cb.id_keys.ir[0], + memcpy(&btc_dm_cb.ble_local_key_cb.id_keys.ir[0], &p_data->ble_id_keys.ir[0], sizeof(BT_OCTET16)); - memcpy(&ble_local_key_cb.id_keys.dhk[0], + memcpy(&btc_dm_cb.ble_local_key_cb.id_keys.dhk[0], &p_data->ble_id_keys.dhk[0], sizeof(BT_OCTET16)); - btc_storage_add_ble_local_key( (char *)&ble_local_key_cb.id_keys.irk[0], + btc_storage_add_ble_local_key( (char *)&btc_dm_cb.ble_local_key_cb.id_keys.irk[0], BTC_LE_LOCAL_KEY_IRK, BT_OCTET16_LEN); - btc_storage_add_ble_local_key( (char *)&ble_local_key_cb.id_keys.ir[0], + btc_storage_add_ble_local_key( (char *)&btc_dm_cb.ble_local_key_cb.id_keys.ir[0], BTC_LE_LOCAL_KEY_IR, BT_OCTET16_LEN); - btc_storage_add_ble_local_key( (char *)&ble_local_key_cb.id_keys.dhk[0], + btc_storage_add_ble_local_key( (char *)&btc_dm_cb.ble_local_key_cb.id_keys.dhk[0], BTC_LE_LOCAL_KEY_DHK, BT_OCTET16_LEN); break; @@ -801,9 +800,9 @@ void btc_dm_sec_cb_handler(btc_msg_t *msg) ble_msg.act = ESP_GAP_BLE_LOCAL_ER_EVT; memcpy(¶m.ble_security.ble_id_keys, &p_data->ble_id_keys, sizeof(tBTA_BLE_LOCAL_ID_KEYS)); BTC_TRACE_DEBUG("BTA_DM_BLE_LOCAL_ER_EVT. "); - ble_local_key_cb.is_er_rcvd = TRUE; - memcpy(&ble_local_key_cb.er[0], &p_data->ble_er[0], sizeof(BT_OCTET16)); - btc_storage_add_ble_local_key( (char *)&ble_local_key_cb.er[0], + btc_dm_cb.ble_local_key_cb.is_er_rcvd = TRUE; + memcpy(&btc_dm_cb.ble_local_key_cb.er[0], &p_data->ble_er[0], sizeof(BT_OCTET16)); + btc_storage_add_ble_local_key( (char *)&btc_dm_cb.ble_local_key_cb.er[0], BTC_LE_LOCAL_KEY_ER, BT_OCTET16_LEN); break; diff --git a/components/bt/bluedroid/btc/core/btc_manage.c b/components/bt/bluedroid/btc/core/btc_manage.c index 81ecad4103..08afcb7543 100644 --- a/components/bt/bluedroid/btc/core/btc_manage.c +++ b/components/bt/bluedroid/btc/core/btc_manage.c @@ -19,7 +19,11 @@ #include "esp_bt_defs.h" #include "esp_gatt_defs.h" -static void *btc_profile_cb_tab[BTC_PID_NUM] = {}; +#if BTC_DYNAMIC_MENDRY == FALSE +void *btc_profile_cb_tab[BTC_PID_NUM] = {}; +#else +void **btc_profile_cb_tab; +#endif void esp_profile_cb_reset(void) { diff --git a/components/bt/bluedroid/btc/core/btc_task.c b/components/bt/bluedroid/btc/core/btc_task.c index 8b787405bc..e77f5ac4b9 100644 --- a/components/bt/bluedroid/btc/core/btc_task.c +++ b/components/bt/bluedroid/btc/core/btc_task.c @@ -21,12 +21,14 @@ #include "common/bt_defs.h" #include "osi/allocator.h" #include "btc/btc_main.h" +#include "btc/btc_manage.h" #include "btc/btc_dev.h" #include "btc_gatts.h" #include "btc_gattc.h" #include "btc_gatt_common.h" #include "btc_gap_ble.h" #include "btc_blufi_prf.h" +#include "blufi_int.h" #include "btc/btc_dm.h" #include "btc/btc_alarm.h" #include "bta/bta_gatt_api.h" @@ -38,6 +40,7 @@ #if BTC_AV_INCLUDED #include "btc_av.h" #include "btc_avrc.h" +#include "btc_av_co.h" #endif /* #if BTC_AV_INCLUDED */ #if (BTC_SPP_INCLUDED == TRUE) #include "btc_spp.h" @@ -54,7 +57,7 @@ static osi_thread_t *btc_thread; -static btc_func_t profile_tab[BTC_PID_NUM] = { +static const btc_func_t profile_tab[BTC_PID_NUM] = { [BTC_PID_MAIN_INIT] = {btc_main_call_handler, NULL }, [BTC_PID_DEV] = {btc_dev_call_handler, NULL }, #if (GATTS_INCLUDED == TRUE) @@ -168,6 +171,94 @@ bt_status_t btc_transfer_context(btc_msg_t *msg, void *arg, int arg_len, btc_arg } +#if BTC_DYNAMIC_MENDRY +static bt_status_t btc_init_mem(void) { + if ((btc_dm_cb_ptr = (btc_dm_cb_t *)osi_malloc(sizeof(btc_dm_cb_t))) == NULL) { + return BT_STATUS_NOMEM; + } + memset((void *)btc_dm_cb_ptr, 0, sizeof(btc_dm_cb_t)); + + if ((btc_profile_cb_tab = (void **)osi_malloc(sizeof(void *) * BTC_PID_NUM)) == NULL) { + return BT_STATUS_NOMEM; + } + memset((void *)btc_profile_cb_tab, 0, sizeof(void *) * BTC_PID_NUM); + + if ((gl_bta_adv_data_ptr = (tBTA_BLE_ADV_DATA *)osi_malloc(sizeof(tBTA_BLE_ADV_DATA))) == NULL) { + return BT_STATUS_NOMEM; + } + memset((void *)gl_bta_adv_data_ptr, 0, sizeof(tBTA_BLE_ADV_DATA)); + + if ((gl_bta_scan_rsp_data_ptr = (tBTA_BLE_ADV_DATA *)osi_malloc(sizeof(tBTA_BLE_ADV_DATA))) == NULL) { + return BT_STATUS_NOMEM; + } + memset((void *)gl_bta_scan_rsp_data_ptr, 0, sizeof(tBTA_BLE_ADV_DATA)); + +#if GATTS_INCLUDED == TRUE && GATT_DYNAMIC_MEMORY == TRUE + if ((btc_creat_tab_env_ptr = (esp_btc_creat_tab_t *)osi_malloc(sizeof(esp_btc_creat_tab_t))) == NULL) { + return BT_STATUS_NOMEM; + } + memset((void *)btc_creat_tab_env_ptr, 0, sizeof(esp_btc_creat_tab_t)); + + if ((blufi_env_ptr = (tBLUFI_ENV *)osi_malloc(sizeof(tBLUFI_ENV))) == NULL) { + return BT_STATUS_NOMEM; + } + memset((void *)blufi_env_ptr, 0, sizeof(tBLUFI_ENV)); +#endif + +#if BTC_HF_CLIENT_INCLUDED == TRUE && HFP_DYNAMIC_MEMORY == TRUE + if ((hf_client_local_param_ptr = (hf_client_local_param_t *)osi_malloc(sizeof(hf_client_local_param_t))) == NULL) { + return BT_STATUS_NOMEM; + } + memset((void *)hf_client_local_param_ptr, 0, sizeof(hf_client_local_param_t)); +#endif + +#if BTC_AV_INCLUDED == TRUE && AVRC_DYNAMIC_MEMORY == TRUE + if ((btc_rc_vb_ptr = (btc_rc_cb_t *)osi_malloc(sizeof(btc_rc_cb_t))) == NULL) { + return BT_STATUS_NOMEM; + } + memset((void *)btc_rc_vb_ptr, 0, sizeof(btc_rc_cb_t)); + if ((bta_av_co_cb_ptr = (tBTA_AV_CO_CB *)osi_malloc(sizeof(tBTA_AV_CO_CB))) == NULL) { + return BT_STATUS_NOMEM; + } + memset((void *)bta_av_co_cb_ptr, 0, sizeof(tBTA_AV_CO_CB)); +#endif + + return BT_STATUS_SUCCESS; +} + +static void btc_deinit_mem(void) { + osi_free(btc_dm_cb_ptr); + btc_dm_cb_ptr = NULL; + + osi_free(btc_profile_cb_tab); + btc_profile_cb_tab = NULL; + + osi_free(gl_bta_adv_data_ptr); + gl_bta_adv_data_ptr = NULL; + + osi_free(gl_bta_scan_rsp_data_ptr); + gl_bta_scan_rsp_data_ptr = NULL; + +#if GATTS_INCLUDED == TRUE && GATT_DYNAMIC_MEMORY == TRUE + osi_free(btc_creat_tab_env_ptr); + btc_creat_tab_env_ptr = NULL; + osi_free(blufi_env_ptr); + blufi_env_ptr = NULL; +#endif + +#if BTC_HF_CLIENT_INCLUDED == TRUE && HFP_DYNAMIC_MEMORY == TRUE + osi_free(hf_client_local_param_ptr); + hf_client_local_param_ptr = NULL; +#endif + +#if BTC_AV_INCLUDED == TRUE && AVRC_DYNAMIC_MEMORY == TRUE + osi_free(btc_rc_vb_ptr); + btc_rc_vb_ptr = NULL; + osi_free(bta_av_co_cb_ptr); + bta_av_co_cb_ptr = NULL; +#endif +} +#endif int btc_init(void) { @@ -176,6 +267,12 @@ int btc_init(void) return BT_STATUS_NOMEM; } +#if BTC_DYNAMIC_MENDRY + if (btc_init_mem() != BT_STATUS_SUCCESS){ + return BT_STATUS_NOMEM; + } +#endif + btc_gap_callback_init(); #if SCAN_QUEUE_CONGEST_CHECK btc_adv_list_init(); @@ -186,8 +283,13 @@ int btc_init(void) void btc_deinit(void) { +#if BTC_DYNAMIC_MENDRY + btc_deinit_mem(); +#endif + osi_thread_free(btc_thread); btc_thread = NULL; + #if SCAN_QUEUE_CONGEST_CHECK btc_adv_list_deinit(); #endif diff --git a/components/bt/bluedroid/btc/include/btc/btc_dm.h b/components/bt/bluedroid/btc/include/btc/btc_dm.h index b6e7741ef6..1accc3970b 100644 --- a/components/bt/bluedroid/btc/include/btc/btc_dm.h +++ b/components/bt/bluedroid/btc/include/btc/btc_dm.h @@ -66,7 +66,21 @@ typedef struct btc_dm_local_key_id_t id_keys; /* ID kyes */ } btc_dm_local_key_cb_t; +typedef struct +{ + tBTA_SERVICE_MASK btc_enabled_services; +#if (SMP_INCLUDED == TRUE) + btc_dm_pairing_cb_t pairing_cb; + btc_dm_local_key_cb_t ble_local_key_cb; +#endif +} btc_dm_cb_t; +#if BTC_DYNAMIC_MENDRY == FALSE +extern btc_dm_cb_t btc_dm_cb; +#else +extern btc_dm_cb_t *btc_dm_cb_ptr; +#define btc_dm_cb (*btc_dm_cb_ptr) +#endif // void btc_dm_call_handler(btc_msg_t *msg); void btc_dm_sec_evt(tBTA_DM_SEC_EVT event, tBTA_DM_SEC *data); diff --git a/components/bt/bluedroid/btc/include/btc/btc_manage.h b/components/bt/bluedroid/btc/include/btc/btc_manage.h index 46f746e8df..bd031a0e89 100644 --- a/components/bt/bluedroid/btc/include/btc/btc_manage.h +++ b/components/bt/bluedroid/btc/include/btc/btc_manage.h @@ -19,6 +19,11 @@ #include "btc/btc_task.h" #include "esp_bt_defs.h" +#if BTC_DYNAMIC_MENDRY == FALSE +extern void *btc_profile_cb_tab[BTC_PID_NUM]; +#else +extern void **btc_profile_cb_tab; +#endif /* reset gatt callback table */ void esp_profile_cb_reset(void); diff --git a/components/bt/bluedroid/btc/profile/esp/blufi/blufi_prf.c b/components/bt/bluedroid/btc/profile/esp/blufi/blufi_prf.c index 45c220b3ba..25ebba40d8 100644 --- a/components/bt/bluedroid/btc/profile/esp/blufi/blufi_prf.c +++ b/components/bt/bluedroid/btc/profile/esp/blufi/blufi_prf.c @@ -55,13 +55,17 @@ #define BLUFI_HDL_NUM 6 +#if GATT_DYNAMIC_MEMORY == FALSE tBLUFI_ENV blufi_env; +#else +tBLUFI_ENV *blufi_env_ptr; +#endif -static /* const */ tBT_UUID blufi_srvc_uuid = {LEN_UUID_16, {BLUFI_SERVICE_UUID}}; -static /* const */ tBT_UUID blufi_char_uuid_p2e = {LEN_UUID_16, {BLUFI_CHAR_P2E_UUID}}; -static /* const */ tBT_UUID blufi_char_uuid_e2p = {LEN_UUID_16, {BLUFI_CHAR_E2P_UUID}}; -static /* const */ tBT_UUID blufi_descr_uuid_e2p = {LEN_UUID_16, {BLUFI_DESCR_E2P_UUID}}; -static /* const */ tBT_UUID blufi_app_uuid = {LEN_UUID_16, {BLUFI_APP_UUID}}; +static const tBT_UUID blufi_srvc_uuid = {LEN_UUID_16, {BLUFI_SERVICE_UUID}}; +static const tBT_UUID blufi_char_uuid_p2e = {LEN_UUID_16, {BLUFI_CHAR_P2E_UUID}}; +static const tBT_UUID blufi_char_uuid_e2p = {LEN_UUID_16, {BLUFI_CHAR_E2P_UUID}}; +static const tBT_UUID blufi_descr_uuid_e2p = {LEN_UUID_16, {BLUFI_DESCR_E2P_UUID}}; +static const tBT_UUID blufi_app_uuid = {LEN_UUID_16, {BLUFI_APP_UUID}}; // static functions declare static void blufi_profile_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data); @@ -189,7 +193,7 @@ static void blufi_profile_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data) BTA_GATTS_SendRsp(p_data->req_data.conn_id, p_data->req_data.trans_id, p_data->req_data.status, NULL); } - + if (p_data->req_data.p_data->write_req.handle == blufi_env.handle_char_p2e) { btc_blufi_recv_handler(&p_data->req_data.p_data->write_req.value[0], p_data->req_data.p_data->write_req.len); @@ -363,7 +367,7 @@ static void btc_blufi_send_notify(uint8_t *pkt, int pkt_len) UINT16 conn_id = blufi_env.conn_id; UINT16 attr_id = blufi_env.handle_char_e2p; bool rsp = false; - + BTA_GATTS_HandleValueIndication(conn_id, attr_id, pkt_len, pkt, rsp); } @@ -479,7 +483,7 @@ void btc_blufi_send_encap(uint8_t type, uint8_t *data, int total_data_len) hdr->type = type; hdr->fc |= BLUFI_FC_DIR_E2P; hdr->seq = blufi_env.send_seq++; - + if (BLUFI_TYPE_IS_CTRL(hdr->type)) { if ((blufi_env.sec_mode & BLUFI_CTRL_SEC_MODE_CHECK_MASK) && (blufi_env.cbs && blufi_env.cbs->checksum_func)) { @@ -1052,7 +1056,7 @@ void btc_blufi_call_deep_free(btc_msg_t *msg) case BTC_BLUFI_ACT_SEND_CUSTOM_DATA:{ uint8_t *data = arg->custom_data.data; if(data) { - osi_free(data); + osi_free(data); } break; } diff --git a/components/bt/bluedroid/btc/profile/esp/blufi/blufi_protocol.c b/components/bt/bluedroid/btc/profile/esp/blufi/blufi_protocol.c index b962d10051..ab81eac507 100644 --- a/components/bt/bluedroid/btc/profile/esp/blufi/blufi_protocol.c +++ b/components/bt/bluedroid/btc/profile/esp/blufi/blufi_protocol.c @@ -36,7 +36,7 @@ //#include "esp_wifi.h" #if (GATTS_INCLUDED == TRUE) -extern tBLUFI_ENV blufi_env; +// extern tBLUFI_ENV blufi_env; void btc_blufi_protocol_handler(uint8_t type, uint8_t *data, int len) { diff --git a/components/bt/bluedroid/btc/profile/esp/blufi/include/blufi_int.h b/components/bt/bluedroid/btc/profile/esp/blufi/include/blufi_int.h index 08be6703f5..cd6f5a200b 100644 --- a/components/bt/bluedroid/btc/profile/esp/blufi/include/blufi_int.h +++ b/components/bt/bluedroid/btc/profile/esp/blufi/include/blufi_int.h @@ -24,10 +24,10 @@ typedef struct { /* Protocol reference */ tGATT_IF gatt_if; UINT8 srvc_inst; - UINT16 handle_srvc; - UINT16 handle_char_p2e; - UINT16 handle_char_e2p; - UINT16 handle_descr_e2p; + UINT16 handle_srvc; + UINT16 handle_char_p2e; + UINT16 handle_char_e2p; + UINT16 handle_descr_e2p; UINT16 conn_id; BOOLEAN is_connected; BD_ADDR remote_bda; @@ -68,17 +68,24 @@ struct blufi_frag_hdr { }; typedef struct blufi_frag_hdr blufi_frag_hdr_t; -#define BLUFI_DATA_SEC_MODE_CHECK_MASK 0x01 -#define BLUFI_DATA_SEC_MODE_ENC_MASK 0x02 -#define BLUFI_CTRL_SEC_MODE_CHECK_MASK 0x10 +#if GATT_DYNAMIC_MEMORY == FALSE +extern tBLUFI_ENV blufi_env; +#else +extern tBLUFI_ENV *blufi_env_ptr; +#define blufi_env (*blufi_env_ptr) +#endif + +#define BLUFI_DATA_SEC_MODE_CHECK_MASK 0x01 +#define BLUFI_DATA_SEC_MODE_ENC_MASK 0x02 +#define BLUFI_CTRL_SEC_MODE_CHECK_MASK 0x10 #define BLUFI_CTRL_SEC_MODE_ENC_MASK 0x20 #define BLUFI_MAX_DATA_LEN 255 // packet type #define BLUFI_TYPE_MASK 0x03 -#define BLUFI_TYPE_SHIFT 0 +#define BLUFI_TYPE_SHIFT 0 #define BLUFI_SUBTYPE_MASK 0xFC -#define BLUFI_SUBTYPE_SHIFT 2 +#define BLUFI_SUBTYPE_SHIFT 2 #define BLUFI_GET_TYPE(type) ((type) & BLUFI_TYPE_MASK) #define BLUFI_GET_SUBTYPE(type) (((type) & BLUFI_SUBTYPE_MASK) >>BLUFI_SUBTYPE_SHIFT) @@ -161,12 +168,12 @@ typedef struct blufi_frag_hdr blufi_frag_hdr_t; #define BLUFI_FC_REQ_ACK 0x08 #define BLUFI_FC_FRAG 0x10 -#define BLUFI_FC_IS_ENC(fc) ((fc) & BLUFI_FC_ENC_MASK) -#define BLUFI_FC_IS_CHECK(fc) ((fc) & BLUFI_FC_CHECK_MASK) -#define BLUFI_FC_IS_DIR_P2E(fc) ((fc) & BLUFI_FC_DIR_P2E_MASK) -#define BLUFI_FC_IS_DIR_E2P(fc) (!((fc) & BLUFI_DIR_P2E_MASK)) -#define BLUFI_FC_IS_REQ_ACK(fc) ((fc) & BLUFI_FC_REQ_ACK_MASK) -#define BLUFI_FC_IS_FRAG(fc) ((fc) & BLUFI_FC_FRAG_MASK) +#define BLUFI_FC_IS_ENC(fc) ((fc) & BLUFI_FC_ENC_MASK) +#define BLUFI_FC_IS_CHECK(fc) ((fc) & BLUFI_FC_CHECK_MASK) +#define BLUFI_FC_IS_DIR_P2E(fc) ((fc) & BLUFI_FC_DIR_P2E_MASK) +#define BLUFI_FC_IS_DIR_E2P(fc) (!((fc) & BLUFI_DIR_P2E_MASK)) +#define BLUFI_FC_IS_REQ_ACK(fc) ((fc) & BLUFI_FC_REQ_ACK_MASK) +#define BLUFI_FC_IS_FRAG(fc) ((fc) & BLUFI_FC_FRAG_MASK) /* BLUFI HEADER + TOTAL(REMAIN) LENGTH + CRC + L2CAP RESERVED */ #define BLUFI_MTU_RESERVED_SIZE (sizeof(struct blufi_hdr) + 2 + 2 + 3) diff --git a/components/bt/bluedroid/btc/profile/std/a2dp/bta_av_co.c b/components/bt/bluedroid/btc/profile/std/a2dp/bta_av_co.c index 2e72bd8cba..a3250cdac5 100644 --- a/components/bt/bluedroid/btc/profile/std/a2dp/bta_av_co.c +++ b/components/bt/bluedroid/btc/profile/std/a2dp/bta_av_co.c @@ -105,58 +105,12 @@ const tA2D_SBC_CIE btc_av_sbc_default_config = { A2D_SBC_IE_MIN_BITPOOL /* min_bitpool */ }; - -/***************************************************************************** -** Local data -*****************************************************************************/ -typedef struct { - UINT8 sep_info_idx; /* local SEP index (in BTA tables) */ - UINT8 seid; /* peer SEP index (in peer tables) */ - UINT8 codec_type; /* peer SEP codec type */ - UINT8 codec_caps[AVDT_CODEC_SIZE]; /* peer SEP codec capabilities */ - UINT8 num_protect; /* peer SEP number of CP elements */ - UINT8 protect_info[BTA_AV_CP_INFO_LEN]; /* peer SEP content protection info */ -} tBTA_AV_CO_SINK; - -typedef struct { - BD_ADDR addr; /* address of audio/video peer */ - tBTA_AV_CO_SINK snks[BTC_SV_AV_AA_SEP_INDEX]; /* array of supported sinks */ - tBTA_AV_CO_SINK srcs[BTC_SV_AV_AA_SEP_INDEX]; /* array of supported srcs */ - UINT8 num_snks; /* total number of sinks at peer */ - UINT8 num_srcs; /* total number of srcs at peer */ - UINT8 num_seps; /* total number of seids at peer */ - UINT8 num_rx_snks; /* number of received sinks */ - UINT8 num_rx_srcs; /* number of received srcs */ - UINT8 num_sup_snks; /* number of supported sinks in the snks array */ - UINT8 num_sup_srcs; /* number of supported srcs in the srcs array */ - tBTA_AV_CO_SINK *p_snk; /* currently selected sink */ - tBTA_AV_CO_SINK *p_src; /* currently selected src */ - UINT8 codec_cfg[AVDT_CODEC_SIZE]; /* current codec configuration */ - BOOLEAN cp_active; /* current CP configuration */ - BOOLEAN acp; /* acceptor */ - BOOLEAN recfg_needed; /* reconfiguration is needed */ - BOOLEAN opened; /* opened */ - UINT16 mtu; /* maximum transmit unit size */ - UINT16 uuid_to_connect; /* uuid of peer device */ -} tBTA_AV_CO_PEER; - -typedef struct { - BOOLEAN active; - UINT8 flag; -} tBTA_AV_CO_CP; - -typedef struct { - /* Connected peer information */ - tBTA_AV_CO_PEER peers[BTA_AV_NUM_STRS]; - /* Current codec configuration - access to this variable must be protected */ - tBTC_AV_CODEC_INFO codec_cfg; - tBTC_AV_CODEC_INFO codec_cfg_setconfig; /* remote peer setconfig preference */ - - tBTA_AV_CO_CP cp; -} tBTA_AV_CO_CB; - /* Control block instance */ -static tBTA_AV_CO_CB bta_av_co_cb; +#if AVRC_DYNAMIC_MEMORY == FALSE +tBTA_AV_CO_CB bta_av_co_cb; +#else +tBTA_AV_CO_CB *bta_av_co_cb_ptr; +#endif static BOOLEAN bta_av_co_audio_codec_build_config(const UINT8 *p_codec_caps, UINT8 *p_codec_cfg); static void bta_av_co_audio_peer_reset_config(tBTA_AV_CO_PEER *p_peer); @@ -1735,7 +1689,7 @@ BOOLEAN bta_av_co_get_remote_bitpool_pref(UINT8 *min, UINT8 *max) } /* the call out functions for audio stream */ -tBTA_AV_CO_FUNCTS bta_av_a2d_cos = { +const tBTA_AV_CO_FUNCTS bta_av_a2d_cos = { bta_av_co_audio_init, bta_av_co_audio_disc_res, bta_av_co_audio_getconfig, diff --git a/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_sink.c b/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_sink.c index d25a1e5de5..ad1f5558b6 100644 --- a/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_sink.c +++ b/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_sink.c @@ -112,6 +112,15 @@ typedef struct { UINT32 sample_rate; } tBTC_A2DP_SINK_CB; +typedef struct { + tBTC_A2DP_SINK_CB btc_aa_snk_cb; + future_t *btc_a2dp_sink_future; + osi_thread_t *btc_aa_snk_task_hdl; + OI_CODEC_SBC_DECODER_CONTEXT context; + OI_UINT32 contextData[CODEC_DATA_WORDS(2, SBC_CODEC_FAST_FILTER_BUFFERS)]; + OI_INT16 pcmData[15 * SBC_MAX_SAMPLES_PER_FRAME * SBC_MAX_CHANNELS]; +} a2dp_sink_local_param_t; + static void btc_a2dp_sink_thread_init(UNUSED_ATTR void *context); static void btc_a2dp_sink_thread_cleanup(UNUSED_ATTR void *context); static void btc_a2dp_sink_flush_q(fixed_queue_t *p_q); @@ -128,11 +137,14 @@ static void btc_a2dp_sink_ctrl_handler(void *arg); static void btc_a2dp_sink_data_ready(void *context); -static tBTC_A2DP_SINK_CB btc_aa_snk_cb; static int btc_a2dp_sink_state = BTC_A2DP_SINK_STATE_OFF; -static future_t *btc_a2dp_sink_future = NULL; -static osi_thread_t *btc_aa_snk_task_hdl = NULL; static esp_a2d_sink_data_cb_t bt_aa_snk_data_cb = NULL; +#if A2D_DYNAMIC_MEMORY == FALSE +static a2dp_sink_local_param_t a2dp_sink_local_param; +#else +static a2dp_sink_local_param_t *a2dp_sink_local_param_ptr; +#define a2dp_sink_local_param (*a2dp_sink_local_param_ptr) +#endif ///A2D_DYNAMIC_MEMORY == FALSE void btc_a2dp_sink_reg_data_cb(esp_a2d_sink_data_cb_t callback) { @@ -148,21 +160,6 @@ static inline void btc_a2d_data_cb_to_app(const uint8_t *data, uint32_t len) } } -#define BTC_SBC_DEC_CONTEXT_DATA_LEN (CODEC_DATA_WORDS(2, SBC_CODEC_FAST_FILTER_BUFFERS)) -#define BTC_SBC_DEC_PCM_DATA_LEN (15 * SBC_MAX_SAMPLES_PER_FRAME * SBC_MAX_CHANNELS) - -#if BTC_SBC_DEC_DYNAMIC_MEMORY == FALSE -static OI_CODEC_SBC_DECODER_CONTEXT btc_sbc_decoder_context; -static OI_UINT32 btc_sbc_decoder_context_data[BTC_SBC_DEC_CONTEXT_DATA_LEN]; -static OI_INT16 btc_sbc_pcm_data[BTC_SBC_DEC_PCM_DATA_LEN]; -#else -static OI_CODEC_SBC_DECODER_CONTEXT *btc_sbc_decoder_context_ptr; -static OI_UINT32 *btc_sbc_decoder_context_data; -static OI_INT16 *btc_sbc_pcm_data; -#define btc_sbc_decoder_context (*btc_sbc_decoder_context_ptr) -#endif /* BTC_SBC_DEC_DYNAMIC_MEMORY == FALSE */ - - /***************************************************************************** ** Misc helper functions @@ -190,7 +187,7 @@ static bool btc_a2dp_sink_ctrl_post(uint32_t sig, void *param) evt->sig = sig; evt->param = param; - return osi_thread_post(btc_aa_snk_task_hdl, btc_a2dp_sink_ctrl_handler, evt, 0, OSI_THREAD_BLOCKING); + return osi_thread_post(a2dp_sink_local_param.btc_aa_snk_task_hdl, btc_a2dp_sink_ctrl_handler, evt, 0, OSI_THREAD_BLOCKING); } static void btc_a2dp_sink_ctrl_handler(void *arg) @@ -235,20 +232,18 @@ bool btc_a2dp_sink_startup(void) return false; } +#if A2D_DYNAMIC_MEMORY == TRUE + if ((a2dp_sink_local_param_ptr = (a2dp_sink_local_param_t *)osi_malloc(sizeof(a2dp_sink_local_param_t))) == NULL) { + APPL_TRACE_ERROR("%s malloc failed!", __func__); + return false; + } + memset((void *)a2dp_sink_local_param_ptr, 0, sizeof(a2dp_sink_local_param_t)); +#endif + APPL_TRACE_EVENT("## A2DP SINK START MEDIA THREAD ##"); -#if (BTC_SBC_DEC_DYNAMIC_MEMORY == TRUE) - btc_sbc_decoder_context_ptr = osi_calloc(sizeof(OI_CODEC_SBC_DECODER_CONTEXT)); - btc_sbc_decoder_context_data = osi_calloc(BTC_SBC_DEC_CONTEXT_DATA_LEN * sizeof(OI_UINT32)); - btc_sbc_pcm_data = osi_calloc(BTC_SBC_DEC_PCM_DATA_LEN * sizeof(OI_INT16)); - if (!btc_sbc_decoder_context_ptr || !btc_sbc_decoder_context_data || !btc_sbc_pcm_data) { - APPL_TRACE_ERROR("failed to allocate SBC decoder"); - goto error_exit; - } -#endif /* BTC_SBC_DEC_DYNAMIC_MEMORY == TRUE */ - - btc_aa_snk_task_hdl = osi_thread_create(BTC_A2DP_SINK_TASK_NAME, BTC_A2DP_SINK_TASK_STACK_SIZE, BTC_A2DP_SINK_TASK_PRIO, BTC_A2DP_SINK_TASK_PINNED_TO_CORE, 2); - if (btc_aa_snk_task_hdl == NULL) { + a2dp_sink_local_param.btc_aa_snk_task_hdl = osi_thread_create(BTC_A2DP_SINK_TASK_NAME, BTC_A2DP_SINK_TASK_STACK_SIZE, BTC_A2DP_SINK_TASK_PRIO, BTC_A2DP_SINK_TASK_PINNED_TO_CORE, 2); + if (a2dp_sink_local_param.btc_aa_snk_task_hdl == NULL) { goto error_exit; } @@ -262,25 +257,15 @@ bool btc_a2dp_sink_startup(void) error_exit:; APPL_TRACE_ERROR("%s unable to start up media thread\n", __func__); - if (btc_aa_snk_task_hdl != NULL) { - osi_thread_free(btc_aa_snk_task_hdl); - btc_aa_snk_task_hdl = NULL; + if (a2dp_sink_local_param.btc_aa_snk_task_hdl != NULL) { + osi_thread_free(a2dp_sink_local_param.btc_aa_snk_task_hdl); + a2dp_sink_local_param.btc_aa_snk_task_hdl = NULL; } -#if (BTC_SBC_DEC_DYNAMIC_MEMORY == TRUE) - if (btc_sbc_decoder_context_ptr) { - osi_free(btc_sbc_decoder_context_ptr); - btc_sbc_decoder_context_ptr = NULL; - } - if (btc_sbc_decoder_context_data) { - osi_free(btc_sbc_decoder_context_data); - btc_sbc_decoder_context_data = NULL; - } - if (btc_sbc_pcm_data) { - osi_free(btc_sbc_pcm_data); - btc_sbc_pcm_data = NULL; - } -#endif /* BTC_SBC_DEC_DYNAMIC_MEMORY == TRUE */ +#if A2D_DYNAMIC_MEMORY == TRUE + osi_free(a2dp_sink_local_param_ptr); + a2dp_sink_local_param_ptr = NULL; +#endif return false; } @@ -291,25 +276,19 @@ void btc_a2dp_sink_shutdown(void) // Exit thread btc_a2dp_sink_state = BTC_A2DP_SINK_STATE_SHUTTING_DOWN; - btc_a2dp_sink_future = future_new(); - assert(btc_a2dp_sink_future); + a2dp_sink_local_param.btc_a2dp_sink_future = future_new(); + assert(a2dp_sink_local_param.btc_a2dp_sink_future); btc_a2dp_sink_ctrl_post(BTC_MEDIA_TASK_SINK_CLEAN_UP, NULL); - future_await(btc_a2dp_sink_future); - btc_a2dp_sink_future = NULL; + future_await(a2dp_sink_local_param.btc_a2dp_sink_future); + a2dp_sink_local_param.btc_a2dp_sink_future = NULL; - osi_thread_free(btc_aa_snk_task_hdl); - btc_aa_snk_task_hdl = NULL; + osi_thread_free(a2dp_sink_local_param.btc_aa_snk_task_hdl); + a2dp_sink_local_param.btc_aa_snk_task_hdl = NULL; -#if (BTC_SBC_DEC_DYNAMIC_MEMORY == TRUE) - osi_free(btc_sbc_decoder_context_ptr); - btc_sbc_decoder_context_ptr = NULL; - - osi_free(btc_sbc_decoder_context_data); - btc_sbc_decoder_context_data = NULL; - - osi_free(btc_sbc_pcm_data); - btc_sbc_pcm_data = NULL; -#endif /* BTC_SBC_DEC_DYNAMIC_MEMORY == TRUE */ +#if A2D_DYNAMIC_MEMORY == TRUE + osi_free(a2dp_sink_local_param_ptr); + a2dp_sink_local_param_ptr = NULL; +#endif } /***************************************************************************** @@ -320,7 +299,7 @@ void btc_a2dp_sink_shutdown(void) void btc_a2dp_sink_on_idle(void) { - btc_aa_snk_cb.rx_flush = TRUE; + a2dp_sink_local_param.btc_aa_snk_cb.rx_flush = TRUE; btc_a2dp_sink_rx_flush_req(); btc_a2dp_sink_clear_track(); @@ -335,7 +314,7 @@ void btc_a2dp_sink_on_idle(void) void btc_a2dp_sink_on_stopped(tBTA_AV_SUSPEND *p_av) { - btc_aa_snk_cb.rx_flush = TRUE; + a2dp_sink_local_param.btc_aa_snk_cb.rx_flush = TRUE; btc_a2dp_sink_rx_flush_req(); btc_a2dp_control_set_datachnl_stat(FALSE); } @@ -348,14 +327,14 @@ void btc_a2dp_sink_on_stopped(tBTA_AV_SUSPEND *p_av) void btc_a2dp_sink_on_suspended(tBTA_AV_SUSPEND *p_av) { - btc_aa_snk_cb.rx_flush = TRUE; + a2dp_sink_local_param.btc_aa_snk_cb.rx_flush = TRUE; btc_a2dp_sink_rx_flush_req(); return; } static void btc_a2dp_sink_data_post(void) { - osi_thread_post(btc_aa_snk_task_hdl, btc_a2dp_sink_data_ready, NULL, 1, OSI_THREAD_BLOCKING); + osi_thread_post(a2dp_sink_local_param.btc_aa_snk_task_hdl, btc_a2dp_sink_data_ready, NULL, 1, OSI_THREAD_BLOCKING); } /******************************************************************************* @@ -376,7 +355,7 @@ static BOOLEAN btc_a2dp_sink_clear_track(void) void btc_a2dp_sink_set_rx_flush(BOOLEAN enable) { APPL_TRACE_EVENT("## DROP RX %d ##\n", enable); - btc_aa_snk_cb.rx_flush = enable; + a2dp_sink_local_param.btc_aa_snk_cb.rx_flush = enable; } /***************************************************************************** @@ -410,20 +389,20 @@ static void btc_a2dp_sink_data_ready(UNUSED_ATTR void *context) { tBT_SBC_HDR *p_msg; - if (fixed_queue_is_empty(btc_aa_snk_cb.RxSbcQ)) { + if (fixed_queue_is_empty(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ)) { APPL_TRACE_DEBUG(" QUE EMPTY "); } else { - if (btc_aa_snk_cb.rx_flush == TRUE) { - btc_a2dp_sink_flush_q(btc_aa_snk_cb.RxSbcQ); + if (a2dp_sink_local_param.btc_aa_snk_cb.rx_flush == TRUE) { + btc_a2dp_sink_flush_q(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ); return; } - while ((p_msg = (tBT_SBC_HDR *)fixed_queue_try_peek_first(btc_aa_snk_cb.RxSbcQ)) != NULL ) { + while ((p_msg = (tBT_SBC_HDR *)fixed_queue_try_peek_first(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ)) != NULL ) { if (btc_a2dp_sink_state != BTC_A2DP_SINK_STATE_ON){ return; } btc_a2dp_sink_handle_inc_media(p_msg); - p_msg = (tBT_SBC_HDR *)fixed_queue_try_dequeue(btc_aa_snk_cb.RxSbcQ); + p_msg = (tBT_SBC_HDR *)fixed_queue_try_dequeue(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ); if ( p_msg == NULL ) { APPL_TRACE_ERROR("Insufficient data in que "); break; @@ -463,13 +442,13 @@ static void btc_a2dp_sink_handle_decoder_reset(tBTC_MEDIA_SINK_CFG_UPDATE *p_msg return; } - btc_aa_snk_cb.sample_rate = btc_a2dp_sink_get_track_frequency(sbc_cie.samp_freq); - btc_aa_snk_cb.channel_count = btc_a2dp_sink_get_track_channel_count(sbc_cie.ch_mode); + a2dp_sink_local_param.btc_aa_snk_cb.sample_rate = btc_a2dp_sink_get_track_frequency(sbc_cie.samp_freq); + a2dp_sink_local_param.btc_aa_snk_cb.channel_count = btc_a2dp_sink_get_track_channel_count(sbc_cie.ch_mode); - btc_aa_snk_cb.rx_flush = FALSE; + a2dp_sink_local_param.btc_aa_snk_cb.rx_flush = FALSE; APPL_TRACE_EVENT("Reset to sink role"); - status = OI_CODEC_SBC_DecoderReset(&btc_sbc_decoder_context, btc_sbc_decoder_context_data, - BTC_SBC_DEC_CONTEXT_DATA_LEN * sizeof(OI_UINT32), 2, 2, FALSE, FALSE); + status = OI_CODEC_SBC_DecoderReset(&a2dp_sink_local_param.context, a2dp_sink_local_param.contextData, + sizeof(a2dp_sink_local_param.contextData), 2, 2, FALSE, FALSE); if (!OI_SUCCESS(status)) { APPL_TRACE_ERROR("OI_CODEC_SBC_DecoderReset failed with error code %d\n", status); } @@ -585,14 +564,14 @@ static void btc_a2dp_sink_handle_inc_media(tBT_SBC_HDR *p_msg) UINT8 *sbc_start_frame = ((UINT8 *)(p_msg + 1) + p_msg->offset + 1); int count; UINT32 pcmBytes, availPcmBytes; - OI_INT16 *pcmDataPointer = btc_sbc_pcm_data; /*Will be overwritten on next packet receipt*/ + OI_INT16 *pcmDataPointer = a2dp_sink_local_param.pcmData; /*Will be overwritten on next packet receipt*/ OI_STATUS status; int num_sbc_frames = p_msg->num_frames_to_be_processed; UINT32 sbc_frame_len = p_msg->len - 1; - availPcmBytes = BTC_SBC_DEC_PCM_DATA_LEN * sizeof(OI_INT16); + availPcmBytes = sizeof(a2dp_sink_local_param.pcmData); /* XXX: Check if the below check is correct, we are checking for peer to be sink when we are sink */ - if (btc_av_get_peer_sep() == AVDT_TSEP_SNK || (btc_aa_snk_cb.rx_flush)) { + if (btc_av_get_peer_sep() == AVDT_TSEP_SNK || (a2dp_sink_local_param.btc_aa_snk_cb.rx_flush)) { APPL_TRACE_DEBUG(" State Changed happened in this tick "); return; } @@ -606,7 +585,7 @@ static void btc_a2dp_sink_handle_inc_media(tBT_SBC_HDR *p_msg) for (count = 0; count < num_sbc_frames && sbc_frame_len != 0; count ++) { pcmBytes = availPcmBytes; - status = OI_CODEC_SBC_DecodeFrame(&btc_sbc_decoder_context, (const OI_BYTE **)&sbc_start_frame, + status = OI_CODEC_SBC_DecodeFrame(&a2dp_sink_local_param.context, (const OI_BYTE **)&sbc_start_frame, (OI_UINT32 *)&sbc_frame_len, (OI_INT16 *)pcmDataPointer, (OI_UINT32 *)&pcmBytes); @@ -620,7 +599,7 @@ static void btc_a2dp_sink_handle_inc_media(tBT_SBC_HDR *p_msg) p_msg->len = sbc_frame_len + 1; } - btc_a2d_data_cb_to_app((uint8_t *)btc_sbc_pcm_data, (BTC_SBC_DEC_PCM_DATA_LEN * sizeof(OI_INT16) - availPcmBytes)); + btc_a2d_data_cb_to_app((uint8_t *)a2dp_sink_local_param.pcmData, (sizeof(a2dp_sink_local_param.pcmData) - availPcmBytes)); } /******************************************************************************* @@ -634,7 +613,7 @@ static void btc_a2dp_sink_handle_inc_media(tBT_SBC_HDR *p_msg) *******************************************************************************/ BOOLEAN btc_a2dp_sink_rx_flush_req(void) { - if (fixed_queue_is_empty(btc_aa_snk_cb.RxSbcQ) == TRUE) { /* Que is already empty */ + if (fixed_queue_is_empty(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ) == TRUE) { /* Que is already empty */ return TRUE; } @@ -655,7 +634,7 @@ static void btc_a2dp_sink_rx_flush(void) /* Flush all enqueued SBC buffers (encoded) */ APPL_TRACE_DEBUG("btc_a2dp_sink_rx_flush"); - btc_a2dp_sink_flush_q(btc_aa_snk_cb.RxSbcQ); + btc_a2dp_sink_flush_q(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ); } static int btc_a2dp_sink_get_track_frequency(UINT8 frequency) @@ -711,13 +690,13 @@ UINT8 btc_a2dp_sink_enque_buf(BT_HDR *p_pkt) return 0; } - if (btc_aa_snk_cb.rx_flush == TRUE) { /* Flush enabled, do not enque*/ - return fixed_queue_length(btc_aa_snk_cb.RxSbcQ); + if (a2dp_sink_local_param.btc_aa_snk_cb.rx_flush == TRUE) { /* Flush enabled, do not enque*/ + return fixed_queue_length(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ); } - if (fixed_queue_length(btc_aa_snk_cb.RxSbcQ) >= MAX_OUTPUT_A2DP_SNK_FRAME_QUEUE_SZ) { + if (fixed_queue_length(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ) >= MAX_OUTPUT_A2DP_SNK_FRAME_QUEUE_SZ) { APPL_TRACE_WARNING("Pkt dropped\n"); - return fixed_queue_length(btc_aa_snk_cb.RxSbcQ); + return fixed_queue_length(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ); } APPL_TRACE_DEBUG("btc_a2dp_sink_enque_buf + "); @@ -728,13 +707,13 @@ UINT8 btc_a2dp_sink_enque_buf(BT_HDR *p_pkt) memcpy(p_msg, p_pkt, (sizeof(BT_HDR) + p_pkt->offset + p_pkt->len)); p_msg->num_frames_to_be_processed = (*((UINT8 *)(p_msg + 1) + p_msg->offset)) & 0x0f; APPL_TRACE_VERBOSE("btc_a2dp_sink_enque_buf %d + \n", p_msg->num_frames_to_be_processed); - fixed_queue_enqueue(btc_aa_snk_cb.RxSbcQ, p_msg); + fixed_queue_enqueue(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ, p_msg); btc_a2dp_sink_data_post(); } else { /* let caller deal with a failed allocation */ APPL_TRACE_WARNING("btc_a2dp_sink_enque_buf No Buffer left - "); } - return fixed_queue_length(btc_aa_snk_cb.RxSbcQ); + return fixed_queue_length(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ); } static void btc_a2dp_sink_handle_clear_track (void) @@ -761,11 +740,11 @@ static void btc_a2dp_sink_flush_q(fixed_queue_t *p_q) static void btc_a2dp_sink_thread_init(UNUSED_ATTR void *context) { APPL_TRACE_EVENT("%s\n", __func__); - memset(&btc_aa_snk_cb, 0, sizeof(btc_aa_snk_cb)); + memset(&a2dp_sink_local_param.btc_aa_snk_cb, 0, sizeof(a2dp_sink_local_param.btc_aa_snk_cb)); btc_a2dp_sink_state = BTC_A2DP_SINK_STATE_ON; - btc_aa_snk_cb.RxSbcQ = fixed_queue_new(QUEUE_SIZE_MAX); + a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ = fixed_queue_new(QUEUE_SIZE_MAX); btc_a2dp_control_init(); } @@ -778,9 +757,9 @@ static void btc_a2dp_sink_thread_cleanup(UNUSED_ATTR void *context) btc_a2dp_control_cleanup(); - fixed_queue_free(btc_aa_snk_cb.RxSbcQ, osi_free_func); + fixed_queue_free(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ, osi_free_func); - future_ready(btc_a2dp_sink_future, NULL); + future_ready(a2dp_sink_local_param.btc_a2dp_sink_future, NULL); } #endif /* BTC_AV_SINK_INCLUDED */ diff --git a/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_source.c b/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_source.c index d6f12b69f1..430d6f5fff 100644 --- a/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_source.c +++ b/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_source.c @@ -165,9 +165,17 @@ typedef struct { tBTC_AV_FEEDING_MODE feeding_mode; tBTC_AV_MEDIA_FEEDINGS_STATE media_feeding_state; tBTC_AV_MEDIA_FEEDINGS media_feeding; + SBC_ENC_PARAMS encoder; osi_alarm_t *media_alarm; } tBTC_A2DP_SOURCE_CB; +typedef struct { + tBTC_A2DP_SOURCE_CB btc_aa_src_cb; + future_t *btc_a2dp_source_future; + osi_thread_t *btc_aa_src_task_hdl; + UINT64 last_frame_us; +} a2dp_source_local_param_t; + static void btc_a2dp_source_thread_init(UNUSED_ATTR void *context); static void btc_a2dp_source_thread_cleanup(UNUSED_ATTR void *context); static void btc_a2dp_source_flush_q(fixed_queue_t *p_q); @@ -185,19 +193,14 @@ static void btc_a2dp_source_handle_timer(UNUSED_ATTR void *context); static void btc_a2dp_source_encoder_init(void); static void btc_a2dp_source_ctrl_handler(void *arg); -static tBTC_A2DP_SOURCE_CB btc_aa_src_cb; static int btc_a2dp_source_state = BTC_A2DP_SOURCE_STATE_OFF; -static future_t *btc_a2dp_source_future = NULL; -static osi_thread_t *btc_aa_src_task_hdl = NULL; static esp_a2d_source_data_cb_t btc_aa_src_data_cb = NULL; -static UINT64 last_frame_us = 0; - -#if BTC_SBC_ENC_DYNAMIC_MEMORY == FALSE -static SBC_ENC_PARAMS btc_sbc_encoder; +#if A2D_DYNAMIC_MEMORY == FALSE +static a2dp_source_local_param_t a2dp_source_local_param; #else -static SBC_ENC_PARAMS *btc_sbc_encoder_ptr; -#define btc_sbc_encoder (*btc_sbc_encoder_ptr) -#endif /* BTC_SBC_ENC_DYNAMIC_MEMORY == FALSE */ +static a2dp_source_local_param_t *a2dp_source_local_param_ptr; +#define a2dp_source_local_param (*a2dp_source_local_param_ptr) +#endif ///A2D_DYNAMIC_MEMORY == FALSE void btc_a2dp_src_reg_data_cb(esp_a2d_source_data_cb_t callback) { @@ -232,7 +235,7 @@ static inline void btc_aa_cb_to_app(esp_a2d_cb_event_t event, esp_a2d_cb_param_t bool btc_a2dp_source_is_streaming(void) { - return btc_aa_src_cb.is_tx_timer == TRUE; + return a2dp_source_local_param.btc_aa_src_cb.is_tx_timer == TRUE; } bool btc_a2dp_source_is_task_shutting_down(void) @@ -251,7 +254,7 @@ static void btc_a2dp_source_ctrl_post(uint32_t sig, void *param) evt->sig = sig; evt->param = param; - osi_thread_post(btc_aa_src_task_hdl, btc_a2dp_source_ctrl_handler, evt, 0, OSI_THREAD_BLOCKING); + osi_thread_post(a2dp_source_local_param.btc_aa_src_task_hdl, btc_a2dp_source_ctrl_handler, evt, 0, OSI_THREAD_BLOCKING); } static void btc_a2dp_source_ctrl_handler(void *arg) @@ -305,18 +308,18 @@ bool btc_a2dp_source_startup(void) return false; } +#if A2D_DYNAMIC_MEMORY == TRUE + if ((a2dp_source_local_param_ptr = (a2dp_source_local_param_t *)osi_malloc(sizeof(a2dp_source_local_param_t))) == NULL) { + APPL_TRACE_ERROR("%s malloc failed!", __func__); + return false; + } + memset((void *)a2dp_source_local_param_ptr, 0, sizeof(a2dp_source_local_param_t)); +#endif + APPL_TRACE_EVENT("## A2DP SOURCE START MEDIA THREAD ##"); -#if BTC_SBC_ENC_DYNAMIC_MEMORY == TRUE - btc_sbc_encoder_ptr = osi_calloc(sizeof(SBC_ENC_PARAMS)); - if (!btc_sbc_encoder_ptr) { - APPL_TRACE_ERROR("failed to allocate SBC encoder"); - goto error_exit; - } -#endif /* #if BTC_SBC_ENC_DYNAMIC_MEMORY == TRUE */ - - btc_aa_src_task_hdl = osi_thread_create(BTC_A2DP_SOURCE_TASK_NAME, BTC_A2DP_SOURCE_TASK_STACK_SIZE, BTC_A2DP_SOURCE_TASK_PRIO, BTC_A2DP_SOURCE_TASK_PINNED_TO_CORE, 2); - if (btc_aa_src_task_hdl == NULL) { + a2dp_source_local_param.btc_aa_src_task_hdl = osi_thread_create(BTC_A2DP_SOURCE_TASK_NAME, BTC_A2DP_SOURCE_TASK_STACK_SIZE, BTC_A2DP_SOURCE_TASK_PRIO, BTC_A2DP_SOURCE_TASK_PINNED_TO_CORE, 2); + if (a2dp_source_local_param.btc_aa_src_task_hdl == NULL) { goto error_exit; } @@ -327,15 +330,13 @@ bool btc_a2dp_source_startup(void) error_exit:; APPL_TRACE_ERROR("%s unable to start up media thread\n", __func__); - osi_thread_free(btc_aa_src_task_hdl); - btc_aa_src_task_hdl = NULL; + osi_thread_free(a2dp_source_local_param.btc_aa_src_task_hdl); + a2dp_source_local_param.btc_aa_src_task_hdl = NULL; -#if (BTC_SBC_ENC_DYNAMIC_MEMORY == TRUE) - if (btc_sbc_encoder_ptr) { - osi_free(btc_sbc_encoder_ptr); - btc_sbc_encoder_ptr = NULL; - } -#endif /* #if BTC_SBC_ENC_DYNAMIC_MEMORY == TRUE */ +#if A2D_DYNAMIC_MEMORY == TRUE + osi_free(a2dp_source_local_param_ptr); + a2dp_source_local_param_ptr = NULL; +#endif return false; } @@ -346,19 +347,19 @@ void btc_a2dp_source_shutdown(void) // Exit thread btc_a2dp_source_state = BTC_A2DP_SOURCE_STATE_SHUTTING_DOWN; - btc_a2dp_source_future = future_new(); - assert(btc_a2dp_source_future); + a2dp_source_local_param.btc_a2dp_source_future = future_new(); + assert(a2dp_source_local_param.btc_a2dp_source_future); btc_a2dp_source_ctrl_post(BTC_MEDIA_TASK_CLEAN_UP, NULL); - future_await(btc_a2dp_source_future); - btc_a2dp_source_future = NULL; + future_await(a2dp_source_local_param.btc_a2dp_source_future); + a2dp_source_local_param.btc_a2dp_source_future = NULL; - osi_thread_free(btc_aa_src_task_hdl); - btc_aa_src_task_hdl = NULL; + osi_thread_free(a2dp_source_local_param.btc_aa_src_task_hdl); + a2dp_source_local_param.btc_aa_src_task_hdl = NULL; -#if (BTC_SBC_ENC_DYNAMIC_MEMORY == TRUE) - osi_free(btc_sbc_encoder_ptr); - btc_sbc_encoder_ptr = NULL; -#endif /* #if BTC_SBC_ENC_DYNAMIC_MEMORY == TRUE */ +#if A2D_DYNAMIC_MEMORY == TRUE + osi_free(a2dp_source_local_param_ptr); + a2dp_source_local_param_ptr = NULL; +#endif } /***************************************************************************** @@ -391,7 +392,7 @@ void btc_a2dp_source_on_stopped(tBTA_AV_SUSPEND *p_av) } /* ensure tx frames are immediately suspended */ - btc_aa_src_cb.tx_flush = 1; + a2dp_source_local_param.btc_aa_src_cb.tx_flush = 1; /* request to stop media task */ btc_a2dp_source_tx_flush_req(); @@ -419,7 +420,7 @@ void btc_a2dp_source_on_suspended(tBTA_AV_SUSPEND *p_av) /* once stream is fully stopped we will ack back */ /* ensure tx frames are immediately flushed */ - btc_aa_src_cb.tx_flush = 1; + a2dp_source_local_param.btc_aa_src_cb.tx_flush = 1; /* stop timer tick */ btc_a2dp_source_stop_audio_req(); @@ -427,7 +428,7 @@ void btc_a2dp_source_on_suspended(tBTA_AV_SUSPEND *p_av) static void btc_a2dp_source_data_post(void) { - osi_thread_post(btc_aa_src_task_hdl, btc_a2dp_source_handle_timer, NULL, 1, OSI_THREAD_BLOCKING); + osi_thread_post(a2dp_source_local_param.btc_aa_src_task_hdl, btc_a2dp_source_handle_timer, NULL, 1, OSI_THREAD_BLOCKING); } static UINT64 time_now_us() @@ -448,7 +449,7 @@ static void log_tstamps_us(char *comment) static UINT64 prev_us = 0; UINT64 now_us = time_now_us(); APPL_TRACE_DEBUG("[%s] ts %08llu, diff : %08llu, queue sz %d", comment, now_us, now_us - prev_us, - fixed_queue_length(btc_aa_src_cb.TxAaQ)); + fixed_queue_length(a2dp_source_local_param.btc_aa_src_cb.TxAaQ)); prev_us = now_us; UNUSED(prev_us); } @@ -457,7 +458,7 @@ static void log_tstamps_us(char *comment) void btc_a2dp_source_set_tx_flush(BOOLEAN enable) { APPL_TRACE_EVENT("## DROP TX %d ##", enable); - btc_aa_src_cb.tx_flush = enable; + a2dp_source_local_param.btc_aa_src_cb.tx_flush = enable; } /***************************************************************************** @@ -516,7 +517,7 @@ BT_HDR *btc_a2dp_source_audio_readbuf(void) if (btc_a2dp_source_state != BTC_A2DP_SOURCE_STATE_ON){ return NULL; } - return fixed_queue_try_dequeue(btc_aa_src_cb.TxAaQ); + return fixed_queue_try_dequeue(a2dp_source_local_param.btc_aa_src_cb.TxAaQ); } /******************************************************************************* @@ -776,35 +777,35 @@ static void btc_a2dp_source_enc_init(BT_HDR *p_msg) APPL_TRACE_DEBUG("btc_a2dp_source_enc_init"); - btc_aa_src_cb.timestamp = 0; + a2dp_source_local_param.btc_aa_src_cb.timestamp = 0; /* SBC encoder config (enforced even if not used) */ - btc_sbc_encoder.sbc_mode = SBC_MODE_STD; - btc_sbc_encoder.s16ChannelMode = pInitAudio->ChannelMode; - btc_sbc_encoder.s16NumOfSubBands = pInitAudio->NumOfSubBands; - btc_sbc_encoder.s16NumOfBlocks = pInitAudio->NumOfBlocks; - btc_sbc_encoder.s16AllocationMethod = pInitAudio->AllocationMethod; - btc_sbc_encoder.s16SamplingFreq = pInitAudio->SamplingFreq; + a2dp_source_local_param.btc_aa_src_cb.encoder.sbc_mode = SBC_MODE_STD; + a2dp_source_local_param.btc_aa_src_cb.encoder.s16ChannelMode = pInitAudio->ChannelMode; + a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfSubBands = pInitAudio->NumOfSubBands; + a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfBlocks = pInitAudio->NumOfBlocks; + a2dp_source_local_param.btc_aa_src_cb.encoder.s16AllocationMethod = pInitAudio->AllocationMethod; + a2dp_source_local_param.btc_aa_src_cb.encoder.s16SamplingFreq = pInitAudio->SamplingFreq; - btc_sbc_encoder.u16BitRate = btc_a2dp_source_get_sbc_rate(); + a2dp_source_local_param.btc_aa_src_cb.encoder.u16BitRate = btc_a2dp_source_get_sbc_rate(); /* Default transcoding is PCM to SBC, modified by feeding configuration */ - btc_aa_src_cb.TxTranscoding = BTC_MEDIA_TRSCD_PCM_2_SBC; - btc_aa_src_cb.TxAaMtuSize = ((BTC_MEDIA_AA_BUF_SIZE - BTC_MEDIA_AA_SBC_OFFSET - sizeof(BT_HDR)) + a2dp_source_local_param.btc_aa_src_cb.TxTranscoding = BTC_MEDIA_TRSCD_PCM_2_SBC; + a2dp_source_local_param.btc_aa_src_cb.TxAaMtuSize = ((BTC_MEDIA_AA_BUF_SIZE - BTC_MEDIA_AA_SBC_OFFSET - sizeof(BT_HDR)) < pInitAudio->MtuSize) ? (BTC_MEDIA_AA_BUF_SIZE - BTC_MEDIA_AA_SBC_OFFSET - sizeof(BT_HDR)) : pInitAudio->MtuSize; APPL_TRACE_EVENT("btc_a2dp_source_enc_init mtu %d, peer mtu %d", - btc_aa_src_cb.TxAaMtuSize, pInitAudio->MtuSize); + a2dp_source_local_param.btc_aa_src_cb.TxAaMtuSize, pInitAudio->MtuSize); APPL_TRACE_EVENT(" ch mode %d, subnd %d, nb blk %d, alloc %d, rate %d, freq %d", - btc_sbc_encoder.s16ChannelMode, btc_sbc_encoder.s16NumOfSubBands, - btc_sbc_encoder.s16NumOfBlocks, - btc_sbc_encoder.s16AllocationMethod, btc_sbc_encoder.u16BitRate, - btc_sbc_encoder.s16SamplingFreq); + a2dp_source_local_param.btc_aa_src_cb.encoder.s16ChannelMode, a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfSubBands, + a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfBlocks, + a2dp_source_local_param.btc_aa_src_cb.encoder.s16AllocationMethod, a2dp_source_local_param.btc_aa_src_cb.encoder.u16BitRate, + a2dp_source_local_param.btc_aa_src_cb.encoder.s16SamplingFreq); /* Reset entirely the SBC encoder */ - SBC_Encoder_Init(&(btc_sbc_encoder)); - APPL_TRACE_DEBUG("btc_a2dp_source_enc_init bit pool %d", btc_sbc_encoder.s16BitPool); + SBC_Encoder_Init(&(a2dp_source_local_param.btc_aa_src_cb.encoder)); + APPL_TRACE_DEBUG("btc_a2dp_source_enc_init bit pool %d", a2dp_source_local_param.btc_aa_src_cb.encoder.s16BitPool); } @@ -821,7 +822,7 @@ static void btc_a2dp_source_enc_init(BT_HDR *p_msg) static void btc_a2dp_source_enc_update(BT_HDR *p_msg) { tBTC_MEDIA_UPDATE_AUDIO *pUpdateAudio = (tBTC_MEDIA_UPDATE_AUDIO *) p_msg; - SBC_ENC_PARAMS *pstrEncParams = &btc_sbc_encoder; + SBC_ENC_PARAMS *pstrEncParams = &a2dp_source_local_param.btc_aa_src_cb.encoder; UINT16 s16SamplingFreq; SINT16 s16BitPool = 0; SINT16 s16BitRate; @@ -832,9 +833,9 @@ static void btc_a2dp_source_enc_update(BT_HDR *p_msg) pUpdateAudio->MinMtuSize, pUpdateAudio->MaxBitPool, pUpdateAudio->MinBitPool); /* Only update the bitrate and MTU size while timer is running to make sure it has been initialized */ - //if (btc_aa_src_cb.is_tx_timer) + //if (a2dp_source_local_param.btc_aa_src_cb.is_tx_timer) { - btc_aa_src_cb.TxAaMtuSize = ((BTC_MEDIA_AA_BUF_SIZE - + a2dp_source_local_param.btc_aa_src_cb.TxAaMtuSize = ((BTC_MEDIA_AA_BUF_SIZE - BTC_MEDIA_AA_SBC_OFFSET - sizeof(BT_HDR)) < pUpdateAudio->MinMtuSize) ? (BTC_MEDIA_AA_BUF_SIZE - BTC_MEDIA_AA_SBC_OFFSET - sizeof(BT_HDR)) : pUpdateAudio->MinMtuSize; @@ -913,19 +914,19 @@ static void btc_a2dp_source_enc_update(BT_HDR *p_msg) if (s16BitPool > pUpdateAudio->MaxBitPool) { APPL_TRACE_DEBUG("%s computed bitpool too large (%d)", __FUNCTION__, s16BitPool); /* Decrease bitrate */ - btc_sbc_encoder.u16BitRate -= BTC_MEDIA_BITRATE_STEP; + a2dp_source_local_param.btc_aa_src_cb.encoder.u16BitRate -= BTC_MEDIA_BITRATE_STEP; /* Record that we have decreased the bitrate */ protect |= 1; } else if (s16BitPool < pUpdateAudio->MinBitPool) { APPL_TRACE_WARNING("%s computed bitpool too small (%d)", __FUNCTION__, s16BitPool); /* Increase bitrate */ - UINT16 previous_u16BitRate = btc_sbc_encoder.u16BitRate; - btc_sbc_encoder.u16BitRate += BTC_MEDIA_BITRATE_STEP; + UINT16 previous_u16BitRate = a2dp_source_local_param.btc_aa_src_cb.encoder.u16BitRate; + a2dp_source_local_param.btc_aa_src_cb.encoder.u16BitRate += BTC_MEDIA_BITRATE_STEP; /* Record that we have increased the bitrate */ protect |= 2; /* Check over-flow */ - if (btc_sbc_encoder.u16BitRate < previous_u16BitRate) { + if (a2dp_source_local_param.btc_aa_src_cb.encoder.u16BitRate < previous_u16BitRate) { protect |= 3; } } else { @@ -942,10 +943,10 @@ static void btc_a2dp_source_enc_update(BT_HDR *p_msg) pstrEncParams->s16BitPool = s16BitPool; APPL_TRACE_DEBUG("%s final bit rate %d, final bit pool %d", __FUNCTION__, - btc_sbc_encoder.u16BitRate, btc_sbc_encoder.s16BitPool); + a2dp_source_local_param.btc_aa_src_cb.encoder.u16BitRate, a2dp_source_local_param.btc_aa_src_cb.encoder.s16BitPool); /* make sure we reinitialize encoder with new settings */ - SBC_Encoder_Init(&(btc_sbc_encoder)); + SBC_Encoder_Init(&(a2dp_source_local_param.btc_aa_src_cb.encoder)); } } @@ -976,10 +977,10 @@ static void btc_a2dp_source_pcm2sbc_init(tBTC_MEDIA_INIT_AUDIO_FEEDING *p_feedin case 32000: case 48000: /* For these sampling_freq the AV connection must be 48000 */ - if (btc_sbc_encoder.s16SamplingFreq != SBC_sf48000) { + if (a2dp_source_local_param.btc_aa_src_cb.encoder.s16SamplingFreq != SBC_sf48000) { /* Reconfiguration needed at 48000 */ APPL_TRACE_DEBUG("SBC Reconfiguration needed at 48000"); - btc_sbc_encoder.s16SamplingFreq = SBC_sf48000; + a2dp_source_local_param.btc_aa_src_cb.encoder.s16SamplingFreq = SBC_sf48000; reconfig_needed = TRUE; } break; @@ -988,10 +989,10 @@ static void btc_a2dp_source_pcm2sbc_init(tBTC_MEDIA_INIT_AUDIO_FEEDING *p_feedin case 22050: case 44100: /* For these sampling_freq the AV connection must be 44100 */ - if (btc_sbc_encoder.s16SamplingFreq != SBC_sf44100) { + if (a2dp_source_local_param.btc_aa_src_cb.encoder.s16SamplingFreq != SBC_sf44100) { /* Reconfiguration needed at 44100 */ APPL_TRACE_DEBUG("SBC Reconfiguration needed at 44100"); - btc_sbc_encoder.s16SamplingFreq = SBC_sf44100; + a2dp_source_local_param.btc_aa_src_cb.encoder.s16SamplingFreq = SBC_sf44100; reconfig_needed = TRUE; } break; @@ -1001,21 +1002,21 @@ static void btc_a2dp_source_pcm2sbc_init(tBTC_MEDIA_INIT_AUDIO_FEEDING *p_feedin } /* Some AV Headsets do not support Mono => always ask for Stereo */ - if (btc_sbc_encoder.s16ChannelMode == SBC_MONO) { + if (a2dp_source_local_param.btc_aa_src_cb.encoder.s16ChannelMode == SBC_MONO) { APPL_TRACE_DEBUG("SBC Reconfiguration needed in Stereo"); - btc_sbc_encoder.s16ChannelMode = SBC_JOINT_STEREO; + a2dp_source_local_param.btc_aa_src_cb.encoder.s16ChannelMode = SBC_JOINT_STEREO; reconfig_needed = TRUE; } if (reconfig_needed != FALSE) { - APPL_TRACE_DEBUG("%s :: mtu %d", __FUNCTION__, btc_aa_src_cb.TxAaMtuSize); + APPL_TRACE_DEBUG("%s :: mtu %d", __FUNCTION__, a2dp_source_local_param.btc_aa_src_cb.TxAaMtuSize); APPL_TRACE_DEBUG("ch mode %d, nbsubd %d, nb %d, alloc %d, rate %d, freq %d", - btc_sbc_encoder.s16ChannelMode, - btc_sbc_encoder.s16NumOfSubBands, btc_sbc_encoder.s16NumOfBlocks, - btc_sbc_encoder.s16AllocationMethod, btc_sbc_encoder.u16BitRate, - btc_sbc_encoder.s16SamplingFreq); + a2dp_source_local_param.btc_aa_src_cb.encoder.s16ChannelMode, + a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfSubBands, a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfBlocks, + a2dp_source_local_param.btc_aa_src_cb.encoder.s16AllocationMethod, a2dp_source_local_param.btc_aa_src_cb.encoder.u16BitRate, + a2dp_source_local_param.btc_aa_src_cb.encoder.s16SamplingFreq); - SBC_Encoder_Init(&(btc_sbc_encoder)); + SBC_Encoder_Init(&(a2dp_source_local_param.btc_aa_src_cb.encoder)); } else { APPL_TRACE_DEBUG("%s no SBC reconfig needed", __FUNCTION__); } @@ -1037,13 +1038,13 @@ static void btc_a2dp_source_audio_feeding_init(BT_HDR *p_msg) APPL_TRACE_DEBUG("%s format:%d", __FUNCTION__, p_feeding->feeding.format); /* Save Media Feeding information */ - btc_aa_src_cb.feeding_mode = p_feeding->feeding_mode; - btc_aa_src_cb.media_feeding = p_feeding->feeding; + a2dp_source_local_param.btc_aa_src_cb.feeding_mode = p_feeding->feeding_mode; + a2dp_source_local_param.btc_aa_src_cb.media_feeding = p_feeding->feeding; /* Handle different feeding formats */ switch (p_feeding->feeding.format) { case BTC_AV_CODEC_PCM: - btc_aa_src_cb.TxTranscoding = BTC_MEDIA_TRSCD_PCM_2_SBC; + a2dp_source_local_param.btc_aa_src_cb.TxTranscoding = BTC_MEDIA_TRSCD_PCM_2_SBC; btc_a2dp_source_pcm2sbc_init(p_feeding); break; @@ -1067,10 +1068,10 @@ static void btc_a2dp_source_aa_tx_flush(void) /* Flush all enqueued music buffers (encoded) */ APPL_TRACE_DEBUG("%s", __FUNCTION__); - btc_aa_src_cb.media_feeding_state.pcm.counter = 0; - btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue = 0; + a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.counter = 0; + a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue = 0; - btc_a2dp_source_flush_q(btc_aa_src_cb.TxAaQ); + btc_a2dp_source_flush_q(a2dp_source_local_param.btc_aa_src_cb.TxAaQ); btc_aa_src_data_read(NULL, -1); } @@ -1088,35 +1089,35 @@ static UINT8 btc_get_num_aa_frame(void) { UINT8 result = 0; - switch (btc_aa_src_cb.TxTranscoding) { + switch (a2dp_source_local_param.btc_aa_src_cb.TxTranscoding) { case BTC_MEDIA_TRSCD_PCM_2_SBC: { - UINT32 pcm_bytes_per_frame = btc_sbc_encoder.s16NumOfSubBands * - btc_sbc_encoder.s16NumOfBlocks * - btc_aa_src_cb.media_feeding.cfg.pcm.num_channel * - btc_aa_src_cb.media_feeding.cfg.pcm.bit_per_sample / 8; + UINT32 pcm_bytes_per_frame = a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfSubBands * + a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfBlocks * + a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.num_channel * + a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.bit_per_sample / 8; UINT32 us_this_tick = BTC_MEDIA_TIME_TICK_MS * 1000; UINT64 now_us = time_now_us(); - if (last_frame_us != 0) { + if (a2dp_source_local_param.last_frame_us != 0) { #if _POSIX_TIMERS - us_this_tick = (now_us - last_frame_us); + us_this_tick = (now_us - a2dp_source_local_param.last_frame_us); #else // consider the case that the number of day increases and timeofday wraps around - us_this_tick = (now_us > last_frame_us) ? (now_us - last_frame_us) : - (now_us + 86400000000ull - last_frame_us); + us_this_tick = (now_us > a2dp_source_local_param.last_frame_us) ? (now_us - a2dp_source_local_param.last_frame_us) : + (now_us + 86400000000ull - a2dp_source_local_param.last_frame_us); #endif } - last_frame_us = now_us; + a2dp_source_local_param.last_frame_us = now_us; - btc_aa_src_cb.media_feeding_state.pcm.counter += - btc_aa_src_cb.media_feeding_state.pcm.bytes_per_tick * + a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.counter += + a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.bytes_per_tick * us_this_tick / (BTC_MEDIA_TIME_TICK_MS * 1000); /* calculate nbr of frames pending for this media tick */ - result = btc_aa_src_cb.media_feeding_state.pcm.counter / pcm_bytes_per_frame; + result = a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.counter / pcm_bytes_per_frame; /* limit the frames to be sent */ - UINT32 frm_nb_threshold = MAX_OUTPUT_A2DP_SRC_FRAME_QUEUE_SZ - fixed_queue_length(btc_aa_src_cb.TxAaQ); + UINT32 frm_nb_threshold = MAX_OUTPUT_A2DP_SRC_FRAME_QUEUE_SZ - fixed_queue_length(a2dp_source_local_param.btc_aa_src_cb.TxAaQ); if (frm_nb_threshold > MAX_PCM_FRAME_NUM_PER_TICK) { frm_nb_threshold = MAX_PCM_FRAME_NUM_PER_TICK; } @@ -1125,7 +1126,7 @@ static UINT8 btc_get_num_aa_frame(void) APPL_TRACE_EVENT("Limit frms to send from %d to %d", result, frm_nb_threshold); result = frm_nb_threshold; } - btc_aa_src_cb.media_feeding_state.pcm.counter -= result * pcm_bytes_per_frame; + a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.counter -= result * pcm_bytes_per_frame; BTC_TRACE_VERBOSE("WRITE %d FRAMES", result); } @@ -1133,7 +1134,7 @@ static UINT8 btc_get_num_aa_frame(void) default: APPL_TRACE_ERROR("ERROR btc_get_num_aa_frame Unsupported transcoding format 0x%x", - btc_aa_src_cb.TxTranscoding); + a2dp_source_local_param.btc_aa_src_cb.TxTranscoding); result = 0; break; } @@ -1153,13 +1154,13 @@ static UINT8 btc_get_num_aa_frame(void) BOOLEAN btc_media_aa_read_feeding(void) { - UINT16 blocm_x_subband = btc_sbc_encoder.s16NumOfSubBands * \ - btc_sbc_encoder.s16NumOfBlocks; + UINT16 blocm_x_subband = a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfSubBands * \ + a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfBlocks; UINT32 read_size; UINT16 sbc_sampling = 48000; UINT32 src_samples; - UINT16 bytes_needed = blocm_x_subband * btc_sbc_encoder.s16NumOfChannels * \ - btc_aa_src_cb.media_feeding.cfg.pcm.bit_per_sample / 8; + UINT16 bytes_needed = blocm_x_subband * a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfChannels * \ + a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.bit_per_sample / 8; static UINT16 up_sampled_buffer[SBC_MAX_NUM_FRAME * SBC_MAX_NUM_OF_BLOCKS * SBC_MAX_NUM_OF_CHANNELS * SBC_MAX_NUM_OF_SUBBANDS * 2]; static UINT16 read_buffer[SBC_MAX_NUM_FRAME * SBC_MAX_NUM_OF_BLOCKS @@ -1172,7 +1173,7 @@ BOOLEAN btc_media_aa_read_feeding(void) UINT32 nb_byte_read = 0; /* Get the SBC sampling rate */ - switch (btc_sbc_encoder.s16SamplingFreq) { + switch (a2dp_source_local_param.btc_aa_src_cb.encoder.s16SamplingFreq) { case SBC_sf48000: sbc_sampling = 48000; break; @@ -1187,19 +1188,19 @@ BOOLEAN btc_media_aa_read_feeding(void) break; } - if (sbc_sampling == btc_aa_src_cb.media_feeding.cfg.pcm.sampling_freq) { - read_size = bytes_needed - btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue; + if (sbc_sampling == a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.sampling_freq) { + read_size = bytes_needed - a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue; nb_byte_read = btc_aa_src_data_read( - ((uint8_t *)btc_sbc_encoder.as16PcmBuffer) + - btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue, + ((uint8_t *)a2dp_source_local_param.btc_aa_src_cb.encoder.as16PcmBuffer) + + a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue, read_size); if (nb_byte_read == read_size) { - btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue = 0; + a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue = 0; return TRUE; } else { APPL_TRACE_WARNING("### UNDERFLOW :: ONLY READ %d BYTES OUT OF %d ###", nb_byte_read, read_size); - btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue += nb_byte_read; + a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue += nb_byte_read; return FALSE; } } @@ -1208,7 +1209,7 @@ BOOLEAN btc_media_aa_read_feeding(void) /* to read. */ /* E.g 128/6=21.3333 => read 22 and 21 and 21 => max = 2; threshold = 0*/ fract_needed = FALSE; /* Default */ - switch (btc_aa_src_cb.media_feeding.cfg.pcm.sampling_freq) { + switch (a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.sampling_freq) { case 32000: case 8000: fract_needed = TRUE; @@ -1224,26 +1225,26 @@ BOOLEAN btc_media_aa_read_feeding(void) /* Compute number of sample to read from source */ src_samples = blocm_x_subband; - src_samples *= btc_aa_src_cb.media_feeding.cfg.pcm.sampling_freq; + src_samples *= a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.sampling_freq; src_samples /= sbc_sampling; /* The previous division may have a remainder not null */ if (fract_needed) { - if (btc_aa_src_cb.media_feeding_state.pcm.aa_feed_counter <= fract_threshold) { + if (a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_counter <= fract_threshold) { src_samples++; /* for every read before threshold add one sample */ } /* do nothing if counter >= threshold */ - btc_aa_src_cb.media_feeding_state.pcm.aa_feed_counter++; /* one more read */ - if (btc_aa_src_cb.media_feeding_state.pcm.aa_feed_counter > fract_max) { - btc_aa_src_cb.media_feeding_state.pcm.aa_feed_counter = 0; + a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_counter++; /* one more read */ + if (a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_counter > fract_max) { + a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_counter = 0; } } /* Compute number of bytes to read from source */ read_size = src_samples; - read_size *= btc_aa_src_cb.media_feeding.cfg.pcm.num_channel; - read_size *= (btc_aa_src_cb.media_feeding.cfg.pcm.bit_per_sample / 8); + read_size *= a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.num_channel; + read_size *= (a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.bit_per_sample / 8); /* Read Data from data channel */ nb_byte_read = btc_aa_src_data_read((uint8_t *)read_buffer, read_size); @@ -1258,7 +1259,7 @@ BOOLEAN btc_media_aa_read_feeding(void) return FALSE; } - if (btc_aa_src_cb.feeding_mode == BTC_AV_FEEDING_ASYNCHRONOUS) { + if (a2dp_source_local_param.btc_aa_src_cb.feeding_mode == BTC_AV_FEEDING_ASYNCHRONOUS) { /* Fill the unfilled part of the read buffer with silence (0) */ memset(((UINT8 *)read_buffer) + nb_byte_read, 0, read_size - nb_byte_read); nb_byte_read = read_size; @@ -1266,34 +1267,34 @@ BOOLEAN btc_media_aa_read_feeding(void) } /* Initialize PCM up-sampling engine */ - bta_av_sbc_init_up_sample(btc_aa_src_cb.media_feeding.cfg.pcm.sampling_freq, - sbc_sampling, btc_aa_src_cb.media_feeding.cfg.pcm.bit_per_sample, - btc_aa_src_cb.media_feeding.cfg.pcm.num_channel); + bta_av_sbc_init_up_sample(a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.sampling_freq, + sbc_sampling, a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.bit_per_sample, + a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.num_channel); /* re-sample read buffer */ /* The output PCM buffer will be stereo, 16 bit per sample */ dst_size_used = bta_av_sbc_up_sample((UINT8 *)read_buffer, - (UINT8 *)up_sampled_buffer + btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue, + (UINT8 *)up_sampled_buffer + a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue, nb_byte_read, - sizeof(up_sampled_buffer) - btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue, + sizeof(up_sampled_buffer) - a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue, &src_size_used); /* update the residue */ - btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue += dst_size_used; + a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue += dst_size_used; /* only copy the pcm sample when we have up-sampled enough PCM */ - if (btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue >= bytes_needed) { + if (a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue >= bytes_needed) { /* Copy the output pcm samples in SBC encoding buffer */ - memcpy((UINT8 *)btc_sbc_encoder.as16PcmBuffer, + memcpy((UINT8 *)a2dp_source_local_param.btc_aa_src_cb.encoder.as16PcmBuffer, (UINT8 *)up_sampled_buffer, bytes_needed); /* update the residue */ - btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue -= bytes_needed; + a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue -= bytes_needed; - if (btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue != 0) { + if (a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue != 0) { memcpy((UINT8 *)up_sampled_buffer, (UINT8 *)up_sampled_buffer + bytes_needed, - btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue); + a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue); } return TRUE; } @@ -1313,13 +1314,13 @@ BOOLEAN btc_media_aa_read_feeding(void) static void btc_media_aa_prep_sbc_2_send(UINT8 nb_frame) { BT_HDR *p_buf; - UINT16 blocm_x_subband = btc_sbc_encoder.s16NumOfSubBands * - btc_sbc_encoder.s16NumOfBlocks; + UINT16 blocm_x_subband = a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfSubBands * + a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfBlocks; while (nb_frame) { if (NULL == (p_buf = osi_malloc(BTC_MEDIA_AA_BUF_SIZE))) { APPL_TRACE_ERROR ("ERROR btc_media_aa_prep_sbc_2_send no buffer TxCnt %d ", - fixed_queue_length(btc_aa_src_cb.TxAaQ)); + fixed_queue_length(a2dp_source_local_param.btc_aa_src_cb.TxAaQ)); return; } @@ -1330,53 +1331,53 @@ static void btc_media_aa_prep_sbc_2_send(UINT8 nb_frame) do { /* Write @ of allocated buffer in encoder.pu8Packet */ - btc_sbc_encoder.pu8Packet = (UINT8 *) (p_buf + 1) + p_buf->offset + p_buf->len; + a2dp_source_local_param.btc_aa_src_cb.encoder.pu8Packet = (UINT8 *) (p_buf + 1) + p_buf->offset + p_buf->len; /* Fill allocated buffer with 0 */ - memset(btc_sbc_encoder.as16PcmBuffer, 0, blocm_x_subband - * btc_sbc_encoder.s16NumOfChannels); + memset(a2dp_source_local_param.btc_aa_src_cb.encoder.as16PcmBuffer, 0, blocm_x_subband + * a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfChannels); /* Read PCM data and upsample them if needed */ if (btc_media_aa_read_feeding()) { /* SBC encode and descramble frame */ - SBC_Encoder(&(btc_sbc_encoder)); + SBC_Encoder(&(a2dp_source_local_param.btc_aa_src_cb.encoder)); /* Update SBC frame length */ - p_buf->len += btc_sbc_encoder.u16PacketLength; + p_buf->len += a2dp_source_local_param.btc_aa_src_cb.encoder.u16PacketLength; nb_frame--; p_buf->layer_specific++; } else { APPL_TRACE_WARNING("btc_media_aa_prep_sbc_2_send underflow %d, %d", - nb_frame, btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue); - btc_aa_src_cb.media_feeding_state.pcm.counter += nb_frame * - btc_sbc_encoder.s16NumOfSubBands * - btc_sbc_encoder.s16NumOfBlocks * - btc_aa_src_cb.media_feeding.cfg.pcm.num_channel * - btc_aa_src_cb.media_feeding.cfg.pcm.bit_per_sample / 8; + nb_frame, a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue); + a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.counter += nb_frame * + a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfSubBands * + a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfBlocks * + a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.num_channel * + a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.bit_per_sample / 8; /* no more pcm to read */ nb_frame = 0; /* break read loop if timer was stopped (media task stopped) */ - if ( btc_aa_src_cb.is_tx_timer == FALSE ) { + if ( a2dp_source_local_param.btc_aa_src_cb.is_tx_timer == FALSE ) { osi_free(p_buf); return; } } - } while (((p_buf->len + btc_sbc_encoder.u16PacketLength) < btc_aa_src_cb.TxAaMtuSize) + } while (((p_buf->len + a2dp_source_local_param.btc_aa_src_cb.encoder.u16PacketLength) < a2dp_source_local_param.btc_aa_src_cb.TxAaMtuSize) && (p_buf->layer_specific < 0x0F) && nb_frame); if (p_buf->len) { /* timestamp of the media packet header represent the TS of the first SBC frame i.e the timestamp before including this frame */ - *((UINT32 *) (p_buf + 1)) = btc_aa_src_cb.timestamp; + *((UINT32 *) (p_buf + 1)) = a2dp_source_local_param.btc_aa_src_cb.timestamp; - btc_aa_src_cb.timestamp += p_buf->layer_specific * blocm_x_subband; + a2dp_source_local_param.btc_aa_src_cb.timestamp += p_buf->layer_specific * blocm_x_subband; - if (btc_aa_src_cb.tx_flush) { + if (a2dp_source_local_param.btc_aa_src_cb.tx_flush) { APPL_TRACE_DEBUG("### tx suspended, discarded frame ###"); - if (fixed_queue_length(btc_aa_src_cb.TxAaQ) > 0) { - btc_a2dp_source_flush_q(btc_aa_src_cb.TxAaQ); + if (fixed_queue_length(a2dp_source_local_param.btc_aa_src_cb.TxAaQ) > 0) { + btc_a2dp_source_flush_q(a2dp_source_local_param.btc_aa_src_cb.TxAaQ); } osi_free(p_buf); @@ -1384,7 +1385,7 @@ static void btc_media_aa_prep_sbc_2_send(UINT8 nb_frame) } /* Enqueue the encoded SBC frame in AA Tx Queue */ - fixed_queue_enqueue(btc_aa_src_cb.TxAaQ, p_buf); + fixed_queue_enqueue(a2dp_source_local_param.btc_aa_src_cb.TxAaQ, p_buf); } else { osi_free(p_buf); } @@ -1407,24 +1408,24 @@ static void btc_a2dp_source_prep_2_send(UINT8 nb_frame) nb_frame = MAX_OUTPUT_A2DP_SRC_FRAME_QUEUE_SZ; } - if (fixed_queue_length(btc_aa_src_cb.TxAaQ) > (MAX_OUTPUT_A2DP_SRC_FRAME_QUEUE_SZ - nb_frame)) { + if (fixed_queue_length(a2dp_source_local_param.btc_aa_src_cb.TxAaQ) > (MAX_OUTPUT_A2DP_SRC_FRAME_QUEUE_SZ - nb_frame)) { APPL_TRACE_WARNING("TX Q overflow: %d/%d", - fixed_queue_length(btc_aa_src_cb.TxAaQ), MAX_OUTPUT_A2DP_SRC_FRAME_QUEUE_SZ - nb_frame); + fixed_queue_length(a2dp_source_local_param.btc_aa_src_cb.TxAaQ), MAX_OUTPUT_A2DP_SRC_FRAME_QUEUE_SZ - nb_frame); } - while (fixed_queue_length(btc_aa_src_cb.TxAaQ) > (MAX_OUTPUT_A2DP_SRC_FRAME_QUEUE_SZ - nb_frame)) { - osi_free(fixed_queue_try_dequeue(btc_aa_src_cb.TxAaQ)); + while (fixed_queue_length(a2dp_source_local_param.btc_aa_src_cb.TxAaQ) > (MAX_OUTPUT_A2DP_SRC_FRAME_QUEUE_SZ - nb_frame)) { + osi_free(fixed_queue_try_dequeue(a2dp_source_local_param.btc_aa_src_cb.TxAaQ)); } // Transcode frame - switch (btc_aa_src_cb.TxTranscoding) { + switch (a2dp_source_local_param.btc_aa_src_cb.TxTranscoding) { case BTC_MEDIA_TRSCD_PCM_2_SBC: btc_media_aa_prep_sbc_2_send(nb_frame); break; default: - APPL_TRACE_ERROR("%s unsupported transcoding format 0x%x", __func__, btc_aa_src_cb.TxTranscoding); + APPL_TRACE_ERROR("%s unsupported transcoding format 0x%x", __func__, a2dp_source_local_param.btc_aa_src_cb.TxTranscoding); break; } } @@ -1464,7 +1465,7 @@ static void btc_a2dp_source_handle_timer(UNUSED_ATTR void *context) return; } - if (btc_aa_src_cb.is_tx_timer == TRUE) { + if (a2dp_source_local_param.btc_aa_src_cb.is_tx_timer == TRUE) { btc_a2dp_source_send_aa_frame(); } else { APPL_TRACE_WARNING("Media task Scheduled after Suspend"); @@ -1489,17 +1490,17 @@ static void btc_a2dp_source_alarm_cb(UNUSED_ATTR void *context) static void btc_a2dp_source_feeding_state_reset(void) { /* By default, just clear the entire state */ - memset(&btc_aa_src_cb.media_feeding_state, 0, sizeof(btc_aa_src_cb.media_feeding_state)); + memset(&a2dp_source_local_param.btc_aa_src_cb.media_feeding_state, 0, sizeof(a2dp_source_local_param.btc_aa_src_cb.media_feeding_state)); - if (btc_aa_src_cb.TxTranscoding == BTC_MEDIA_TRSCD_PCM_2_SBC) { - btc_aa_src_cb.media_feeding_state.pcm.bytes_per_tick = - (btc_aa_src_cb.media_feeding.cfg.pcm.sampling_freq * - btc_aa_src_cb.media_feeding.cfg.pcm.bit_per_sample / 8 * - btc_aa_src_cb.media_feeding.cfg.pcm.num_channel * + if (a2dp_source_local_param.btc_aa_src_cb.TxTranscoding == BTC_MEDIA_TRSCD_PCM_2_SBC) { + a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.bytes_per_tick = + (a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.sampling_freq * + a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.bit_per_sample / 8 * + a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.num_channel * BTC_MEDIA_TIME_TICK_MS) / 1000; APPL_TRACE_EVENT("pcm bytes per tick %d", - (int)btc_aa_src_cb.media_feeding_state.pcm.bytes_per_tick); + (int)a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.bytes_per_tick); } } @@ -1515,26 +1516,26 @@ static void btc_a2dp_source_feeding_state_reset(void) static void btc_a2dp_source_aa_start_tx(void) { APPL_TRACE_DEBUG("btc_a2dp_source_aa_start_tx is timer %d, feeding mode %d", - btc_aa_src_cb.is_tx_timer, btc_aa_src_cb.feeding_mode); + a2dp_source_local_param.btc_aa_src_cb.is_tx_timer, a2dp_source_local_param.btc_aa_src_cb.feeding_mode); - btc_aa_src_cb.is_tx_timer = TRUE; - last_frame_us = 0; + a2dp_source_local_param.btc_aa_src_cb.is_tx_timer = TRUE; + a2dp_source_local_param.last_frame_us = 0; /* Reset the media feeding state */ btc_a2dp_source_feeding_state_reset(); APPL_TRACE_EVENT("starting timer %dms", BTC_MEDIA_TIME_TICK_MS); - assert(btc_aa_src_cb.media_alarm == NULL); + assert(a2dp_source_local_param.btc_aa_src_cb.media_alarm == NULL); - btc_aa_src_cb.media_alarm = osi_alarm_new("aaTx", btc_a2dp_source_alarm_cb, NULL, BTC_MEDIA_TIME_TICK_MS); + a2dp_source_local_param.btc_aa_src_cb.media_alarm = osi_alarm_new("aaTx", btc_a2dp_source_alarm_cb, NULL, BTC_MEDIA_TIME_TICK_MS); - if (!btc_aa_src_cb.media_alarm) { + if (!a2dp_source_local_param.btc_aa_src_cb.media_alarm) { BTC_TRACE_ERROR("%s unable to allocate media alarm.", __func__); return; } - osi_alarm_set_periodic(btc_aa_src_cb.media_alarm, BTC_MEDIA_TIME_TICK_MS); + osi_alarm_set_periodic(a2dp_source_local_param.btc_aa_src_cb.media_alarm, BTC_MEDIA_TIME_TICK_MS); } /******************************************************************************* @@ -1548,17 +1549,17 @@ static void btc_a2dp_source_aa_start_tx(void) *******************************************************************************/ static void btc_a2dp_source_aa_stop_tx(void) { - APPL_TRACE_DEBUG("%s is_tx_timer: %d", __func__, btc_aa_src_cb.is_tx_timer); + APPL_TRACE_DEBUG("%s is_tx_timer: %d", __func__, a2dp_source_local_param.btc_aa_src_cb.is_tx_timer); - const bool send_ack = (btc_aa_src_cb.is_tx_timer != FALSE); + const bool send_ack = (a2dp_source_local_param.btc_aa_src_cb.is_tx_timer != FALSE); /* Stop the timer first */ - if (btc_aa_src_cb.media_alarm) { - osi_alarm_cancel(btc_aa_src_cb.media_alarm); - osi_alarm_free(btc_aa_src_cb.media_alarm); + if (a2dp_source_local_param.btc_aa_src_cb.media_alarm) { + osi_alarm_cancel(a2dp_source_local_param.btc_aa_src_cb.media_alarm); + osi_alarm_free(a2dp_source_local_param.btc_aa_src_cb.media_alarm); } - btc_aa_src_cb.media_alarm = NULL; - btc_aa_src_cb.is_tx_timer = FALSE; + a2dp_source_local_param.btc_aa_src_cb.media_alarm = NULL; + a2dp_source_local_param.btc_aa_src_cb.is_tx_timer = FALSE; /* Try to send acknowldegment once the media stream is stopped. This will make sure that the A2DP HAL layer is @@ -1576,8 +1577,8 @@ static void btc_a2dp_source_aa_stop_tx(void) } /* audio engine stopped, reset tx suspended flag */ - btc_aa_src_cb.tx_flush = 0; - last_frame_us = 0; + a2dp_source_local_param.btc_aa_src_cb.tx_flush = 0; + a2dp_source_local_param.last_frame_us = 0; /* Reset the feeding state */ btc_a2dp_source_feeding_state_reset(); @@ -1602,11 +1603,11 @@ static void btc_a2dp_source_flush_q(fixed_queue_t *p_q) static void btc_a2dp_source_thread_init(UNUSED_ATTR void *context) { APPL_TRACE_EVENT("%s\n", __func__); - memset(&btc_aa_src_cb, 0, sizeof(btc_aa_src_cb)); + memset(&a2dp_source_local_param.btc_aa_src_cb, 0, sizeof(a2dp_source_local_param.btc_aa_src_cb)); btc_a2dp_source_state = BTC_A2DP_SOURCE_STATE_ON; - btc_aa_src_cb.TxAaQ = fixed_queue_new(QUEUE_SIZE_MAX); + a2dp_source_local_param.btc_aa_src_cb.TxAaQ = fixed_queue_new(QUEUE_SIZE_MAX); btc_a2dp_control_init(); } @@ -1619,9 +1620,9 @@ static void btc_a2dp_source_thread_cleanup(UNUSED_ATTR void *context) btc_a2dp_control_cleanup(); - fixed_queue_free(btc_aa_src_cb.TxAaQ, osi_free_func); + fixed_queue_free(a2dp_source_local_param.btc_aa_src_cb.TxAaQ, osi_free_func); - future_ready(btc_a2dp_source_future, NULL); + future_ready(a2dp_source_local_param.btc_a2dp_source_future, NULL); } #endif /* BTC_AV_INCLUDED */ diff --git a/components/bt/bluedroid/btc/profile/std/a2dp/btc_av.c b/components/bt/bluedroid/btc/profile/std/a2dp/btc_av.c index f428150bae..be1f1f9628 100644 --- a/components/bt/bluedroid/btc/profile/std/a2dp/btc_av.c +++ b/components/bt/bluedroid/btc/profile/std/a2dp/btc_av.c @@ -79,6 +79,9 @@ typedef struct { UINT8 flags; tBTA_AV_EDR edr; UINT8 peer_sep; /* sep type of peer device */ +#if BTC_AV_SRC_INCLUDED + osi_alarm_t *tle_av_open_on_rc; +#endif /* BTC_AV_SRC_INCLUDED */ } btc_av_cb_t; typedef struct { @@ -89,11 +92,12 @@ typedef struct { /***************************************************************************** ** Static variables ******************************************************************************/ +#if A2D_DYNAMIC_MEMORY == FALSE static btc_av_cb_t btc_av_cb = {0}; - -#if BTC_AV_SRC_INCLUDED -static osi_alarm_t *tle_av_open_on_rc = NULL; -#endif /* BTC_AV_SRC_INCLUDED */ +#else +static btc_av_cb_t *btc_av_cb_ptr = NULL; +#define btc_av_cb (*btc_av_cb_ptr) +#endif ///A2D_DYNAMIC_MEMORY == FALSE /* both interface and media task needs to be ready to alloc incoming request */ #define CHECK_BTAV_INIT() do \ @@ -337,8 +341,8 @@ static BOOLEAN btc_av_state_idle_handler(btc_sm_event_t event, void *p_data) #if BTC_AV_SRC_INCLUDED BTC_TRACE_DEBUG("BTA_AV_RC_OPEN_EVT received w/o AV"); - tle_av_open_on_rc = osi_alarm_new("AVconn", btc_initiate_av_open_tmr_hdlr, NULL, BTC_TIMEOUT_AV_OPEN_ON_RC_SECS * 1000); - osi_alarm_set(tle_av_open_on_rc, BTC_TIMEOUT_AV_OPEN_ON_RC_SECS * 1000); + btc_av_cb.tle_av_open_on_rc = osi_alarm_new("AVconn", btc_initiate_av_open_tmr_hdlr, NULL, BTC_TIMEOUT_AV_OPEN_ON_RC_SECS * 1000); + osi_alarm_set(btc_av_cb.tle_av_open_on_rc, BTC_TIMEOUT_AV_OPEN_ON_RC_SECS * 1000); #endif /* BTC_AV_SRC_INCLUDED */ btc_rc_handler(event, p_data); break; @@ -353,9 +357,9 @@ static BOOLEAN btc_av_state_idle_handler(btc_sm_event_t event, void *p_data) case BTA_AV_RC_CLOSE_EVT: #if BTC_AV_SRC_INCLUDED - if (tle_av_open_on_rc) { - osi_alarm_free(tle_av_open_on_rc); - tle_av_open_on_rc = NULL; + if (btc_av_cb.tle_av_open_on_rc) { + osi_alarm_free(btc_av_cb.tle_av_open_on_rc); + btc_av_cb.tle_av_open_on_rc = NULL; } #endif /* BTC_AV_SRC_INCLUDED */ btc_rc_handler(event, p_data); @@ -961,6 +965,19 @@ static void btc_av_event_free_data(btc_sm_event_t event, void *p_data) static bt_status_t btc_av_init(int service_id) { + +#if A2D_DYNAMIC_MEMORY == TRUE + if (btc_av_cb_ptr != NULL) { + return BT_STATUS_FAIL; + } + + if ((btc_av_cb_ptr = (btc_av_cb_t *)osi_malloc(sizeof(btc_av_cb_t))) == NULL) { + APPL_TRACE_ERROR("%s malloc failed!", __func__); + return BT_STATUS_NOMEM; + } + memset((void *)btc_av_cb_ptr, 0, sizeof(btc_av_cb_t)); +#endif + if (btc_av_cb.sm_handle == NULL) { btc_av_cb.service_id = service_id; bool stat = false; @@ -975,6 +992,10 @@ static bt_status_t btc_av_init(int service_id) } if (!stat) { +#if A2D_DYNAMIC_MEMORY == TRUE + osi_free(btc_av_cb_ptr); + btc_av_cb_ptr = NULL; +#endif return BT_STATUS_FAIL; } @@ -1034,9 +1055,9 @@ static void clean_up(int service_id) if (service_id == BTA_A2DP_SOURCE_SERVICE_ID) { #if BTC_AV_SRC_INCLUDED btc_a2dp_source_shutdown(); - if (tle_av_open_on_rc) { - osi_alarm_free(tle_av_open_on_rc); - tle_av_open_on_rc = NULL; + if (btc_av_cb.tle_av_open_on_rc) { + osi_alarm_free(btc_av_cb.tle_av_open_on_rc); + btc_av_cb.tle_av_open_on_rc = NULL; } #endif /* BTC_AV_SRC_INCLUDED */ } @@ -1056,6 +1077,11 @@ static void clean_up(int service_id) btc_a2dp_sink_shutdown(); #endif /* BTC_AV_SINK_INCLUDED */ } + +#if A2D_DYNAMIC_MEMORY == TRUE + osi_free(btc_av_cb_ptr); + btc_av_cb_ptr = NULL; +#endif } /******************************************************************************* diff --git a/components/bt/bluedroid/btc/profile/std/a2dp/include/btc_av_co.h b/components/bt/bluedroid/btc/profile/std/a2dp/include/btc_av_co.h index cacaa01d8f..a7943b70cb 100644 --- a/components/bt/bluedroid/btc/profile/std/a2dp/include/btc_av_co.h +++ b/components/bt/bluedroid/btc/profile/std/a2dp/include/btc_av_co.h @@ -16,6 +16,7 @@ #define __BTC_AV_CO_H__ #include "btc_a2dp.h" +#include "bta/bta_av_co.h" #if (BTA_AV_INCLUDED == TRUE) /******************************************************************************* @@ -28,7 +29,62 @@ enum { BTC_SV_AV_AA_SEP_INDEX /* Last index */ }; +/***************************************************************************** +** Local data +*****************************************************************************/ +typedef struct { + UINT8 sep_info_idx; /* local SEP index (in BTA tables) */ + UINT8 seid; /* peer SEP index (in peer tables) */ + UINT8 codec_type; /* peer SEP codec type */ + UINT8 codec_caps[AVDT_CODEC_SIZE]; /* peer SEP codec capabilities */ + UINT8 num_protect; /* peer SEP number of CP elements */ + UINT8 protect_info[BTA_AV_CP_INFO_LEN]; /* peer SEP content protection info */ +} tBTA_AV_CO_SINK; +typedef struct { + BD_ADDR addr; /* address of audio/video peer */ + tBTA_AV_CO_SINK snks[BTC_SV_AV_AA_SEP_INDEX]; /* array of supported sinks */ + tBTA_AV_CO_SINK srcs[BTC_SV_AV_AA_SEP_INDEX]; /* array of supported srcs */ + UINT8 num_snks; /* total number of sinks at peer */ + UINT8 num_srcs; /* total number of srcs at peer */ + UINT8 num_seps; /* total number of seids at peer */ + UINT8 num_rx_snks; /* number of received sinks */ + UINT8 num_rx_srcs; /* number of received srcs */ + UINT8 num_sup_snks; /* number of supported sinks in the snks array */ + UINT8 num_sup_srcs; /* number of supported srcs in the srcs array */ + tBTA_AV_CO_SINK *p_snk; /* currently selected sink */ + tBTA_AV_CO_SINK *p_src; /* currently selected src */ + UINT8 codec_cfg[AVDT_CODEC_SIZE]; /* current codec configuration */ + BOOLEAN cp_active; /* current CP configuration */ + BOOLEAN acp; /* acceptor */ + BOOLEAN recfg_needed; /* reconfiguration is needed */ + BOOLEAN opened; /* opened */ + UINT16 mtu; /* maximum transmit unit size */ + UINT16 uuid_to_connect; /* uuid of peer device */ +} tBTA_AV_CO_PEER; + +typedef struct { + BOOLEAN active; + UINT8 flag; +} tBTA_AV_CO_CP; + +typedef struct { + /* Connected peer information */ + tBTA_AV_CO_PEER peers[BTA_AV_NUM_STRS]; + /* Current codec configuration - access to this variable must be protected */ + tBTC_AV_CODEC_INFO codec_cfg; + tBTC_AV_CODEC_INFO codec_cfg_setconfig; /* remote peer setconfig preference */ + + tBTA_AV_CO_CP cp; +} tBTA_AV_CO_CB; + +/* Control block instance */ +#if AVRC_DYNAMIC_MEMORY == FALSE +extern tBTA_AV_CO_CB bta_av_co_cb; +#else +extern tBTA_AV_CO_CB *bta_av_co_cb_ptr; +#define bta_av_co_cb (*bta_av_co_cb_ptr) +#endif /******************************************************************************* ** Functions ********************************************************************************/ diff --git a/components/bt/bluedroid/btc/profile/std/avrc/btc_avrc.c b/components/bt/bluedroid/btc/profile/std/avrc/btc_avrc.c index 6e204be482..841d55c360 100644 --- a/components/bt/bluedroid/btc/profile/std/avrc/btc_avrc.c +++ b/components/bt/bluedroid/btc/profile/std/avrc/btc_avrc.c @@ -35,40 +35,6 @@ #if BTC_AV_INCLUDED -/***************************************************************************** -** Constants & Macros -******************************************************************************/ -#define BTC_RC_CT_INIT_MAGIC 0x20181128 -#define BTC_RC_TG_INIT_MAGIC 0x20181129 - -#define MAX_RC_NOTIFICATIONS (13) // refer to ESP_AVRC_RN_MAX_EVT - -#define CHECK_ESP_RC_CONNECTED do { \ - BTC_TRACE_DEBUG("## %s ##", __FUNCTION__); \ - if (btc_rc_cb.rc_connected == FALSE) { \ - BTC_TRACE_WARNING("Function %s() called when RC is not connected", __FUNCTION__); \ - return ESP_ERR_INVALID_STATE; \ - } \ - } while (0) - -/***************************************************************************** -** Local type definitions -******************************************************************************/ -typedef struct { - BOOLEAN registered; - UINT8 label; -} btc_rc_reg_ntf_t; - -typedef struct { - BOOLEAN rc_connected; - UINT8 rc_handle; - tBTA_AV_FEAT rc_features; - UINT16 rc_ct_features; - UINT16 rc_tg_features; - BD_ADDR rc_addr; - btc_rc_reg_ntf_t rc_ntf[MAX_RC_NOTIFICATIONS]; -} btc_rc_cb_t; - static UINT8 opcode_from_pdu(UINT8 pdu); static void send_reject_response (UINT8 rc_handle, UINT8 label, UINT8 pdu, UINT8 status); static void handle_rc_connect (tBTA_AV_RC_OPEN *p_rc_open); @@ -86,7 +52,11 @@ static void btc_rc_upstreams_evt(UINT16 event, tAVRC_COMMAND *pavrc_cmd, UINT8 c static uint32_t s_rc_ct_init; static uint32_t s_rc_tg_init; +#if AVRC_DYNAMIC_MEMORY == FALSE static btc_rc_cb_t btc_rc_cb; +#else +btc_rc_cb_t *btc_rc_cb_ptr; +#endif ///AVRC_DYNAMIC_MEMORY == FALSE const static uint16_t cs_psth_allowed_cmd[8] = { 0x0000, /* bit mask: 0=SELECT, 1=UP, 2=DOWN, 3=LEFT, @@ -1031,7 +1001,9 @@ static void btc_avrc_ct_init(void) /// initialize CT-TG shared resources if (s_rc_tg_init != BTC_RC_TG_INIT_MAGIC) { - memset (&btc_rc_cb, 0, sizeof(btc_rc_cb)); + memset (&btc_rc_cb, 0, sizeof(btc_rc_cb_t)); + btc_rc_cb.rc_vol_label = MAX_LABEL; + btc_rc_cb.rc_volume = MAX_VOLUME; } } @@ -1059,7 +1031,7 @@ static void btc_avrc_ct_deinit(void) /// deinit CT-TG shared resources if (s_rc_tg_init != BTC_RC_TG_INIT_MAGIC) { - memset (&btc_rc_cb, 0, sizeof(btc_rc_cb)); + memset (&btc_rc_cb, 0, sizeof(btc_rc_cb_t)); } BTC_TRACE_API("## %s ## completed", __FUNCTION__); diff --git a/components/bt/bluedroid/btc/profile/std/gap/btc_gap_ble.c b/components/bt/bluedroid/btc/profile/std/gap/btc_gap_ble.c index ffac0c35f6..be270a430c 100644 --- a/components/bt/bluedroid/btc/profile/std/gap/btc_gap_ble.c +++ b/components/bt/bluedroid/btc/profile/std/gap/btc_gap_ble.c @@ -31,8 +31,14 @@ #include "osi/mutex.h" #include "esp_bt.h" +#if BTC_DYNAMIC_MENDRY == FALSE static tBTA_BLE_ADV_DATA gl_bta_adv_data; static tBTA_BLE_ADV_DATA gl_bta_scan_rsp_data; +#else +tBTA_BLE_ADV_DATA *gl_bta_adv_data_ptr; +tBTA_BLE_ADV_DATA *gl_bta_scan_rsp_data_ptr; +#endif + #if SCAN_QUEUE_CONGEST_CHECK static list_t *adv_filter_list; static osi_mutex_t adv_list_lock; diff --git a/components/bt/bluedroid/btc/profile/std/gatt/btc_gatt_util.c b/components/bt/bluedroid/btc/profile/std/gatt/btc_gatt_util.c index 8653f60561..37497a8afc 100644 --- a/components/bt/bluedroid/btc/profile/std/gatt/btc_gatt_util.c +++ b/components/bt/bluedroid/btc/profile/std/gatt/btc_gatt_util.c @@ -20,7 +20,7 @@ #define GATTC_READ_VALUE_TYPE_VALUE 0x0000 /* Attribute value itself */ #define GATTC_READ_VALUE_TYPE_AGG_FORMAT 0x2905 /* Characteristic Aggregate Format*/ -static unsigned char BASE_UUID[16] = { +static const unsigned char BASE_UUID[16] = { 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; diff --git a/components/bt/bluedroid/btc/profile/std/gatt/btc_gatts.c b/components/bt/bluedroid/btc/profile/std/gatt/btc_gatts.c index e93fc937ea..117b6b8614 100644 --- a/components/bt/bluedroid/btc/profile/std/gatt/btc_gatts.c +++ b/components/bt/bluedroid/btc/profile/std/gatt/btc_gatts.c @@ -30,19 +30,11 @@ #define A2C_GATTS_EVT(_bta_event) (_bta_event) //BTA TO BTC EVT #define C2A_GATTS_EVT(_btc_event) (_btc_event) //BTC TO BTA EVT -typedef struct { - future_t *complete_future; - uint16_t svc_start_hdl; - esp_bt_uuid_t svc_uuid; - bool is_tab_creat_svc; - bool is_use_svc; - uint8_t num_handle; - uint8_t handle_idx; - uint16_t handles[ESP_GATT_ATTR_HANDLE_MAX]; -} esp_btc_creat_tab_t; - +#if GATT_DYNAMIC_MEMORY == FALSE static esp_btc_creat_tab_t btc_creat_tab_env; - +#else +esp_btc_creat_tab_t *btc_creat_tab_env_ptr; +#endif static esp_gatt_status_t btc_gatts_check_valid_attr_tab(esp_gatts_attr_db_t *gatts_attr_db, uint8_t max_nb_attr); @@ -106,13 +98,13 @@ void btc_gatts_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) } } break; - + } case BTC_GATTS_ACT_ADD_CHAR: { if (src->add_char.char_val.attr_value && (src->add_char.char_val.attr_len > 0)) { dst->add_char.char_val.attr_value = (uint8_t *) osi_malloc(src->add_char.char_val.attr_len); if (dst->add_char.char_val.attr_value) { - memcpy(dst->add_char.char_val.attr_value, src->add_char.char_val.attr_value, + memcpy(dst->add_char.char_val.attr_value, src->add_char.char_val.attr_value, src->add_char.char_val.attr_len); } else { BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act); @@ -231,7 +223,7 @@ void btc_gatts_arg_deep_free(btc_msg_t *msg) } -static void btc_gatts_act_create_attr_tab(esp_gatts_attr_db_t *gatts_attr_db, +static void btc_gatts_act_create_attr_tab(esp_gatts_attr_db_t *gatts_attr_db, esp_gatt_if_t gatts_if, uint8_t max_nb_attr, uint8_t srvc_inst_id) @@ -283,7 +275,7 @@ static void btc_gatts_act_create_attr_tab(esp_gatts_attr_db_t *gatts_attr_db, esp_srvc_id.id.inst_id = srvc_inst_id; btc_gatts_uuid_format_convert(&esp_srvc_id.id.uuid,gatts_attr_db[i].att_desc.length, gatts_attr_db[i].att_desc.value); - + btc_to_bta_srvc_id(&srvc_id, &esp_srvc_id); if (btc_creat_tab_env.is_use_svc != true) { BTA_GATTS_CreateService(gatts_if, &srvc_id.id.uuid, @@ -297,7 +289,7 @@ static void btc_gatts_act_create_attr_tab(esp_gatts_attr_db_t *gatts_attr_db, memset(&btc_creat_tab_env, 0, sizeof(esp_btc_creat_tab_t)); return; } - + if (future_await(future_p) == FUTURE_FAIL) { BTC_TRACE_ERROR("%s failed\n", __func__); return; @@ -332,12 +324,12 @@ static void btc_gatts_act_create_attr_tab(esp_gatts_attr_db_t *gatts_attr_db, } case ESP_GATT_UUID_INCLUDE_SERVICE:{ esp_gatts_incl_svc_desc_t *incl_svc_desc = (esp_gatts_incl_svc_desc_t *)gatts_attr_db[i].att_desc.value; - + if(incl_svc_desc!= NULL){ if(btc_creat_tab_env.svc_start_hdl != 0){ - BTA_GATTS_AddIncludeService(btc_creat_tab_env.svc_start_hdl, + BTA_GATTS_AddIncludeService(btc_creat_tab_env.svc_start_hdl, incl_svc_desc->start_hdl); - + if (future_await(future_p) == FUTURE_FAIL) { BTC_TRACE_ERROR("%s failed\n", __func__); return; @@ -378,10 +370,10 @@ static void btc_gatts_act_create_attr_tab(esp_gatts_attr_db_t *gatts_attr_db, } } } - + break; } - case ESP_GATT_UUID_CHAR_EXT_PROP: + case ESP_GATT_UUID_CHAR_EXT_PROP: case ESP_GATT_UUID_CHAR_DESCRIPTION: case ESP_GATT_UUID_CHAR_CLIENT_CONFIG: case ESP_GATT_UUID_CHAR_SRVR_CONFIG: @@ -406,7 +398,7 @@ static void btc_gatts_act_create_attr_tab(esp_gatts_attr_db_t *gatts_attr_db, btc_to_bta_uuid(&bta_char_uuid, &uuid_temp); control.auto_rsp = gatts_attr_db[i].attr_control.auto_rsp; BTA_GATTS_AddCharDescriptor(svc_hal, perm, &bta_char_uuid, &attr_val, &control); - + if (future_await(future_p) == FUTURE_FAIL) { BTC_TRACE_ERROR("%s failed\n", __func__); return; @@ -419,7 +411,7 @@ static void btc_gatts_act_create_attr_tab(esp_gatts_attr_db_t *gatts_attr_db, break; } - + } param.add_attr_tab.handles = btc_creat_tab_env.handles; @@ -427,7 +419,7 @@ static void btc_gatts_act_create_attr_tab(esp_gatts_attr_db_t *gatts_attr_db, param.add_attr_tab.svc_inst_id = srvc_inst_id; - btc_gatts_cb_to_app(ESP_GATTS_CREAT_ATTR_TAB_EVT, gatts_if, ¶m); + btc_gatts_cb_to_app(ESP_GATTS_CREAT_ATTR_TAB_EVT, gatts_if, ¶m); //reset the env after sent the data to app memset(&btc_creat_tab_env, 0, sizeof(esp_btc_creat_tab_t)); @@ -504,7 +496,7 @@ static esp_gatt_status_t btc_gatts_check_valid_attr_tab(esp_gatts_attr_db_t *gat esp_gatt_status_t btc_gatts_get_attr_value(uint16_t attr_handle, uint16_t *length, uint8_t **value) { - + return BTA_GetAttributeValue(attr_handle, length, value); } @@ -568,14 +560,14 @@ static void btc_gatts_inter_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data) { bt_status_t status; btc_msg_t msg; - + msg.sig = BTC_SIG_API_CB; msg.pid = BTC_PID_GATTS; msg.act = event; if(btc_creat_tab_env.is_tab_creat_svc && btc_creat_tab_env.complete_future) { switch(event) { case BTA_GATTS_CREATE_EVT: { - //save the service handle to the btc module after used + //save the service handle to the btc module after used //the attribute table method to creat a service bta_to_btc_uuid(&btc_creat_tab_env.svc_uuid, &p_data->create.uuid); uint8_t index = btc_creat_tab_env.handle_idx; @@ -663,7 +655,7 @@ void btc_gatts_call_handler(btc_msg_t *msg) btc_to_bta_uuid(&uuid, &arg->add_char.char_uuid); BTA_GATTS_AddCharacteristic(arg->add_char.service_handle, &uuid, - arg->add_char.perm, arg->add_char.property, + arg->add_char.perm, arg->add_char.property, (tGATT_ATTR_VAL *)&arg->add_char.char_val, (tBTA_GATTS_ATTR_CONTROL *)&arg->add_char.attr_control); break; @@ -672,7 +664,7 @@ void btc_gatts_call_handler(btc_msg_t *msg) tBT_UUID uuid; btc_to_bta_uuid(&uuid, &arg->add_descr.descr_uuid); BTA_GATTS_AddCharDescriptor(arg->add_descr.service_handle, arg->add_descr.perm, &uuid, - (tBTA_GATT_ATTR_VAL *)&arg->add_descr.descr_val, + (tBTA_GATT_ATTR_VAL *)&arg->add_descr.descr_val, (tBTA_GATTS_ATTR_CONTROL *)&arg->add_descr.attr_control); break; } @@ -700,7 +692,7 @@ void btc_gatts_call_handler(btc_msg_t *msg) break; } case BTC_GATTS_ACT_SET_ATTR_VALUE: - BTA_SetAttributeValue(arg->set_attr_val.handle, arg->set_attr_val.length, + BTA_SetAttributeValue(arg->set_attr_val.handle, arg->set_attr_val.length, arg->set_attr_val.value); break; case BTC_GATTS_ACT_OPEN: { @@ -785,7 +777,7 @@ void btc_gatts_cb_handler(btc_msg_t *msg) param.read.offset = p_data->req_data.p_data->read_req.offset; param.read.is_long = p_data->req_data.p_data->read_req.is_long; - param.read.need_rsp = p_data->req_data.p_data->read_req.need_rsp; + param.read.need_rsp = p_data->req_data.p_data->read_req.need_rsp; btc_gatts_cb_to_app(ESP_GATTS_READ_EVT, gatts_if, ¶m); break; } diff --git a/components/bt/bluedroid/btc/profile/std/hf_client/btc_hf_client.c b/components/bt/bluedroid/btc/profile/std/hf_client/btc_hf_client.c index d0cdbc5e66..d3ccd66997 100644 --- a/components/bt/bluedroid/btc/profile/std/hf_client/btc_hf_client.c +++ b/components/bt/bluedroid/btc/profile/std/hf_client/btc_hf_client.c @@ -61,42 +61,31 @@ BTA_HF_CLIENT_FEAT_CODEC) #endif -/************************************************************************************ -** Local type definitions -************************************************************************************/ -/* BTC-HF control block to map bdaddr to BTA handle */ -typedef struct -{ - bool initialized; - UINT16 handle; - bt_bdaddr_t connected_bda; - esp_hf_client_connection_state_t state; - esp_hf_vr_state_t vr_state; - tBTA_HF_CLIENT_PEER_FEAT peer_feat; - tBTA_HF_CLIENT_CHLD_FEAT chld_feat; -} btc_hf_client_cb_t; + /************************************************************************************ ** Static variables ************************************************************************************/ const char *btc_hf_client_version = "1.6"; -static UINT32 btc_hf_client_features = 0; -static btc_hf_client_cb_t btc_hf_client_cb; -static esp_hf_client_incoming_data_cb_t btc_hf_client_incoming_data_cb = NULL; -static esp_hf_client_outgoing_data_cb_t btc_hf_client_outgoing_data_cb = NULL; + +#if HFP_DYNAMIC_MEMORY == FALSE +static hf_client_local_param_t hf_client_local_param; +#else +hf_client_local_param_t *hf_client_local_param_ptr; +#endif /************************************************************************************ ** Static functions ************************************************************************************/ #define CHECK_HF_CLIENT_INIT() do { \ -if (! btc_hf_client_cb.initialized) { \ +if (! hf_client_local_param.btc_hf_client_cb.initialized) { \ return BT_STATUS_NOT_READY; \ } \ } while (0) #define CHECK_HF_CLIENT_SLC_CONNECTED() do { \ -if (! btc_hf_client_cb.initialized || \ - btc_hf_client_cb.state != ESP_HF_CLIENT_CONNECTION_STATE_SLC_CONNECTED) { \ +if (! hf_client_local_param.btc_hf_client_cb.initialized || \ + hf_client_local_param.btc_hf_client_cb.state != ESP_HF_CLIENT_CONNECTION_STATE_SLC_CONNECTED) { \ return BT_STATUS_NOT_READY; \ } \ } while (0) @@ -111,14 +100,14 @@ static inline void btc_hf_client_cb_to_app(esp_hf_client_cb_event_t event, esp_h static void clear_state(void) { - memset(&btc_hf_client_cb, 0, sizeof(btc_hf_client_cb_t)); + memset(&hf_client_local_param.btc_hf_client_cb, 0, sizeof(btc_hf_client_cb_t)); } static BOOLEAN is_connected(bt_bdaddr_t *bd_addr) { - if (((btc_hf_client_cb.state == ESP_HF_CLIENT_CONNECTION_STATE_CONNECTED) || - (btc_hf_client_cb.state == ESP_HF_CLIENT_CONNECTION_STATE_SLC_CONNECTED))&& - ((bd_addr == NULL) || (bdcmp(bd_addr->address, btc_hf_client_cb.connected_bda.address) == 0))) + if (((hf_client_local_param.btc_hf_client_cb.state == ESP_HF_CLIENT_CONNECTION_STATE_CONNECTED) || + (hf_client_local_param.btc_hf_client_cb.state == ESP_HF_CLIENT_CONNECTION_STATE_SLC_CONNECTED))&& + ((bd_addr == NULL) || (bdcmp(bd_addr->address, hf_client_local_param.btc_hf_client_cb.connected_bda.address) == 0))) return TRUE; return FALSE; } @@ -126,23 +115,23 @@ static BOOLEAN is_connected(bt_bdaddr_t *bd_addr) void btc_hf_client_reg_data_cb(esp_hf_client_incoming_data_cb_t recv, esp_hf_client_outgoing_data_cb_t send) { - btc_hf_client_incoming_data_cb = recv; - btc_hf_client_outgoing_data_cb = send; + hf_client_local_param.btc_hf_client_incoming_data_cb = recv; + hf_client_local_param.btc_hf_client_outgoing_data_cb = send; } void btc_hf_client_incoming_data_cb_to_app(const uint8_t *data, uint32_t len) { // todo: critical section protection - if (btc_hf_client_incoming_data_cb) { - btc_hf_client_incoming_data_cb(data, len); + if (hf_client_local_param.btc_hf_client_incoming_data_cb) { + hf_client_local_param.btc_hf_client_incoming_data_cb(data, len); } } uint32_t btc_hf_client_outgoing_data_cb_to_app(uint8_t *data, uint32_t len) { // todo: critical section protection - if (btc_hf_client_outgoing_data_cb) { - return btc_hf_client_outgoing_data_cb(data, len); + if (hf_client_local_param.btc_hf_client_outgoing_data_cb) { + return hf_client_local_param.btc_hf_client_outgoing_data_cb(data, len); } else { return 0; } @@ -172,7 +161,7 @@ bt_status_t btc_hf_client_init(void) clear_state(); - btc_hf_client_cb.initialized = true; + hf_client_local_param.btc_hf_client_cb.initialized = true; #if BTM_SCO_HCI_INCLUDED data_path = ESP_SCO_DATA_PATH_HCI; @@ -199,10 +188,10 @@ static bt_status_t connect_int( bt_bdaddr_t *bd_addr, uint16_t uuid ) return BT_STATUS_BUSY; } - btc_hf_client_cb.state = ESP_HF_CLIENT_CONNECTION_STATE_CONNECTING; - bdcpy(btc_hf_client_cb.connected_bda.address, bd_addr->address); + hf_client_local_param.btc_hf_client_cb.state = ESP_HF_CLIENT_CONNECTION_STATE_CONNECTING; + bdcpy(hf_client_local_param.btc_hf_client_cb.connected_bda.address, bd_addr->address); - BTA_HfClientOpen(btc_hf_client_cb.handle, btc_hf_client_cb.connected_bda.address, + BTA_HfClientOpen(hf_client_local_param.btc_hf_client_cb.handle, hf_client_local_param.btc_hf_client_cb.connected_bda.address, BTC_HF_CLIENT_SECURITY); return BT_STATUS_SUCCESS; @@ -232,7 +221,7 @@ void btc_hf_client_deinit( void ) btc_dm_disable_service(BTA_HFP_HS_SERVICE_ID); - btc_hf_client_cb.initialized = false; + hf_client_local_param.btc_hf_client_cb.initialized = false; } /******************************************************************************* @@ -250,7 +239,7 @@ bt_status_t btc_hf_client_disconnect( bt_bdaddr_t *bd_addr ) if (is_connected(bd_addr)) { - BTA_HfClientClose(btc_hf_client_cb.handle); + BTA_HfClientClose(hf_client_local_param.btc_hf_client_cb.handle); return BT_STATUS_SUCCESS; } @@ -272,13 +261,13 @@ bt_status_t btc_hf_client_connect_audio( bt_bdaddr_t *bd_addr ) if (is_connected(bd_addr)) { - if (btc_hf_client_cb.peer_feat & BTA_HF_CLIENT_PEER_CODEC) + if (hf_client_local_param.btc_hf_client_cb.peer_feat & BTA_HF_CLIENT_PEER_CODEC) { - BTA_HfClientSendAT(btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_BCC, 0, 0, NULL); + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_BCC, 0, 0, NULL); } else { - BTA_HfClientAudioOpen(btc_hf_client_cb.handle); + BTA_HfClientAudioOpen(hf_client_local_param.btc_hf_client_cb.handle); } /* Inform the application that the audio connection has been initiated successfully */ @@ -286,7 +275,7 @@ bt_status_t btc_hf_client_connect_audio( bt_bdaddr_t *bd_addr ) esp_hf_client_cb_param_t param; memset(¶m, 0, sizeof(esp_hf_client_cb_param_t)); param.audio_stat.state = ESP_HF_CLIENT_AUDIO_STATE_CONNECTING; - memcpy(param.audio_stat.remote_bda, &btc_hf_client_cb.connected_bda, sizeof(esp_bd_addr_t)); + memcpy(param.audio_stat.remote_bda, &hf_client_local_param.btc_hf_client_cb.connected_bda, sizeof(esp_bd_addr_t)); btc_hf_client_cb_to_app(ESP_HF_CLIENT_AUDIO_STATE_EVT, ¶m); } while (0); @@ -311,7 +300,7 @@ bt_status_t btc_hf_client_disconnect_audio( bt_bdaddr_t *bd_addr ) if (is_connected(bd_addr)) { - BTA_HfClientAudioClose(btc_hf_client_cb.handle); + BTA_HfClientAudioClose(hf_client_local_param.btc_hf_client_cb.handle); return BT_STATUS_SUCCESS; } @@ -331,9 +320,9 @@ static bt_status_t btc_hf_client_start_voice_recognition(void) { CHECK_HF_CLIENT_SLC_CONNECTED(); - if (btc_hf_client_cb.peer_feat & BTA_HF_CLIENT_PEER_FEAT_VREC) + if (hf_client_local_param.btc_hf_client_cb.peer_feat & BTA_HF_CLIENT_PEER_FEAT_VREC) { - BTA_HfClientSendAT(btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_BVRA, 1, 0, NULL); + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_BVRA, 1, 0, NULL); return BT_STATUS_SUCCESS; } @@ -355,9 +344,9 @@ static bt_status_t btc_hf_client_stop_voice_recognition(void) { CHECK_HF_CLIENT_SLC_CONNECTED(); - if (btc_hf_client_cb.peer_feat & BTA_HF_CLIENT_PEER_FEAT_VREC) + if (hf_client_local_param.btc_hf_client_cb.peer_feat & BTA_HF_CLIENT_PEER_FEAT_VREC) { - BTA_HfClientSendAT(btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_BVRA, 0, 0, NULL); + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_BVRA, 0, 0, NULL); return BT_STATUS_SUCCESS; } @@ -381,10 +370,10 @@ static bt_status_t btc_hf_client_volume_update(esp_hf_volume_control_target_t ty switch (type) { case ESP_HF_VOLUME_CONTROL_TARGET_SPK: - BTA_HfClientSendAT(btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_VGS, volume, 0, NULL); + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_VGS, volume, 0, NULL); break; case ESP_HF_VOLUME_CONTROL_TARGET_MIC: - BTA_HfClientSendAT(btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_VGM, volume, 0, NULL); + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_VGM, volume, 0, NULL); break; default: return BT_STATUS_UNSUPPORTED; @@ -408,11 +397,11 @@ static bt_status_t btc_hf_client_dial(const char *number) if (strlen(number) != 0) { - BTA_HfClientSendAT(btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_ATD, 0, 0, number); + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_ATD, 0, 0, number); } else { - BTA_HfClientSendAT(btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_BLDN, 0, 0, NULL); + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_BLDN, 0, 0, NULL); } return BT_STATUS_SUCCESS; @@ -431,7 +420,7 @@ static bt_status_t btc_hf_client_dial_memory(int location) { CHECK_HF_CLIENT_SLC_CONNECTED(); - BTA_HfClientSendAT(btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_ATD, location, 0, NULL); + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_ATD, location, 0, NULL); return BT_STATUS_SUCCESS; } @@ -443,61 +432,61 @@ static bt_status_t btc_hf_client_send_chld_cmd(esp_hf_chld_type_t type, int idx) switch (type) { case ESP_HF_CHLD_TYPE_REL: - if (btc_hf_client_cb.chld_feat & BTA_HF_CLIENT_CHLD_REL) + if (hf_client_local_param.btc_hf_client_cb.chld_feat & BTA_HF_CLIENT_CHLD_REL) { - BTA_HfClientSendAT(btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_CHLD, 0, 0, NULL); + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_CHLD, 0, 0, NULL); break; } return BT_STATUS_UNSUPPORTED; case ESP_HF_CHLD_TYPE_REL_ACC: // CHLD 1 is mandatory for 3 way calling - if (btc_hf_client_cb.peer_feat & BTA_HF_CLIENT_PEER_FEAT_3WAY) + if (hf_client_local_param.btc_hf_client_cb.peer_feat & BTA_HF_CLIENT_PEER_FEAT_3WAY) { - BTA_HfClientSendAT(btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_CHLD, 1, 0, NULL); + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_CHLD, 1, 0, NULL); break; } return BT_STATUS_UNSUPPORTED; case ESP_HF_CHLD_TYPE_HOLD_ACC: // CHLD 2 is mandatory for 3 way calling - if (btc_hf_client_cb.peer_feat & BTA_HF_CLIENT_PEER_FEAT_3WAY) + if (hf_client_local_param.btc_hf_client_cb.peer_feat & BTA_HF_CLIENT_PEER_FEAT_3WAY) { - BTA_HfClientSendAT(btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_CHLD, 2, 0, NULL); + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_CHLD, 2, 0, NULL); break; } return BT_STATUS_UNSUPPORTED; case ESP_HF_CHLD_TYPE_MERGE: - if (btc_hf_client_cb.chld_feat & BTA_HF_CLIENT_CHLD_MERGE) + if (hf_client_local_param.btc_hf_client_cb.chld_feat & BTA_HF_CLIENT_CHLD_MERGE) { - BTA_HfClientSendAT(btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_CHLD, 3, 0, NULL); + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_CHLD, 3, 0, NULL); break; } return BT_STATUS_UNSUPPORTED; case ESP_HF_CHLD_TYPE_MERGE_DETACH: - if (btc_hf_client_cb.chld_feat & BTA_HF_CLIENT_CHLD_MERGE_DETACH) + if (hf_client_local_param.btc_hf_client_cb.chld_feat & BTA_HF_CLIENT_CHLD_MERGE_DETACH) { - BTA_HfClientSendAT(btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_CHLD, 4, 0, NULL); + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_CHLD, 4, 0, NULL); break; } return BT_STATUS_UNSUPPORTED; case ESP_HF_CHLD_TYPE_REL_X: - if (btc_hf_client_cb.peer_feat & BTA_HF_CLIENT_PEER_ECC) + if (hf_client_local_param.btc_hf_client_cb.peer_feat & BTA_HF_CLIENT_PEER_ECC) { if (idx < 1) { return BT_STATUS_FAIL; } - BTA_HfClientSendAT(btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_CHLD, 1, idx, NULL); + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_CHLD, 1, idx, NULL); break; } return BT_STATUS_UNSUPPORTED; case ESP_HF_CHLD_TYPE_PRIV_X: - if (btc_hf_client_cb.peer_feat & BTA_HF_CLIENT_PEER_ECC) + if (hf_client_local_param.btc_hf_client_cb.peer_feat & BTA_HF_CLIENT_PEER_ECC) { if (idx < 1) { return BT_STATUS_FAIL; } - BTA_HfClientSendAT(btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_CHLD, 2, idx, NULL); + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_CHLD, 2, idx, NULL); break; } return BT_STATUS_UNSUPPORTED; @@ -512,13 +501,13 @@ static bt_status_t btc_hf_client_send_btrh_cmd(esp_hf_btrh_cmd_t btrh) switch (btrh) { case ESP_HF_BTRH_CMD_HOLD: - BTA_HfClientSendAT(btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_BTRH, 0, 0, NULL); + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_BTRH, 0, 0, NULL); break; case ESP_HF_BTRH_CMD_ACCEPT: - BTA_HfClientSendAT(btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_BTRH, 1, 0, NULL); + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_BTRH, 1, 0, NULL); break; case ESP_HF_BTRH_CMD_REJECT: - BTA_HfClientSendAT(btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_BTRH, 2, 0, NULL); + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_BTRH, 2, 0, NULL); break; default: return BT_STATUS_FAIL; @@ -530,14 +519,14 @@ static bt_status_t btc_hf_client_send_btrh_cmd(esp_hf_btrh_cmd_t btrh) static bt_status_t btc_hf_client_answer_call(void) { CHECK_HF_CLIENT_SLC_CONNECTED(); - BTA_HfClientSendAT(btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_ATA, 0, 0, NULL); + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_ATA, 0, 0, NULL); return BT_STATUS_SUCCESS; } static bt_status_t btc_hf_client_reject_call(void) { CHECK_HF_CLIENT_SLC_CONNECTED(); - BTA_HfClientSendAT(btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_CHUP, 0, 0, NULL); + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_CHUP, 0, 0, NULL); return BT_STATUS_SUCCESS; } @@ -554,9 +543,9 @@ static bt_status_t btc_hf_client_query_current_calls(void) { CHECK_HF_CLIENT_SLC_CONNECTED(); - if (btc_hf_client_cb.peer_feat & BTA_HF_CLIENT_PEER_ECS) + if (hf_client_local_param.btc_hf_client_cb.peer_feat & BTA_HF_CLIENT_PEER_ECS) { - BTA_HfClientSendAT(btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_CLCC, 0, 0, NULL); + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_CLCC, 0, 0, NULL); return BT_STATUS_SUCCESS; } @@ -577,7 +566,7 @@ static bt_status_t btc_hf_client_query_current_operator_name(void) { CHECK_HF_CLIENT_SLC_CONNECTED(); - BTA_HfClientSendAT(btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_COPS, 0, 0, NULL); + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_COPS, 0, 0, NULL); return BT_STATUS_SUCCESS; } @@ -595,7 +584,7 @@ static bt_status_t btc_hf_client_retrieve_subscriber_info(void) { CHECK_HF_CLIENT_SLC_CONNECTED(); - BTA_HfClientSendAT(btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_CNUM, 0, 0, NULL); + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_CNUM, 0, 0, NULL); return BT_STATUS_SUCCESS; } @@ -613,7 +602,7 @@ static bt_status_t btc_hf_client_send_dtmf(char code) { CHECK_HF_CLIENT_SLC_CONNECTED(); - BTA_HfClientSendAT(btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_VTS, code, 0, NULL); + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_VTS, code, 0, NULL); return BT_STATUS_SUCCESS; } @@ -631,9 +620,9 @@ static bt_status_t btc_hf_client_request_last_voice_tag_number(void) { CHECK_HF_CLIENT_SLC_CONNECTED(); - if (btc_hf_client_cb.peer_feat & BTA_HF_CLIENT_PEER_VTAG) + if (hf_client_local_param.btc_hf_client_cb.peer_feat & BTA_HF_CLIENT_PEER_VTAG) { - BTA_HfClientSendAT(btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_BINP, 1, 0, NULL); + BTA_HfClientSendAT(hf_client_local_param.btc_hf_client_cb.handle, BTA_HF_CLIENT_AT_CMD_BINP, 1, 0, NULL); return BT_STATUS_SUCCESS; } @@ -694,17 +683,17 @@ bt_status_t btc_hf_client_execute_service(BOOLEAN b_enable) else { BTC_TRACE_EVENT("No Codec Nego Supported"); - btc_hf_client_features = BTC_HF_CLIENT_FEATURES; - btc_hf_client_features = btc_hf_client_features & (~BTA_HF_CLIENT_FEAT_CODEC); - BTC_TRACE_EVENT("btc_hf_client_features is %d", btc_hf_client_features); - BTA_HfClientRegister(BTC_HF_CLIENT_SECURITY, btc_hf_client_features, + hf_client_local_param.btc_hf_client_features = BTC_HF_CLIENT_FEATURES; + hf_client_local_param.btc_hf_client_features = hf_client_local_param.btc_hf_client_features & (~BTA_HF_CLIENT_FEAT_CODEC); + BTC_TRACE_EVENT("hf_client_local_param.btc_hf_client_features is %d", hf_client_local_param.btc_hf_client_features); + BTA_HfClientRegister(BTC_HF_CLIENT_SECURITY, hf_client_local_param.btc_hf_client_features, BTC_HF_CLIENT_SERVICE_NAME); } } else { - BTA_HfClientDeregister(btc_hf_client_cb.handle); + BTA_HfClientDeregister(hf_client_local_param.btc_hf_client_cb.handle); BTA_HfClientDisable(); } return BT_STATUS_SUCCESS; @@ -769,44 +758,43 @@ void btc_hf_client_cb_handler(btc_msg_t *msg) case BTA_HF_CLIENT_DISABLE_EVT: break; case BTA_HF_CLIENT_REGISTER_EVT: - btc_hf_client_cb.handle = p_data->reg.handle; + hf_client_local_param.btc_hf_client_cb.handle = p_data->reg.handle; break; case BTA_HF_CLIENT_OPEN_EVT: if (p_data->open.status == BTA_HF_CLIENT_SUCCESS) { - bdcpy(btc_hf_client_cb.connected_bda.address, p_data->open.bd_addr); - btc_hf_client_cb.state = ESP_HF_CLIENT_CONNECTION_STATE_CONNECTED; - btc_hf_client_cb.peer_feat = 0; - btc_hf_client_cb.chld_feat = 0; + bdcpy(hf_client_local_param.btc_hf_client_cb.connected_bda.address, p_data->open.bd_addr); + hf_client_local_param.btc_hf_client_cb.state = ESP_HF_CLIENT_CONNECTION_STATE_CONNECTED; + hf_client_local_param.btc_hf_client_cb.peer_feat = 0; + hf_client_local_param.btc_hf_client_cb.chld_feat = 0; //clear_phone_state(); } - else if (btc_hf_client_cb.state == ESP_HF_CLIENT_CONNECTION_STATE_CONNECTING) + else if (hf_client_local_param.btc_hf_client_cb.state == ESP_HF_CLIENT_CONNECTION_STATE_CONNECTING) { - btc_hf_client_cb.state = ESP_HF_CLIENT_CONNECTION_STATE_DISCONNECTED; + hf_client_local_param.btc_hf_client_cb.state = ESP_HF_CLIENT_CONNECTION_STATE_DISCONNECTED; } else { BTC_TRACE_WARNING("%s: HF CLient open failed, but another device connected. status=%d state=%d connected device=%s", - __FUNCTION__, p_data->open.status, btc_hf_client_cb.state, bdaddr_to_string(&btc_hf_client_cb.connected_bda, bdstr, sizeof(bdstr))); + __FUNCTION__, p_data->open.status, hf_client_local_param.btc_hf_client_cb.state, bdaddr_to_string(&hf_client_local_param.btc_hf_client_cb.connected_bda, bdstr, sizeof(bdstr))); UNUSED(bdstr); break; } do { memset(¶m, 0, sizeof(esp_hf_client_cb_param_t)); - param.conn_stat.state = btc_hf_client_cb.state; + param.conn_stat.state = hf_client_local_param.btc_hf_client_cb.state; param.conn_stat.peer_feat = 0; param.conn_stat.chld_feat = 0; - memcpy(param.conn_stat.remote_bda, &btc_hf_client_cb.connected_bda, + memcpy(param.conn_stat.remote_bda, &hf_client_local_param.btc_hf_client_cb.connected_bda, sizeof(esp_bd_addr_t)); btc_hf_client_cb_to_app(ESP_HF_CLIENT_CONNECTION_STATE_EVT, ¶m); } while (0); - if (btc_hf_client_cb.state == ESP_HF_CLIENT_CONNECTION_STATE_DISCONNECTED) { - bdsetany(btc_hf_client_cb.connected_bda.address); - } + if (hf_client_local_param.btc_hf_client_cb.state == ESP_HF_CLIENT_CONNECTION_STATE_DISCONNECTED) + bdsetany(hf_client_local_param.btc_hf_client_cb.connected_bda.address); if (p_data->open.status != BTA_HF_CLIENT_SUCCESS) { btc_queue_advance(); @@ -815,24 +803,24 @@ void btc_hf_client_cb_handler(btc_msg_t *msg) break; case BTA_HF_CLIENT_CONN_EVT: - btc_hf_client_cb.peer_feat = p_data->conn.peer_feat; - btc_hf_client_cb.chld_feat = p_data->conn.chld_feat; - btc_hf_client_cb.state = ESP_HF_CLIENT_CONNECTION_STATE_SLC_CONNECTED; + hf_client_local_param.btc_hf_client_cb.peer_feat = p_data->conn.peer_feat; + hf_client_local_param.btc_hf_client_cb.chld_feat = p_data->conn.chld_feat; + hf_client_local_param.btc_hf_client_cb.state = ESP_HF_CLIENT_CONNECTION_STATE_SLC_CONNECTED; do { memset(¶m, 0, sizeof(esp_hf_client_cb_param_t)); - param.conn_stat.state = btc_hf_client_cb.state; - param.conn_stat.peer_feat = btc_hf_client_cb.peer_feat; - param.conn_stat.chld_feat = btc_hf_client_cb.chld_feat; + param.conn_stat.state = hf_client_local_param.btc_hf_client_cb.state; + param.conn_stat.peer_feat = hf_client_local_param.btc_hf_client_cb.peer_feat; + param.conn_stat.chld_feat = hf_client_local_param.btc_hf_client_cb.chld_feat; - memcpy(param.conn_stat.remote_bda, &btc_hf_client_cb.connected_bda, + memcpy(param.conn_stat.remote_bda, &hf_client_local_param.btc_hf_client_cb.connected_bda, sizeof(esp_bd_addr_t)); btc_hf_client_cb_to_app(ESP_HF_CLIENT_CONNECTION_STATE_EVT, ¶m); } while (0); /* Inform the application about in-band ringtone */ - if (btc_hf_client_cb.peer_feat & BTA_HF_CLIENT_PEER_INBAND) + if (hf_client_local_param.btc_hf_client_cb.peer_feat & BTA_HF_CLIENT_PEER_INBAND) { do { memset(¶m, 0, sizeof(esp_hf_client_cb_param_t)); @@ -845,22 +833,22 @@ void btc_hf_client_cb_handler(btc_msg_t *msg) break; case BTA_HF_CLIENT_CLOSE_EVT: - btc_hf_client_cb.state = ESP_HF_CLIENT_CONNECTION_STATE_DISCONNECTED; + hf_client_local_param.btc_hf_client_cb.state = ESP_HF_CLIENT_CONNECTION_STATE_DISCONNECTED; do { memset(¶m, 0, sizeof(esp_hf_client_cb_param_t)); param.conn_stat.state = ESP_HF_CLIENT_CONNECTION_STATE_DISCONNECTED; param.conn_stat.peer_feat = 0; param.conn_stat.chld_feat = 0; - memcpy(param.conn_stat.remote_bda, &btc_hf_client_cb.connected_bda, + memcpy(param.conn_stat.remote_bda, &hf_client_local_param.btc_hf_client_cb.connected_bda, sizeof(esp_bd_addr_t)); btc_hf_client_cb_to_app(ESP_HF_CLIENT_CONNECTION_STATE_EVT, ¶m); } while (0); - bdsetany(btc_hf_client_cb.connected_bda.address); - btc_hf_client_cb.peer_feat = 0; - btc_hf_client_cb.chld_feat = 0; + bdsetany(hf_client_local_param.btc_hf_client_cb.connected_bda.address); + hf_client_local_param.btc_hf_client_cb.peer_feat = 0; + hf_client_local_param.btc_hf_client_cb.chld_feat = 0; btc_queue_advance(); break; case BTA_HF_CLIENT_IND_EVT: @@ -983,7 +971,7 @@ void btc_hf_client_cb_handler(btc_msg_t *msg) do { memset(¶m, 0, sizeof(esp_hf_client_cb_param_t)); param.audio_stat.state = ESP_HF_CLIENT_AUDIO_STATE_CONNECTED; - memcpy(param.audio_stat.remote_bda, &btc_hf_client_cb.connected_bda, + memcpy(param.audio_stat.remote_bda, &hf_client_local_param.btc_hf_client_cb.connected_bda, sizeof(esp_bd_addr_t)); btc_hf_client_cb_to_app(ESP_HF_CLIENT_AUDIO_STATE_EVT, ¶m); } while (0); @@ -992,7 +980,7 @@ void btc_hf_client_cb_handler(btc_msg_t *msg) do { memset(¶m, 0, sizeof(esp_hf_client_cb_param_t)); param.audio_stat.state = ESP_HF_CLIENT_AUDIO_STATE_CONNECTED_MSBC; - memcpy(param.audio_stat.remote_bda, &btc_hf_client_cb.connected_bda, + memcpy(param.audio_stat.remote_bda, &hf_client_local_param.btc_hf_client_cb.connected_bda, sizeof(esp_bd_addr_t)); btc_hf_client_cb_to_app(ESP_HF_CLIENT_AUDIO_STATE_EVT, ¶m); } while (0); @@ -1001,7 +989,7 @@ void btc_hf_client_cb_handler(btc_msg_t *msg) do { memset(¶m, 0, sizeof(esp_hf_client_cb_param_t)); param.audio_stat.state = ESP_HF_CLIENT_AUDIO_STATE_DISCONNECTED; - memcpy(param.audio_stat.remote_bda, &btc_hf_client_cb.connected_bda, + memcpy(param.audio_stat.remote_bda, &hf_client_local_param.btc_hf_client_cb.connected_bda, sizeof(esp_bd_addr_t)); btc_hf_client_cb_to_app(ESP_HF_CLIENT_AUDIO_STATE_EVT, ¶m); } while (0); diff --git a/components/bt/bluedroid/btc/profile/std/include/btc_avrc.h b/components/bt/bluedroid/btc/profile/std/include/btc_avrc.h index 7eaabecc17..7d72393d82 100644 --- a/components/bt/bluedroid/btc/profile/std/include/btc_avrc.h +++ b/components/bt/bluedroid/btc/profile/std/include/btc_avrc.h @@ -86,6 +86,50 @@ typedef enum { BTC_AVRC_TG_API_SEND_RN_RSP_EVT, } btc_avrc_tg_act_t; +/***************************************************************************** +** Constants & Macros +******************************************************************************/ +/* for AVRC 1.4 need to change this */ +#define BTC_RC_CT_INIT_MAGIC 0x20181128 +#define BTC_RC_TG_INIT_MAGIC 0x20181129 + +#define MAX_RC_NOTIFICATIONS (13) // refer to ESP_AVRC_RN_MAX_EVT + + +#define CHECK_ESP_RC_CONNECTED do { \ + BTC_TRACE_DEBUG("## %s ##", __FUNCTION__); \ + if (btc_rc_vb.rc_connected == FALSE) { \ + BTC_TRACE_WARNING("Function %s() called when RC is not connected", __FUNCTION__); \ + return ESP_ERR_INVALID_STATE; \ + } \ + } while (0) + +/***************************************************************************** +** Local type definitions +******************************************************************************/ +typedef struct { + BOOLEAN registered; + UINT8 label; +} btc_rc_reg_ntf_t; + +typedef struct { + BOOLEAN rc_connected; + UINT8 rc_handle; + tBTA_AV_FEAT rc_features; + UINT16 rc_ct_features; + UINT16 rc_tg_features; + BD_ADDR rc_addr; + btc_rc_reg_ntf_t rc_ntf[MAX_RC_NOTIFICATIONS]; +} btc_rc_cb_t; + +/***************************************************************************** +** Static variables +******************************************************************************/ +#if AVRC_DYNAMIC_MEMORY == TRUE +extern btc_rc_cb_t *btc_rc_cb_ptr; +#define btc_rc_cb (*btc_rc_cb_ptr) +#endif ///AVRC_DYNAMIC_MEMORY == FALSE + typedef struct { esp_avrc_rn_event_ids_t event_id; esp_avrc_rn_rsp_t rsp; diff --git a/components/bt/bluedroid/btc/profile/std/include/btc_gap_ble.h b/components/bt/bluedroid/btc/profile/std/include/btc_gap_ble.h index be818269b2..b5e2effe7e 100644 --- a/components/bt/bluedroid/btc/profile/std/include/btc_gap_ble.h +++ b/components/bt/bluedroid/btc/profile/std/include/btc_gap_ble.h @@ -18,6 +18,14 @@ #include "esp_bt_defs.h" #include "esp_gap_ble_api.h" +#if BTC_DYNAMIC_MENDRY == TRUE +#include "bta/bta_api.h" +extern tBTA_BLE_ADV_DATA *gl_bta_adv_data_ptr; +extern tBTA_BLE_ADV_DATA *gl_bta_scan_rsp_data_ptr; +#define gl_bta_adv_data (*gl_bta_adv_data_ptr) +#define gl_bta_scan_rsp_data (*gl_bta_scan_rsp_data_ptr) +#endif + #define BLE_ISVALID_PARAM(x, min, max) (((x) >= (min) && (x) <= (max)) || ((x) == ESP_BLE_CONN_PARAM_UNDEF)) typedef enum { diff --git a/components/bt/bluedroid/btc/profile/std/include/btc_gatts.h b/components/bt/bluedroid/btc/profile/std/include/btc_gatts.h index cad973a8a6..5cf1e84161 100644 --- a/components/bt/bluedroid/btc/profile/std/include/btc_gatts.h +++ b/components/bt/bluedroid/btc/profile/std/include/btc_gatts.h @@ -19,6 +19,7 @@ #include "esp_bt_defs.h" #include "esp_gatt_defs.h" #include "esp_gatts_api.h" +#include "osi/future.h" typedef enum { BTC_GATTS_ACT_APP_REGISTER = 0, @@ -150,6 +151,21 @@ typedef union { } btc_ble_gatts_args_t; +typedef struct { + future_t *complete_future; + uint16_t svc_start_hdl; + esp_bt_uuid_t svc_uuid; + bool is_tab_creat_svc; + bool is_use_svc; + uint8_t num_handle; + uint8_t handle_idx; + uint16_t handles[ESP_GATT_ATTR_HANDLE_MAX]; +} esp_btc_creat_tab_t; + +#if GATT_DYNAMIC_MEMORY == TRUE +extern esp_btc_creat_tab_t *btc_creat_tab_env_ptr; +#define btc_creat_tab_env (*btc_creat_tab_env_ptr) +#endif void btc_gatts_call_handler(btc_msg_t *msg); void btc_gatts_cb_handler(btc_msg_t *msg); diff --git a/components/bt/bluedroid/btc/profile/std/include/btc_hf_client.h b/components/bt/bluedroid/btc/profile/std/include/btc_hf_client.h index 6500b9d878..04226e72a2 100644 --- a/components/bt/bluedroid/btc/profile/std/include/btc_hf_client.h +++ b/components/bt/bluedroid/btc/profile/std/include/btc_hf_client.h @@ -112,6 +112,34 @@ typedef union { } reg_data_cb; } btc_hf_client_args_t; +/************************************************************************************ +** Local type definitions +************************************************************************************/ +/* BTC-HF control block to map bdaddr to BTA handle */ +typedef struct +{ + bool initialized; + UINT16 handle; + bt_bdaddr_t connected_bda; + esp_hf_client_connection_state_t state; + esp_hf_vr_state_t vr_state; + tBTA_HF_CLIENT_PEER_FEAT peer_feat; + tBTA_HF_CLIENT_CHLD_FEAT chld_feat; +} btc_hf_client_cb_t; + +typedef struct +{ + UINT32 btc_hf_client_features; + btc_hf_client_cb_t btc_hf_client_cb; + esp_hf_client_incoming_data_cb_t btc_hf_client_incoming_data_cb; + esp_hf_client_outgoing_data_cb_t btc_hf_client_outgoing_data_cb; +}hf_client_local_param_t; + +#if HFP_DYNAMIC_MEMORY == TRUE +extern hf_client_local_param_t *hf_client_local_param_ptr; +#define hf_client_local_param (*hf_client_local_param_ptr) +#endif + /******************************************************************************* ** BTC HF AG API ********************************************************************************/ diff --git a/components/bt/bluedroid/btc/profile/std/spp/btc_spp.c b/components/bt/bluedroid/btc/profile/std/spp/btc_spp.c index bc2d62427d..93636ef71c 100644 --- a/components/bt/bluedroid/btc/profile/std/spp/btc_spp.c +++ b/components/bt/bluedroid/btc/profile/std/spp/btc_spp.c @@ -55,13 +55,20 @@ typedef struct { char service_name[ESP_SPP_SERVER_NAME_MAX + 1]; } spp_slot_t; -static struct spp_local_param_t { +typedef struct { spp_slot_t *spp_slots[BTA_JV_MAX_RFC_SR_SESSION + 1]; uint32_t spp_slot_id; esp_spp_mode_t spp_mode; osi_mutex_t spp_slot_mutex; esp_vfs_id_t spp_vfs_id; -} spp_local_param; +} spp_local_param_t; + +#if SPP_DYNAMIC_MEMORY == FALSE +static spp_local_param_t spp_local_param; +#else +static spp_local_param_t *spp_local_param_ptr; +#define spp_local_param (*spp_local_param_ptr) +#endif static void spp_osi_free(void *p) { @@ -313,6 +320,15 @@ static void btc_spp_dm_inter_cb(tBTA_JV_EVT event, tBTA_JV *p_data, void *user_d static void btc_spp_init(btc_spp_args_t *arg) { + +#if SPP_DYNAMIC_MEMORY == TRUE + if ((spp_local_param_ptr = (spp_local_param_t *)osi_malloc(sizeof(spp_local_param_t))) == NULL) { + BTC_TRACE_ERROR("%s malloc failed\n", __func__); + return; + } + memset((void *)spp_local_param_ptr, 0, sizeof(spp_local_param_t)); +#endif + if (osi_mutex_new(&spp_local_param.spp_slot_mutex) != 0) { BTC_TRACE_ERROR("%s osi_mutex_new failed\n", __func__); return; @@ -349,6 +365,11 @@ static void btc_spp_uninit(void) BTA_JvDisable(); osi_mutex_unlock(&spp_local_param.spp_slot_mutex); osi_mutex_free(&spp_local_param.spp_slot_mutex); + +#if SPP_DYNAMIC_MEMORY == TRUE + osi_free(spp_local_param_ptr); + spp_local_param_ptr = NULL; +#endif } static void btc_spp_start_discovery(btc_spp_args_t *arg) diff --git a/components/bt/bluedroid/common/include/common/bt_target.h b/components/bt/bluedroid/common/include/common/bt_target.h index b781d435ac..c8f15e8694 100644 --- a/components/bt/bluedroid/common/include/common/bt_target.h +++ b/components/bt/bluedroid/common/include/common/bt_target.h @@ -2049,12 +2049,6 @@ The maximum number of payload octets that the local device can receive in a sing #define HEAP_ALLOCATION_FROM_SPIRAM_FIRST FALSE #endif -#if UC_BT_BLE_DYNAMIC_ENV_MEMORY -#define BT_BLE_DYNAMIC_ENV_MEMORY TRUE -#else -#define BT_BLE_DYNAMIC_ENV_MEMORY FALSE -#endif - #include "common/bt_trace.h" #endif /* BT_TARGET_H */ diff --git a/components/bt/bluedroid/device/controller.c b/components/bt/bluedroid/device/controller.c index 45ef4ea504..df6c5018f6 100644 --- a/components/bt/bluedroid/device/controller.c +++ b/components/bt/bluedroid/device/controller.c @@ -46,38 +46,47 @@ const uint8_t SCO_HOST_BUFFER_SIZE = 0xff; #define BLE_SUPPORTED_STATES_SIZE 8 #define BLE_SUPPORTED_FEATURES_SIZE 8 -static const hci_t *hci; -static const hci_packet_factory_t *packet_factory; -static const hci_packet_parser_t *packet_parser; +typedef struct { + const hci_t *hci; + const hci_packet_factory_t *packet_factory; + const hci_packet_parser_t *packet_parser; -static bt_bdaddr_t address; -static bt_version_t bt_version; + bt_version_t bt_version; + bt_bdaddr_t address; -static uint8_t supported_commands[HCI_SUPPORTED_COMMANDS_ARRAY_SIZE]; -static bt_device_features_t features_classic[MAX_FEATURES_CLASSIC_PAGE_COUNT]; -static uint8_t last_features_classic_page_index; + uint8_t supported_commands[HCI_SUPPORTED_COMMANDS_ARRAY_SIZE]; + uint8_t last_features_classic_page_index; + bt_device_features_t features_classic[MAX_FEATURES_CLASSIC_PAGE_COUNT]; -static uint16_t acl_data_size_classic; -static uint16_t acl_data_size_ble; -static uint16_t acl_buffer_count_classic; -static uint8_t acl_buffer_count_ble; + uint16_t acl_data_size_classic; + uint16_t acl_data_size_ble; + uint16_t acl_buffer_count_classic; + uint8_t acl_buffer_count_ble; -static uint8_t sco_data_size; -static uint16_t sco_buffer_count; + uint8_t sco_data_size; + uint16_t sco_buffer_count; -static uint8_t ble_white_list_size; -static uint8_t ble_resolving_list_max_size; -static uint8_t ble_supported_states[BLE_SUPPORTED_STATES_SIZE]; -static bt_device_features_t features_ble; -static uint16_t ble_suggested_default_data_length; -static uint16_t ble_suggested_default_data_txtime; + uint8_t ble_white_list_size; + uint8_t ble_resolving_list_max_size; + uint8_t ble_supported_states[BLE_SUPPORTED_STATES_SIZE]; + bt_device_features_t features_ble; + uint16_t ble_suggested_default_data_length; + uint16_t ble_suggested_default_data_txtime; -static bool readable; -static bool ble_supported; -static bool simple_pairing_supported; -static bool secure_connections_supported; + bool readable; + bool ble_supported; + bool simple_pairing_supported; + bool secure_connections_supported; +} controller_local_param_t; -#define AWAIT_COMMAND(command) future_await(hci->transmit_command_futured(command)) +#if BT_BLE_DYNAMIC_ENV_MEMORY == FALSE +static controller_local_param_t controller_param; +#else +static controller_local_param_t *controller_param_ptr; +#define controller_param (*controller_param_ptr) +#endif + +#define AWAIT_COMMAND(command) future_await(controller_param.hci->transmit_command_futured(command)) // Module lifecycle functions @@ -86,19 +95,19 @@ static void start_up(void) BT_HDR *response; // Send the initial reset command - response = AWAIT_COMMAND(packet_factory->make_reset()); - packet_parser->parse_generic_command_complete(response); + response = AWAIT_COMMAND(controller_param.packet_factory->make_reset()); + controller_param.packet_parser->parse_generic_command_complete(response); // Request the classic buffer size next - response = AWAIT_COMMAND(packet_factory->make_read_buffer_size()); - packet_parser->parse_read_buffer_size_response( - response, &acl_data_size_classic, &acl_buffer_count_classic, - &sco_data_size, &sco_buffer_count); + response = AWAIT_COMMAND(controller_param.packet_factory->make_read_buffer_size()); + controller_param.packet_parser->parse_read_buffer_size_response( + response, &controller_param.acl_data_size_classic, &controller_param.acl_buffer_count_classic, + &controller_param.sco_data_size, &controller_param.sco_buffer_count); #if (C2H_FLOW_CONTROL_INCLUDED == TRUE) // Enable controller to host flow control - response = AWAIT_COMMAND(packet_factory->make_set_c2h_flow_control(HCI_HOST_FLOW_CTRL_ACL_ON)); - packet_parser->parse_generic_command_complete(response); + response = AWAIT_COMMAND(controller_param.packet_factory->make_set_c2h_flow_control(HCI_HOST_FLOW_CTRL_ACL_ON)); + controller_param.packet_parser->parse_generic_command_complete(response); #endif ///C2H_FLOW_CONTROL_INCLUDED == TRUE #if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE) // Enable adv flow control @@ -108,7 +117,7 @@ static void start_up(void) // Tell the controller about our buffer sizes and buffer counts next // TODO(zachoverflow): factor this out. eww l2cap contamination. And why just a hardcoded 10? response = AWAIT_COMMAND( - packet_factory->make_host_buffer_size( + controller_param.packet_factory->make_host_buffer_size( L2CAP_MTU_SIZE, SCO_HOST_BUFFER_SIZE, L2CAP_HOST_FC_ACL_BUFS, @@ -116,33 +125,33 @@ static void start_up(void) ) ); - packet_parser->parse_generic_command_complete(response); + controller_param.packet_parser->parse_generic_command_complete(response); // Read the local version info off the controller next, including // information such as manufacturer and supported HCI version - response = AWAIT_COMMAND(packet_factory->make_read_local_version_info()); - packet_parser->parse_read_local_version_info_response(response, &bt_version); + response = AWAIT_COMMAND(controller_param.packet_factory->make_read_local_version_info()); + controller_param.packet_parser->parse_read_local_version_info_response(response, &controller_param.bt_version); // Read the bluetooth address off the controller next - response = AWAIT_COMMAND(packet_factory->make_read_bd_addr()); - packet_parser->parse_read_bd_addr_response(response, &address); + response = AWAIT_COMMAND(controller_param.packet_factory->make_read_bd_addr()); + controller_param.packet_parser->parse_read_bd_addr_response(response, &controller_param.address); // Request the controller's supported commands next - response = AWAIT_COMMAND(packet_factory->make_read_local_supported_commands()); - packet_parser->parse_read_local_supported_commands_response( + response = AWAIT_COMMAND(controller_param.packet_factory->make_read_local_supported_commands()); + controller_param.packet_parser->parse_read_local_supported_commands_response( response, - supported_commands, + controller_param.supported_commands, HCI_SUPPORTED_COMMANDS_ARRAY_SIZE ); // Read page 0 of the controller features next uint8_t page_number = 0; - response = AWAIT_COMMAND(packet_factory->make_read_local_extended_features(page_number)); - packet_parser->parse_read_local_extended_features_response( + response = AWAIT_COMMAND(controller_param.packet_factory->make_read_local_extended_features(page_number)); + controller_param.packet_parser->parse_read_local_extended_features_response( response, &page_number, - &last_features_classic_page_index, - features_classic, + &controller_param.last_features_classic_page_index, + controller_param.features_classic, MAX_FEATURES_CLASSIC_PAGE_COUNT ); @@ -154,36 +163,37 @@ static void start_up(void) // next page, because the controller's response for page 1 may be // dependent on what we configure from page 0 #if (BT_SSP_INCLUDED == TRUE) - simple_pairing_supported = HCI_SIMPLE_PAIRING_SUPPORTED(features_classic[0].as_array); + controller_param.simple_pairing_supported = HCI_SIMPLE_PAIRING_SUPPORTED(controller_param.features_classic[0].as_array); #else - simple_pairing_supported = false; + controller_param.simple_pairing_supported = false; #endif - if (simple_pairing_supported) { - response = AWAIT_COMMAND(packet_factory->make_write_simple_pairing_mode(HCI_SP_MODE_ENABLED)); - packet_parser->parse_generic_command_complete(response); + + if (controller_param.simple_pairing_supported) { + response = AWAIT_COMMAND(controller_param.packet_factory->make_write_simple_pairing_mode(HCI_SP_MODE_ENABLED)); + controller_param.packet_parser->parse_generic_command_complete(response); } #if (BLE_INCLUDED == TRUE) - if (HCI_LE_SPT_SUPPORTED(features_classic[0].as_array)) { - uint8_t simultaneous_le_host = HCI_SIMUL_LE_BREDR_SUPPORTED(features_classic[0].as_array) ? BTM_BLE_SIMULTANEOUS_HOST : 0; + if (HCI_LE_SPT_SUPPORTED(controller_param.features_classic[0].as_array)) { + uint8_t simultaneous_le_host = HCI_SIMUL_LE_BREDR_SUPPORTED(controller_param.features_classic[0].as_array) ? BTM_BLE_SIMULTANEOUS_HOST : 0; response = AWAIT_COMMAND( - packet_factory->make_ble_write_host_support(BTM_BLE_HOST_SUPPORT, simultaneous_le_host) + controller_param.packet_factory->make_ble_write_host_support(BTM_BLE_HOST_SUPPORT, simultaneous_le_host) ); - packet_parser->parse_generic_command_complete(response); + controller_param.packet_parser->parse_generic_command_complete(response); } #endif // Done telling the controller about what page 0 features we support // Request the remaining feature pages - while (page_number <= last_features_classic_page_index && + while (page_number <= controller_param.last_features_classic_page_index && page_number < MAX_FEATURES_CLASSIC_PAGE_COUNT) { - response = AWAIT_COMMAND(packet_factory->make_read_local_extended_features(page_number)); - packet_parser->parse_read_local_extended_features_response( + response = AWAIT_COMMAND(controller_param.packet_factory->make_read_local_extended_features(page_number)); + controller_param.packet_parser->parse_read_local_extended_features_response( response, &page_number, - &last_features_classic_page_index, - features_classic, + &controller_param.last_features_classic_page_index, + controller_param.features_classic, MAX_FEATURES_CLASSIC_PAGE_COUNT ); @@ -191,299 +201,297 @@ static void start_up(void) } #if (SC_MODE_INCLUDED == TRUE) - secure_connections_supported = HCI_SC_CTRLR_SUPPORTED(features_classic[2].as_array); - if (secure_connections_supported) { - response = AWAIT_COMMAND(packet_factory->make_write_secure_connections_host_support(HCI_SC_MODE_ENABLED)); - packet_parser->parse_generic_command_complete(response); + controller_param.secure_connections_supported = HCI_SC_CTRLR_SUPPORTED(controller_param.features_classic[2].as_array); + if (controller_param.secure_connections_supported) { + response = AWAIT_COMMAND(controller_param.packet_factory->make_write_secure_connections_host_support(HCI_SC_MODE_ENABLED)); + controller_param.packet_parser->parse_generic_command_complete(response); } #endif #if (BLE_INCLUDED == TRUE) - ble_supported = last_features_classic_page_index >= 1 && HCI_LE_HOST_SUPPORTED(features_classic[1].as_array); - if (ble_supported) { + controller_param.ble_supported = controller_param.last_features_classic_page_index >= 1 && HCI_LE_HOST_SUPPORTED(controller_param.features_classic[1].as_array); + if (controller_param.ble_supported) { // Request the ble white list size next - response = AWAIT_COMMAND(packet_factory->make_ble_read_white_list_size()); - packet_parser->parse_ble_read_white_list_size_response(response, &ble_white_list_size); + response = AWAIT_COMMAND(controller_param.packet_factory->make_ble_read_white_list_size()); + controller_param.packet_parser->parse_ble_read_white_list_size_response(response, &controller_param.ble_white_list_size); // Request the ble buffer size next - response = AWAIT_COMMAND(packet_factory->make_ble_read_buffer_size()); - packet_parser->parse_ble_read_buffer_size_response( + response = AWAIT_COMMAND(controller_param.packet_factory->make_ble_read_buffer_size()); + controller_param.packet_parser->parse_ble_read_buffer_size_response( response, - &acl_data_size_ble, - &acl_buffer_count_ble + &controller_param.acl_data_size_ble, + &controller_param.acl_buffer_count_ble ); // Response of 0 indicates ble has the same buffer size as classic - if (acl_data_size_ble == 0) { - acl_data_size_ble = acl_data_size_classic; + if (controller_param.acl_data_size_ble == 0) { + controller_param.acl_data_size_ble = controller_param.acl_data_size_classic; } // Request the ble supported states next - response = AWAIT_COMMAND(packet_factory->make_ble_read_supported_states()); - packet_parser->parse_ble_read_supported_states_response( + response = AWAIT_COMMAND(controller_param.packet_factory->make_ble_read_supported_states()); + controller_param.packet_parser->parse_ble_read_supported_states_response( response, - ble_supported_states, - sizeof(ble_supported_states) + controller_param.ble_supported_states, + sizeof(controller_param.ble_supported_states) ); // Request the ble supported features next - response = AWAIT_COMMAND(packet_factory->make_ble_read_local_supported_features()); - packet_parser->parse_ble_read_local_supported_features_response( + response = AWAIT_COMMAND(controller_param.packet_factory->make_ble_read_local_supported_features()); + controller_param.packet_parser->parse_ble_read_local_supported_features_response( response, - &features_ble + &controller_param.features_ble ); - if (HCI_LE_ENHANCED_PRIVACY_SUPPORTED(features_ble.as_array)) { - response = AWAIT_COMMAND(packet_factory->make_ble_read_resolving_list_size()); - packet_parser->parse_ble_read_resolving_list_size_response( + if (HCI_LE_ENHANCED_PRIVACY_SUPPORTED(controller_param.features_ble.as_array)) { + response = AWAIT_COMMAND(controller_param.packet_factory->make_ble_read_resolving_list_size()); + controller_param.packet_parser->parse_ble_read_resolving_list_size_response( response, - &ble_resolving_list_max_size); + &controller_param.ble_resolving_list_max_size); } - if (HCI_LE_DATA_LEN_EXT_SUPPORTED(features_ble.as_array)) { + if (HCI_LE_DATA_LEN_EXT_SUPPORTED(controller_param.features_ble.as_array)) { /* set default tx data length to MAX 251 */ - response = AWAIT_COMMAND(packet_factory->make_ble_write_suggested_default_data_length(BTM_BLE_DATA_SIZE_MAX, BTM_BLE_DATA_TX_TIME_MAX)); - packet_parser->parse_generic_command_complete(response); + response = AWAIT_COMMAND(controller_param.packet_factory->make_ble_write_suggested_default_data_length(BTM_BLE_DATA_SIZE_MAX, BTM_BLE_DATA_TX_TIME_MAX)); + controller_param.packet_parser->parse_generic_command_complete(response); - response = AWAIT_COMMAND(packet_factory->make_ble_read_suggested_default_data_length()); - packet_parser->parse_ble_read_suggested_default_data_length_response( + response = AWAIT_COMMAND(controller_param.packet_factory->make_ble_read_suggested_default_data_length()); + controller_param.packet_parser->parse_ble_read_suggested_default_data_length_response( response, - &ble_suggested_default_data_length, - &ble_suggested_default_data_txtime); + &controller_param.ble_suggested_default_data_length, + &controller_param.ble_suggested_default_data_txtime); } // Set the ble event mask next - response = AWAIT_COMMAND(packet_factory->make_ble_set_event_mask(&BLE_EVENT_MASK)); - packet_parser->parse_generic_command_complete(response); + response = AWAIT_COMMAND(controller_param.packet_factory->make_ble_set_event_mask(&BLE_EVENT_MASK)); + controller_param.packet_parser->parse_generic_command_complete(response); } #endif - - response = AWAIT_COMMAND(packet_factory->make_set_event_mask(&CLASSIC_EVENT_MASK)); - packet_parser->parse_generic_command_complete(response); - + response = AWAIT_COMMAND(controller_param.packet_factory->make_set_event_mask(&CLASSIC_EVENT_MASK)); + controller_param.packet_parser->parse_generic_command_complete(response); #if (BTM_SCO_HCI_INCLUDED == TRUE) - response = AWAIT_COMMAND(packet_factory->make_write_sync_flow_control_enable(1)); - packet_parser->parse_generic_command_complete(response); + response = AWAIT_COMMAND(controller_param.packet_factory->make_write_sync_flow_control_enable(1)); + controller_param.packet_parser->parse_generic_command_complete(response); - response = AWAIT_COMMAND(packet_factory->make_write_default_erroneous_data_report(1)); - packet_parser->parse_generic_command_complete(response); + response = AWAIT_COMMAND(controller_param.packet_factory->make_write_default_erroneous_data_report(1)); + controller_param.packet_parser->parse_generic_command_complete(response); #endif - readable = true; + controller_param.readable = true; // return future_new_immediate(FUTURE_SUCCESS); return; } static void shut_down(void) { - readable = false; + controller_param.readable = false; } static bool get_is_ready(void) { - return readable; + return controller_param.readable; } static const bt_bdaddr_t *get_address(void) { - assert(readable); - return &address; + assert(controller_param.readable); + return &controller_param.address; } static const bt_version_t *get_bt_version(void) { - assert(readable); - return &bt_version; + assert(controller_param.readable); + return &controller_param.bt_version; } // TODO(zachoverflow): hide inside, move decoder inside too static const bt_device_features_t *get_features_classic(int index) { - assert(readable); + assert(controller_param.readable); assert(index < MAX_FEATURES_CLASSIC_PAGE_COUNT); - return &features_classic[index]; + return &controller_param.features_classic[index]; } static uint8_t get_last_features_classic_index(void) { - assert(readable); - return last_features_classic_page_index; + assert(controller_param.readable); + return controller_param.last_features_classic_page_index; } static const bt_device_features_t *get_features_ble(void) { - assert(readable); - assert(ble_supported); - return &features_ble; + assert(controller_param.readable); + assert(controller_param.ble_supported); + return &controller_param.features_ble; } static const uint8_t *get_ble_supported_states(void) { - assert(readable); - assert(ble_supported); - return ble_supported_states; + assert(controller_param.readable); + assert(controller_param.ble_supported); + return controller_param.ble_supported_states; } static bool supports_simple_pairing(void) { - assert(readable); - return simple_pairing_supported; + assert(controller_param.readable); + return controller_param.simple_pairing_supported; } static bool supports_secure_connections(void) { - assert(readable); - return secure_connections_supported; + assert(controller_param.readable); + return controller_param.secure_connections_supported; } static bool supports_simultaneous_le_bredr(void) { - assert(readable); - return HCI_SIMUL_LE_BREDR_SUPPORTED(features_classic[0].as_array); + assert(controller_param.readable); + return HCI_SIMUL_LE_BREDR_SUPPORTED(controller_param.features_classic[0].as_array); } static bool supports_reading_remote_extended_features(void) { - assert(readable); - return HCI_READ_REMOTE_EXT_FEATURES_SUPPORTED(supported_commands); + assert(controller_param.readable); + return HCI_READ_REMOTE_EXT_FEATURES_SUPPORTED(controller_param.supported_commands); } static bool supports_interlaced_inquiry_scan(void) { - assert(readable); - return HCI_LMP_INTERLACED_INQ_SCAN_SUPPORTED(features_classic[0].as_array); + assert(controller_param.readable); + return HCI_LMP_INTERLACED_INQ_SCAN_SUPPORTED(controller_param.features_classic[0].as_array); } static bool supports_rssi_with_inquiry_results(void) { - assert(readable); - return HCI_LMP_INQ_RSSI_SUPPORTED(features_classic[0].as_array); + assert(controller_param.readable); + return HCI_LMP_INQ_RSSI_SUPPORTED(controller_param.features_classic[0].as_array); } static bool supports_extended_inquiry_response(void) { - assert(readable); - return HCI_EXT_INQ_RSP_SUPPORTED(features_classic[0].as_array); + assert(controller_param.readable); + return HCI_EXT_INQ_RSP_SUPPORTED(controller_param.features_classic[0].as_array); } static bool supports_master_slave_role_switch(void) { - assert(readable); - return HCI_SWITCH_SUPPORTED(features_classic[0].as_array); + assert(controller_param.readable); + return HCI_SWITCH_SUPPORTED(controller_param.features_classic[0].as_array); } static bool supports_ble(void) { - assert(readable); - return ble_supported; + assert(controller_param.readable); + return controller_param.ble_supported; } static bool supports_ble_privacy(void) { - assert(readable); - assert(ble_supported); - return HCI_LE_ENHANCED_PRIVACY_SUPPORTED(features_ble.as_array); + assert(controller_param.readable); + assert(controller_param.ble_supported); + return HCI_LE_ENHANCED_PRIVACY_SUPPORTED(controller_param.features_ble.as_array); } static bool supports_ble_packet_extension(void) { - assert(readable); - assert(ble_supported); - return HCI_LE_DATA_LEN_EXT_SUPPORTED(features_ble.as_array); + assert(controller_param.readable); + assert(controller_param.ble_supported); + return HCI_LE_DATA_LEN_EXT_SUPPORTED(controller_param.features_ble.as_array); } static bool supports_ble_connection_parameters_request(void) { - assert(readable); - assert(ble_supported); - return HCI_LE_CONN_PARAM_REQ_SUPPORTED(features_ble.as_array); + assert(controller_param.readable); + assert(controller_param.ble_supported); + return HCI_LE_CONN_PARAM_REQ_SUPPORTED(controller_param.features_ble.as_array); } static uint16_t get_acl_data_size_classic(void) { - assert(readable); - return acl_data_size_classic; + assert(controller_param.readable); + return controller_param.acl_data_size_classic; } static uint16_t get_acl_data_size_ble(void) { - assert(readable); - assert(ble_supported); - return acl_data_size_ble; + assert(controller_param.readable); + assert(controller_param.ble_supported); + return controller_param.acl_data_size_ble; } static uint16_t get_acl_packet_size_classic(void) { - assert(readable); - return acl_data_size_classic + HCI_DATA_PREAMBLE_SIZE; + assert(controller_param.readable); + return controller_param.acl_data_size_classic + HCI_DATA_PREAMBLE_SIZE; } static uint16_t get_acl_packet_size_ble(void) { - assert(readable); - return acl_data_size_ble + HCI_DATA_PREAMBLE_SIZE; + assert(controller_param.readable); + return controller_param.acl_data_size_ble + HCI_DATA_PREAMBLE_SIZE; } static uint16_t get_ble_suggested_default_data_length(void) { - assert(readable); - assert(ble_supported); - return ble_suggested_default_data_length; + assert(controller_param.readable); + assert(controller_param.ble_supported); + return controller_param.ble_suggested_default_data_length; } static uint16_t get_ble_suggested_default_data_txtime(void) { - assert(readable); - assert(ble_supported); - return ble_suggested_default_data_txtime; + assert(controller_param.readable); + assert(controller_param.ble_supported); + return controller_param.ble_suggested_default_data_txtime; } static uint16_t get_acl_buffer_count_classic(void) { - assert(readable); - return acl_buffer_count_classic; + assert(controller_param.readable); + return controller_param.acl_buffer_count_classic; } static uint8_t get_acl_buffer_count_ble(void) { - assert(readable); - assert(ble_supported); - return acl_buffer_count_ble; + assert(controller_param.readable); + assert(controller_param.ble_supported); + return controller_param.acl_buffer_count_ble; } static uint8_t get_ble_white_list_size(void) { - assert(readable); - assert(ble_supported); - return ble_white_list_size; + assert(controller_param.readable); + assert(controller_param.ble_supported); + return controller_param.ble_white_list_size; } static uint8_t get_ble_resolving_list_max_size(void) { - assert(readable); - assert(ble_supported); - return ble_resolving_list_max_size; + assert(controller_param.readable); + assert(controller_param.ble_supported); + return controller_param.ble_resolving_list_max_size; } static void set_ble_resolving_list_max_size(int resolving_list_max_size) { - assert(readable); - assert(ble_supported); - ble_resolving_list_max_size = resolving_list_max_size; + assert(controller_param.readable); + assert(controller_param.ble_supported); + controller_param.ble_resolving_list_max_size = resolving_list_max_size; } #if (BTM_SCO_HCI_INCLUDED == TRUE) static uint8_t get_sco_data_size(void) { - assert(readable); - return sco_data_size; + assert(controller_param.readable); + return controller_param.sco_data_size; } static uint8_t get_sco_buffer_count(void) { - assert(readable); - return sco_buffer_count; + assert(controller_param.readable); + return controller_param.sco_buffer_count; } #endif /* (BTM_SCO_HCI_INCLUDED == TRUE) */ @@ -541,10 +549,13 @@ const controller_t *controller_get_interface() static bool loaded = false; if (!loaded) { loaded = true; - - hci = hci_layer_get_interface(); - packet_factory = hci_packet_factory_get_interface(); - packet_parser = hci_packet_parser_get_interface(); +#if BT_BLE_DYNAMIC_ENV_MEMORY == TRUE + controller_param_ptr = (controller_local_param_t *)osi_calloc(sizeof(controller_local_param_t)); + assert(controller_param_ptr); +#endif + controller_param.hci = hci_layer_get_interface(); + controller_param.packet_factory = hci_packet_factory_get_interface(); + controller_param.packet_parser = hci_packet_parser_get_interface(); } return &interface; diff --git a/components/bt/bluedroid/external/sbc/encoder/srce/sbc_analysis.c b/components/bt/bluedroid/external/sbc/encoder/srce/sbc_analysis.c index 12878f3156..a2d6db43c8 100644 --- a/components/bt/bluedroid/external/sbc/encoder/srce/sbc_analysis.c +++ b/components/bt/bluedroid/external/sbc/encoder/srce/sbc_analysis.c @@ -26,6 +26,7 @@ #include #include "sbc_encoder.h" #include "sbc_enc_func_declare.h" +#include "osi/allocator.h" /*#include */ #if (defined(SBC_ENC_INCLUDED) && SBC_ENC_INCLUDED == TRUE) @@ -158,9 +159,16 @@ #if (SBC_USE_ARM_PRAGMA==TRUE) #pragma arm section zidata = "sbc_s32_analysis_section" #endif +#if BT_BLE_DYNAMIC_ENV_MEMORY == FALSE static SINT32 s32DCTY[16] = {0}; static SINT32 s32X[ENC_VX_BUFFER_SIZE / 2]; static SINT16 *s16X = (SINT16 *) s32X; /* s16X must be 32 bits aligned cf SHIFTUP_X8_2*/ +#else +static SINT32 *s32DCTY; +static SINT32 *s32X; +static SINT16 *s16X; /* s16X must be 32 bits aligned cf SHIFTUP_X8_2*/ +#endif //BT_BLE_DYNAMIC_ENV_MEMORY == FALSE + #if (SBC_USE_ARM_PRAGMA==TRUE) #pragma arm section zidata #endif @@ -1076,6 +1084,19 @@ void SbcAnalysisFilter8 (SBC_ENC_PARAMS *pstrEncParams) void SbcAnalysisInit (void) { + static bool loaded = false; + if (!loaded) { + loaded = true; +#if BT_BLE_DYNAMIC_ENV_MEMORY == TRUE + s32X = (SINT32 *)osi_malloc(sizeof(SINT32) * (ENC_VX_BUFFER_SIZE / 2)); + s32DCTY = (SINT32 *)osi_malloc(sizeof(SINT32) * 16); + assert(s32X); + assert(s32DCTY); + memset(s32X, 0, sizeof(SINT16) * ENC_VX_BUFFER_SIZE); + memset(s32DCTY, 0, sizeof(SINT32) * 16); + s16X = (SINT16 *) s32X; +#endif + } memset(s16X, 0, ENC_VX_BUFFER_SIZE * sizeof(SINT16)); ShiftCounter = 0; } diff --git a/components/bt/bluedroid/main/bte_init.c b/components/bt/bluedroid/main/bte_init.c index 38cfc812fb..1d6e994291 100644 --- a/components/bt/bluedroid/main/bte_init.c +++ b/components/bt/bluedroid/main/bte_init.c @@ -116,7 +116,6 @@ #if BTA_JV_INCLUDED==TRUE #include "bta_jv_int.h" -tBTA_JV_CB *bta_jv_cb_ptr = NULL; #endif #if BTA_HL_INCLUDED == TRUE @@ -224,10 +223,14 @@ void BTE_InitStack(void) if ((bta_dm_di_cb_ptr = (tBTA_DM_DI_CB *)osi_malloc(sizeof(tBTA_DM_DI_CB))) == NULL) { return; } + if ((bta_dm_conn_srvcs_ptr = (tBTA_DM_CONNECTED_SRVCS *)osi_malloc(sizeof(tBTA_DM_CONNECTED_SRVCS))) == NULL) { + return; + } memset((void *)bta_sys_cb_ptr, 0, sizeof(tBTA_SYS_CB)); memset((void *)bta_dm_cb_ptr, 0, sizeof(tBTA_DM_CB)); memset((void *)bta_dm_search_cb_ptr, 0, sizeof(tBTA_DM_SEARCH_CB)); memset((void *)bta_dm_di_cb_ptr, 0, sizeof(tBTA_DM_DI_CB)); + memset((void *)bta_dm_conn_srvcs_ptr, 0, sizeof(tBTA_DM_CONNECTED_SRVCS)); //memset((void *)bta_prm_cb_ptr, 0, sizeof(tBTA_PRM_CB)); #if (defined BTA_HF_INCLUDED && BTA_HF_INCLUDED == TRUE) @@ -251,6 +254,12 @@ void BTE_InitStack(void) } memset((void *)bta_sdp_cb_ptr, 0, sizeof(tBTA_SDP_CB)); #endif +#if SDP_INCLUDED == TRUE + if ((g_disc_raw_data_buf = (UINT8 *)osi_malloc(MAX_DISC_RAW_DATA_BUF)) == NULL) { + return; + } + memset((void *)g_disc_raw_data_buf, 0, MAX_DISC_RAW_DATA_BUF); +#endif #if BTA_AR_INCLUDED==TRUE if ((bta_ar_cb_ptr = (tBTA_AR_CB *)osi_malloc(sizeof(tBTA_AR_CB))) == NULL) { return; @@ -262,6 +271,11 @@ void BTE_InitStack(void) return; } memset((void *)bta_av_cb_ptr, 0, sizeof(tBTA_AV_CB)); + + if ((bta_av_sbc_ups_cb_ptr = (tBTA_AV_SBC_UPS_CB *)osi_malloc(sizeof(tBTA_AV_SBC_UPS_CB))) == NULL) { + return; + } + memset((void *)bta_av_sbc_ups_cb_ptr, 0, sizeof(tBTA_AV_SBC_UPS_CB)); #endif #if BTA_HH_INCLUDED==TRUE if ((bta_hh_cb_ptr = (tBTA_HH_CB *)osi_malloc(sizeof(tBTA_HH_CB))) == NULL) { @@ -322,11 +336,17 @@ void BTE_DeinitStack(void) #if BTA_AV_INCLUDED==TRUE osi_free(bta_av_cb_ptr); bta_av_cb_ptr = NULL; + osi_free(bta_av_sbc_ups_cb_ptr); + bta_av_sbc_ups_cb_ptr = NULL; #endif #if BTA_AR_INCLUDED==TRUE osi_free(bta_ar_cb_ptr); bta_ar_cb_ptr = NULL; #endif +#if SDP_INCLUDED == TRUE + osi_free(g_disc_raw_data_buf); + g_disc_raw_data_buf = NULL; +#endif #if BTA_SDP_INCLUDED == TRUE osi_free(bta_sdp_cb_ptr); bta_sdp_cb_ptr = NULL; @@ -339,6 +359,8 @@ void BTE_DeinitStack(void) osi_free(bta_hf_client_cb_ptr); bta_hf_client_cb_ptr = NULL; #endif + osi_free(bta_dm_conn_srvcs_ptr); + bta_dm_conn_srvcs_ptr = NULL; osi_free(bta_dm_di_cb_ptr); bta_dm_di_cb_ptr = NULL; osi_free(bta_dm_search_cb_ptr); @@ -349,6 +371,10 @@ void BTE_DeinitStack(void) bta_sys_cb_ptr = NULL; #endif // BTA_INCLUDED == TRUE +#if (defined(GAP_INCLUDED) && GAP_INCLUDED == TRUE) + GAP_Deinit(); +#endif + #if (defined(AVCT_INCLUDED) && AVCT_INCLUDED == TRUE && AVCT_DYNAMIC_MEMORY == TRUE) osi_free(avct_cb_ptr); avct_cb_ptr = NULL; @@ -366,4 +392,8 @@ void BTE_DeinitStack(void) #if (defined(A2D_INCLUDED) && A2D_INCLUDED == TRUE) A2D_Deinit(); #endif + +#if (defined(RFCOMM_INCLUDED) && RFCOMM_INCLUDED == TRUE) + RFCOMM_Deinit(); +#endif } diff --git a/components/bt/bluedroid/osi/alarm.c b/components/bt/bluedroid/osi/alarm.c index 5307010590..21241b96da 100644 --- a/components/bt/bluedroid/osi/alarm.c +++ b/components/bt/bluedroid/osi/alarm.c @@ -44,7 +44,11 @@ enum { static osi_mutex_t alarm_mutex; static int alarm_state; +#if BT_BLE_DYNAMIC_ENV_MEMORY == FALSE static struct alarm_t alarm_cbs[ALARM_CBS_NUM]; +#else +static struct alarm_t *alarm_cbs; +#endif static osi_alarm_err_t alarm_free(osi_alarm_t *alarm); static osi_alarm_err_t alarm_set(osi_alarm_t *alarm, period_ms_t timeout, bool is_periodic); @@ -78,7 +82,14 @@ void osi_alarm_init(void) OSI_TRACE_WARNING("%s, invalid state %d\n", __func__, alarm_state); goto end; } - memset(alarm_cbs, 0x00, sizeof(alarm_cbs)); +#if BT_BLE_DYNAMIC_ENV_MEMORY == TRUE + if ((alarm_cbs = (osi_alarm_t *)osi_malloc(sizeof(osi_alarm_t) * ALARM_CBS_NUM)) == NULL) { + OSI_TRACE_ERROR("%s, malloc failed\n", __func__); + goto end; + } +#endif + + memset(alarm_cbs, 0x00, sizeof(osi_alarm_t) * ALARM_CBS_NUM); alarm_state = ALARM_STATE_OPEN; end: @@ -100,6 +111,12 @@ void osi_alarm_deinit(void) alarm_free(&alarm_cbs[i]); } } + +#if BT_BLE_DYNAMIC_ENV_MEMORY == TRUE + osi_free(alarm_cbs); + alarm_cbs = NULL; +#endif + alarm_state = ALARM_STATE_IDLE; end: diff --git a/components/bt/bluedroid/stack/btm/btm_ble_adv_filter.c b/components/bt/bluedroid/stack/btm/btm_ble_adv_filter.c index 578e2c5dbc..438485359f 100644 --- a/components/bt/bluedroid/stack/btm/btm_ble_adv_filter.c +++ b/components/bt/bluedroid/stack/btm/btm_ble_adv_filter.c @@ -48,8 +48,16 @@ #define BTM_BLE_PF_BIT_TO_MASK(x) (UINT16)(1 << (x)) +#if BTM_DYNAMIC_MEMORY == FALSE tBTM_BLE_ADV_FILTER_CB btm_ble_adv_filt_cb; -tBTM_BLE_VSC_CB cmn_ble_vsc_cb; +tBTM_BLE_VSC_CB cmn_ble_adv_vsc_cb; +#else +tBTM_BLE_ADV_FILTER_CB *btm_ble_adv_filt_cb_ptr; +tBTM_BLE_VSC_CB *cmn_ble_adv_vsc_cb_ptr; +#define btm_ble_adv_filt_cb (*btm_ble_adv_filt_cb_ptr) +#define cmn_ble_adv_vsc_cb (*cmn_ble_adv_vsc_cb_ptr) +#endif + static const BD_ADDR na_bda = {0}; static UINT8 btm_ble_cs_update_pf_counter(tBTM_BLE_SCAN_COND_OP action, @@ -87,13 +95,13 @@ tBTM_STATUS btm_ble_obtain_vsc_details() tBTM_STATUS st = BTM_SUCCESS; #if BLE_VND_INCLUDED == TRUE - BTM_BleGetVendorCapabilities(&cmn_ble_vsc_cb); - if (0 == cmn_ble_vsc_cb.max_filter) { + BTM_BleGetVendorCapabilities(&cmn_ble_adv_vsc_cb); + if (0 == cmn_ble_adv_vsc_cb.max_filter) { st = BTM_MODE_UNSUPPORTED; return st; } #else - cmn_ble_vsc_cb.max_filter = BTM_BLE_MAX_FILTER_COUNTER; + cmn_ble_adv_vsc_cb.max_filter = BTM_BLE_MAX_FILTER_COUNTER; #endif return st; } @@ -367,7 +375,7 @@ tBTM_BLE_PF_COUNT *btm_ble_find_addr_filter_counter(tBLE_BD_ADDR *p_le_bda) return &btm_ble_adv_filt_cb.p_addr_filter_count[0]; } - for (i = 0; i < cmn_ble_vsc_cb.max_filter; i ++, p_addr_filter ++) { + for (i = 0; i < cmn_ble_adv_vsc_cb.max_filter; i ++, p_addr_filter ++) { if (p_addr_filter->in_use && memcmp(p_le_bda->bda, p_addr_filter->bd_addr, BD_ADDR_LEN) == 0) { return p_addr_filter; @@ -390,7 +398,7 @@ tBTM_BLE_PF_COUNT *btm_ble_alloc_addr_filter_counter(BD_ADDR bd_addr) UINT8 i; tBTM_BLE_PF_COUNT *p_addr_filter = &btm_ble_adv_filt_cb.p_addr_filter_count[1]; - for (i = 0; i < cmn_ble_vsc_cb.max_filter; i ++, p_addr_filter ++) { + for (i = 0; i < cmn_ble_adv_vsc_cb.max_filter; i ++, p_addr_filter ++) { if (memcmp(na_bda, p_addr_filter->bd_addr, BD_ADDR_LEN) == 0) { memcpy(p_addr_filter->bd_addr, bd_addr, BD_ADDR_LEN); p_addr_filter->in_use = TRUE; @@ -418,7 +426,7 @@ BOOLEAN btm_ble_dealloc_addr_filter_counter(tBLE_BD_ADDR *p_bd_addr, UINT8 filte memset(&btm_ble_adv_filt_cb.p_addr_filter_count[0], 0, sizeof(tBTM_BLE_PF_COUNT)); } - for (i = 0; i < cmn_ble_vsc_cb.max_filter; i ++, p_addr_filter ++) { + for (i = 0; i < cmn_ble_adv_vsc_cb.max_filter; i ++, p_addr_filter ++) { if ((p_addr_filter->in_use) && (NULL == p_bd_addr || (NULL != p_bd_addr && memcmp(p_bd_addr->bda, p_addr_filter->bd_addr, BD_ADDR_LEN) == 0))) { @@ -682,7 +690,7 @@ UINT8 btm_ble_cs_update_pf_counter(tBTM_BLE_SCAN_COND_OP action, } BTM_TRACE_DEBUG("counter = %d, maxfilt = %d, num_avbl=%d", - p_counter[cond_type], cmn_ble_vsc_cb.max_filter, num_available); + p_counter[cond_type], cmn_ble_adv_vsc_cb.max_filter, num_available); return p_counter[cond_type]; } } else { @@ -1052,12 +1060,12 @@ tBTM_STATUS BTM_BleAdvFilterParamSetup(int action, tBTM_BLE_PF_FILT_INDEX filt_i /* set onlost timeout */ UINT16_TO_STREAM(p, p_filt_params->lost_timeout); /* set num_of_track_entries for firmware greater than L-release version */ - if (cmn_ble_vsc_cb.version_supported > BTM_VSC_CHIP_CAPABILITY_L_VERSION) { + if (cmn_ble_adv_vsc_cb.version_supported > BTM_VSC_CHIP_CAPABILITY_L_VERSION) { UINT16_TO_STREAM(p, p_filt_params->num_of_tracking_entries); } } - if (cmn_ble_vsc_cb.version_supported == BTM_VSC_CHIP_CAPABILITY_L_VERSION) { + if (cmn_ble_adv_vsc_cb.version_supported == BTM_VSC_CHIP_CAPABILITY_L_VERSION) { len = BTM_BLE_ADV_FILT_META_HDR_LENGTH + BTM_BLE_ADV_FILT_FEAT_SELN_LEN; } else { len = BTM_BLE_ADV_FILT_META_HDR_LENGTH + BTM_BLE_ADV_FILT_FEAT_SELN_LEN + @@ -1248,14 +1256,24 @@ tBTM_STATUS BTM_BleCfgFilterCondition(tBTM_BLE_SCAN_COND_OP action, *******************************************************************************/ void btm_ble_adv_filter_init(void) { - memset(&btm_ble_adv_filt_cb, 0, sizeof(tBTM_BLE_MULTI_ADV_CB)); +#if BTM_DYNAMIC_MEMORY == TRUE + btm_ble_adv_filt_cb_ptr = (tBTM_BLE_ADV_FILTER_CB *)osi_malloc(sizeof(tBTM_BLE_ADV_FILTER_CB)); + cmn_ble_adv_vsc_cb_ptr = (tBTM_BLE_VSC_CB *)osi_malloc(sizeof(tBTM_BLE_VSC_CB)); + if (btm_ble_adv_filt_cb_ptr == NULL || cmn_ble_adv_vsc_cb_ptr == NULL) { + BTM_TRACE_ERROR("%s malloc failed", __func__); + return; + } + memset((void *)btm_ble_adv_filt_cb_ptr, 0, sizeof(tBTM_BLE_ADV_FILTER_CB)); + memset((void *)cmn_ble_adv_vsc_cb_ptr, 0, sizeof(tBTM_BLE_VSC_CB)); +#endif + memset(&btm_ble_adv_filt_cb, 0, sizeof(tBTM_BLE_ADV_FILTER_CB)); if (BTM_SUCCESS != btm_ble_obtain_vsc_details()) { return; } - if (cmn_ble_vsc_cb.max_filter > 0) { + if (cmn_ble_adv_vsc_cb.max_filter > 0) { btm_ble_adv_filt_cb.p_addr_filter_count = - (tBTM_BLE_PF_COUNT *) osi_malloc( sizeof(tBTM_BLE_PF_COUNT) * cmn_ble_vsc_cb.max_filter); + (tBTM_BLE_PF_COUNT *) osi_malloc( sizeof(tBTM_BLE_PF_COUNT) * cmn_ble_adv_vsc_cb.max_filter); } } @@ -1276,6 +1294,13 @@ void btm_ble_adv_filter_cleanup(void) osi_free(btm_ble_adv_filt_cb.p_addr_filter_count); btm_ble_adv_filt_cb.p_addr_filter_count = NULL; } + +#if BTM_DYNAMIC_MEMORY == TRUE + osi_free(btm_ble_adv_filt_cb_ptr); + btm_ble_adv_filt_cb_ptr = NULL; + osi_free(cmn_ble_adv_vsc_cb_ptr); + cmn_ble_adv_vsc_cb_ptr = NULL; +#endif } #endif diff --git a/components/bt/bluedroid/stack/btm/btm_ble_batchscan.c b/components/bt/bluedroid/stack/btm/btm_ble_batchscan.c index d9af18d5e1..5b0fcd8ff0 100644 --- a/components/bt/bluedroid/stack/btm/btm_ble_batchscan.c +++ b/components/bt/bluedroid/stack/btm/btm_ble_batchscan.c @@ -30,9 +30,15 @@ #if (BLE_INCLUDED == TRUE) -tBTM_BLE_BATCH_SCAN_CB ble_batchscan_cb; -tBTM_BLE_ADV_TRACK_CB ble_advtrack_cb; - +#if BTM_DYNAMIC_MEMORY == FALSE +tBTM_BLE_BATCH_SCAN_CB ble_batchscan_cb; +tBTM_BLE_ADV_TRACK_CB ble_advtrack_cb; +#else +tBTM_BLE_BATCH_SCAN_CB *ble_batchscan_cb_ptr; +tBTM_BLE_ADV_TRACK_CB *ble_advtrack_cb_ptr; +#define ble_batchscan_cb (*ble_batchscan_cb_ptr) +#define ble_advtrack_cb (*ble_advtrack_cb_ptr) +#endif /* length of each batch scan command */ #define BTM_BLE_BATCH_SCAN_STORAGE_CFG_LEN 4 @@ -896,6 +902,14 @@ tBTM_STATUS BTM_BleTrackAdvertiser(tBTM_BLE_TRACK_ADV_CBACK *p_track_cback, *******************************************************************************/ void btm_ble_batchscan_init(void) { +#if BTM_DYNAMIC_MEMORY == TRUE + ble_batchscan_cb_ptr = (tBTM_BLE_BATCH_SCAN_CB *)osi_malloc(sizeof(tBTM_BLE_BATCH_SCAN_CB)); + ble_advtrack_cb_ptr = (tBTM_BLE_ADV_TRACK_CB *)osi_malloc(sizeof(tBTM_BLE_ADV_TRACK_CB)); + if (ble_batchscan_cb_ptr == NULL || ble_advtrack_cb_ptr == NULL) { + BTM_TRACE_ERROR("%s malloc failed", __func__); + return; + } +#endif BTM_TRACE_EVENT (" btm_ble_batchscan_init"); memset(&ble_batchscan_cb, 0, sizeof(tBTM_BLE_BATCH_SCAN_CB)); memset(&ble_advtrack_cb, 0, sizeof(tBTM_BLE_ADV_TRACK_CB)); @@ -927,6 +941,13 @@ void btm_ble_batchscan_cleanup(void) memset(&ble_batchscan_cb, 0, sizeof(tBTM_BLE_BATCH_SCAN_CB)); memset(&ble_advtrack_cb, 0, sizeof(tBTM_BLE_ADV_TRACK_CB)); + +#if BTM_DYNAMIC_MEMORY == TRUE + osi_free(ble_batchscan_cb_ptr); + osi_free(ble_advtrack_cb_ptr); + ble_batchscan_cb_ptr = NULL; + ble_advtrack_cb_ptr = NULL; +#endif } #endif diff --git a/components/bt/bluedroid/stack/btm/btm_ble_gap.c b/components/bt/bluedroid/stack/btm/btm_ble_gap.c index cbf349ded1..e7a4ba955b 100644 --- a/components/bt/bluedroid/stack/btm/btm_ble_gap.c +++ b/components/bt/bluedroid/stack/btm/btm_ble_gap.c @@ -57,7 +57,12 @@ #define MIN_ADV_LENGTH 2 #define BTM_VSC_CHIP_CAPABILITY_RSP_LEN_L_RELEASE 9 -static tBTM_BLE_VSC_CB cmn_ble_vsc_cb; +#if BTM_DYNAMIC_MEMORY == FALSE +static tBTM_BLE_VSC_CB cmn_ble_gap_vsc_cb; +#else +static tBTM_BLE_VSC_CB *cmn_ble_gap_vsc_cb_ptr; +#define cmn_ble_gap_vsc_cb (*cmn_ble_gap_vsc_cb_ptr) +#endif #if BLE_VND_INCLUDED == TRUE static tBTM_BLE_CTRL_FEATURES_CBACK *p_ctrl_le_feature_rd_cmpl_cback = NULL; @@ -447,7 +452,7 @@ tBTM_STATUS BTM_BleObserve(BOOLEAN start, UINT32 duration, btm_ble_enable_resolving_list_for_platform(BTM_BLE_RL_SCAN); #endif - if (cmn_ble_vsc_cb.extended_scan_support == 0) { + if (cmn_ble_gap_vsc_cb.extended_scan_support == 0) { btsnd_hcic_ble_set_scan_params(p_inq->scan_type, (UINT16)scan_interval, (UINT16)scan_window, btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type, @@ -4292,10 +4297,18 @@ BOOLEAN btm_ble_update_mode_operation(UINT8 link_role, BD_ADDR bd_addr, UINT8 st *******************************************************************************/ void btm_ble_init (void) { - tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; - BTM_TRACE_DEBUG("%s", __func__); +#if BTM_DYNAMIC_MEMORY == TRUE + cmn_ble_gap_vsc_cb_ptr = (tBTM_BLE_VSC_CB *)osi_malloc(sizeof(tBTM_BLE_VSC_CB)); + if (cmn_ble_gap_vsc_cb_ptr == NULL) { + BTM_TRACE_ERROR("%s malloc failed", __func__); + return; + } +#endif + + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + btu_free_timer(&p_cb->obs_timer_ent); btu_free_timer(&p_cb->scan_timer_ent); btu_free_timer(&p_cb->inq_var.fast_adv_timer); @@ -4340,6 +4353,11 @@ void btm_ble_free (void) BTM_TRACE_DEBUG("%s", __func__); fixed_queue_free(p_cb->conn_pending_q, osi_free_func); + +#if BTM_DYNAMIC_MEMORY == TRUE + osi_free(cmn_ble_gap_vsc_cb_ptr); + cmn_ble_gap_vsc_cb_ptr = NULL; +#endif } /******************************************************************************* diff --git a/components/bt/bluedroid/stack/btm/btm_ble_multi_adv.c b/components/bt/bluedroid/stack/btm/btm_ble_multi_adv.c index 5457324130..a3ad604437 100644 --- a/components/bt/bluedroid/stack/btm/btm_ble_multi_adv.c +++ b/components/bt/bluedroid/stack/btm/btm_ble_multi_adv.c @@ -45,8 +45,15 @@ /************************************************************************************ ** Static variables ************************************************************************************/ -tBTM_BLE_MULTI_ADV_CB btm_multi_adv_cb; -tBTM_BLE_MULTI_ADV_INST_IDX_Q btm_multi_adv_idx_q; +#if BTM_DYNAMIC_MEMORY == FALSE +tBTM_BLE_MULTI_ADV_CB btm_multi_adv_cb; +tBTM_BLE_MULTI_ADV_INST_IDX_Q btm_multi_adv_idx_q; +#else +tBTM_BLE_MULTI_ADV_CB *btm_multi_adv_cb_ptr; +tBTM_BLE_MULTI_ADV_INST_IDX_Q *btm_multi_adv_idx_q_ptr; +#define btm_multi_adv_cb (*btm_multi_adv_cb_ptr) +#define btm_multi_adv_idx_q (*btm_multi_adv_idx_q_ptr) +#endif /************************************************************************************ ** Externs @@ -764,6 +771,15 @@ void btm_ble_multi_adv_vse_cback(UINT8 len, UINT8 *p) *******************************************************************************/ void btm_ble_multi_adv_init() { +#if BTM_DYNAMIC_MEMORY == TRUE + btm_multi_adv_cb_ptr = (tBTM_BLE_MULTI_ADV_CB *)osi_malloc(sizeof(tBTM_BLE_MULTI_ADV_CB)); + btm_multi_adv_idx_q_ptr = (tBTM_BLE_MULTI_ADV_INST_IDX_Q *)osi_malloc(sizeof(tBTM_BLE_MULTI_ADV_INST_IDX_Q)); + if (btm_multi_adv_cb_ptr == NULL || btm_multi_adv_idx_q_ptr == NULL) { + BTM_TRACE_ERROR("%s malloc failed", __func__); + return; + } +#endif + UINT8 i = 0; memset(&btm_multi_adv_cb, 0, sizeof(tBTM_BLE_MULTI_ADV_CB)); memset (&btm_multi_adv_idx_q, 0, sizeof (tBTM_BLE_MULTI_ADV_INST_IDX_Q)); @@ -823,6 +839,12 @@ void btm_ble_multi_adv_cleanup(void) btm_multi_adv_cb.op_q.p_inst_id = NULL; } +#if BTM_DYNAMIC_MEMORY == TRUE + osi_free(btm_multi_adv_cb_ptr); + osi_free(btm_multi_adv_idx_q_ptr); + btm_multi_adv_cb_ptr = NULL; + btm_multi_adv_idx_q_ptr = NULL; +#endif } /******************************************************************************* diff --git a/components/bt/bluedroid/stack/btm/btm_ble_privacy.c b/components/bt/bluedroid/stack/btm/btm_ble_privacy.c index 71f06eb82f..0ec69d953e 100644 --- a/components/bt/bluedroid/stack/btm/btm_ble_privacy.c +++ b/components/bt/bluedroid/stack/btm/btm_ble_privacy.c @@ -882,6 +882,7 @@ void btm_ble_enable_resolving_list(UINT8 rl_mask) } } +#if 0 //Unused /******************************************************************************* ** ** Function btm_ble_resolving_list_empty @@ -896,6 +897,7 @@ BOOLEAN btm_ble_resolving_list_empty(void) return (controller_get_interface()->get_ble_resolving_list_max_size() == btm_cb.ble_ctr_cb.resolving_list_avail_size); } +#endif /******************************************************************************* ** diff --git a/components/bt/bluedroid/stack/gap/gap_api.c b/components/bt/bluedroid/stack/gap/gap_api.c index 305eadee0a..69b5d9629b 100644 --- a/components/bt/bluedroid/stack/gap/gap_api.c +++ b/components/bt/bluedroid/stack/gap/gap_api.c @@ -21,8 +21,13 @@ #include "common/bt_target.h" //#include "bt_utils.h" #include "gap_int.h" +#include "osi/allocator.h" +#if GAP_DYNAMIC_MEMORY == FALSE tGAP_CB gap_cb; +#else +tGAP_CB *gap_cb_ptr; +#endif /******************************************************************************* ** @@ -57,6 +62,10 @@ UINT8 GAP_SetTraceLevel (UINT8 new_level) *******************************************************************************/ void GAP_Init(void) { +#if GAP_DYNAMIC_MEMORY == TRUE + gap_cb_ptr = (tGAP_CB *)osi_malloc(sizeof(tGAP_CB)); +#endif + memset (&gap_cb, 0, sizeof (tGAP_CB)); #if defined(GAP_INITIAL_TRACE_LEVEL) @@ -74,3 +83,20 @@ void GAP_Init(void) #endif } +/******************************************************************************* +** +** Function GAP_Deinit +** +** Description This function is called to deinitialize the control block +** for this layer. +** +** Returns void +** +*******************************************************************************/ +void GAP_Deinit(void) +{ +#if GAP_DYNAMIC_MEMORY == TRUE + osi_free(gap_cb_ptr); + gap_cb_ptr = NULL; +#endif +} \ No newline at end of file diff --git a/components/bt/bluedroid/stack/gap/gap_ble.c b/components/bt/bluedroid/stack/gap/gap_ble.c index bb5db8c1b1..08f7c195ce 100644 --- a/components/bt/bluedroid/stack/gap/gap_ble.c +++ b/components/bt/bluedroid/stack/gap/gap_ble.c @@ -49,7 +49,7 @@ static void gap_ble_c_connect_cback (tGATT_IF gatt_if, BD_ADDR bda, UINT16 conn tGATT_DISCONN_REASON reason, tGATT_TRANSPORT transport); static void gap_ble_c_cmpl_cback (UINT16 conn_id, tGATTC_OPTYPE op, tGATT_STATUS status, tGATT_CL_COMPLETE *p_data); -static tGATT_CBACK gap_cback = { +static const tGATT_CBACK gap_cback = { gap_ble_c_connect_cback, gap_ble_c_cmpl_cback, NULL, diff --git a/components/bt/bluedroid/stack/gap/include/gap_int.h b/components/bt/bluedroid/stack/gap/include/gap_int.h index e278141184..8a3ae0e2f0 100644 --- a/components/bt/bluedroid/stack/gap/include/gap_int.h +++ b/components/bt/bluedroid/stack/gap/include/gap_int.h @@ -142,8 +142,13 @@ typedef struct { #endif } tGAP_CB; - +#if GAP_DYNAMIC_MEMORY == FALSE extern tGAP_CB gap_cb; +#else +extern tGAP_CB *gap_cb_ptr; +#define gap_cb (*gap_cb_ptr) +#endif + #if (GAP_CONN_INCLUDED == TRUE) extern void gap_conn_init(void); #endif diff --git a/components/bt/bluedroid/stack/gatt/gatt_api.c b/components/bt/bluedroid/stack/gatt/gatt_api.c index 7f360d52db..d093d2b9d4 100644 --- a/components/bt/bluedroid/stack/gatt/gatt_api.c +++ b/components/bt/bluedroid/stack/gatt/gatt_api.c @@ -110,7 +110,7 @@ BOOLEAN GATTS_AddHandleRange(tGATTS_HNDL_RANGE *p_hndl_range) ** Returns TRUE if registered OK, else FALSE ** *******************************************************************************/ -BOOLEAN GATTS_NVRegister (tGATT_APPL_INFO *p_cb_info) +BOOLEAN GATTS_NVRegister (const tGATT_APPL_INFO *p_cb_info) { BOOLEAN status = FALSE; if (p_cb_info) { @@ -1192,7 +1192,7 @@ void GATT_SetIdleTimeout (BD_ADDR bd_addr, UINT16 idle_tout, tBT_TRANSPORT trans ** Returns 0 for error, otherwise the index of the client registered with GATT ** *******************************************************************************/ -tGATT_IF GATT_Register (tBT_UUID *p_app_uuid128, tGATT_CBACK *p_cb_info) +tGATT_IF GATT_Register (tBT_UUID *p_app_uuid128, const tGATT_CBACK *p_cb_info) { tGATT_REG *p_reg; UINT8 i_gatt_if = 0; diff --git a/components/bt/bluedroid/stack/gatt/gatt_attr.c b/components/bt/bluedroid/stack/gatt/gatt_attr.c index 5036241317..3a67a8bf25 100644 --- a/components/bt/bluedroid/stack/gatt/gatt_attr.c +++ b/components/bt/bluedroid/stack/gatt/gatt_attr.c @@ -52,7 +52,7 @@ static void gatt_cl_op_cmpl_cback(UINT16 conn_id, tGATTC_OPTYPE op, tGATT_STATUS static void gatt_cl_start_config_ccc(tGATT_PROFILE_CLCB *p_clcb); -static tGATT_CBACK gatt_profile_cback = { +static const tGATT_CBACK gatt_profile_cback = { gatt_connect_cback, gatt_cl_op_cmpl_cback, gatt_disc_res_cback, @@ -308,7 +308,7 @@ static void gatt_connect_cback (tGATT_IF gatt_if, BD_ADDR bda, UINT16 conn_id, p_clcb->connected = TRUE; p_clcb->conn_id = conn_id; } - + if (!p_clcb->connected) { /* wait for connection */ @@ -348,7 +348,7 @@ void gatt_profile_db_init (void) service_handle = GATTS_CreateService (gatt_cb.gatt_if , &uuid, 0, GATTP_MAX_ATTR_NUM, TRUE); GATT_TRACE_DEBUG ("GATTS_CreateService: handle of service handle%x", service_handle); - + /* add Service Changed characteristic */ uuid.uu.uuid16 = gatt_cb.gattp_attr.uuid = GATT_UUID_GATT_SRV_CHGD; diff --git a/components/bt/bluedroid/stack/gatt/gatt_cl.c b/components/bt/bluedroid/stack/gatt/gatt_cl.c index daa911e8c2..4ffd7ca5a0 100644 --- a/components/bt/bluedroid/stack/gatt/gatt_cl.c +++ b/components/bt/bluedroid/stack/gatt/gatt_cl.c @@ -47,7 +47,7 @@ *********************************************************************************/ void gatt_send_prepare_write(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb); -UINT8 disc_type_to_att_opcode[GATT_DISC_MAX] = { +static const UINT8 disc_type_to_att_opcode[GATT_DISC_MAX] = { 0, GATT_REQ_READ_BY_GRP_TYPE, /* GATT_DISC_SRVC_ALL = 1, */ GATT_REQ_FIND_TYPE_VALUE, /* GATT_DISC_SRVC_BY_UUID, */ @@ -56,7 +56,7 @@ UINT8 disc_type_to_att_opcode[GATT_DISC_MAX] = { GATT_REQ_FIND_INFO /* GATT_DISC_CHAR_DSCPT, */ }; -UINT16 disc_type_to_uuid[GATT_DISC_MAX] = { +static const UINT16 disc_type_to_uuid[GATT_DISC_MAX] = { 0, /* reserved */ GATT_UUID_PRI_SERVICE, /* DISC_SRVC_ALL */ GATT_UUID_PRI_SERVICE, /* for DISC_SERVC_BY_UUID */ diff --git a/components/bt/bluedroid/stack/include/stack/dyn_mem.h b/components/bt/bluedroid/stack/include/stack/dyn_mem.h index 2b2db28453..1241c3a85a 100644 --- a/components/bt/bluedroid/stack/include/stack/dyn_mem.h +++ b/components/bt/bluedroid/stack/include/stack/dyn_mem.h @@ -18,15 +18,19 @@ #ifndef DYN_MEM_H #define DYN_MEM_H -#include "common/bt_target.h" -#if BT_BLE_DYNAMIC_ENV_MEMORY +#include "common/bt_user_config.h" + +#if UC_BT_BLE_DYNAMIC_ENV_MEMORY +#define BT_BLE_DYNAMIC_ENV_MEMORY TRUE #define BTU_DYNAMIC_MEMORY TRUE #define BTM_DYNAMIC_MEMORY TRUE #define L2C_DYNAMIC_MEMORY TRUE #define GATT_DYNAMIC_MEMORY TRUE #define SMP_DYNAMIC_MEMORY TRUE #define BTA_DYNAMIC_MEMORY TRUE +#define BTC_DYNAMIC_MENDRY TRUE #define SDP_DYNAMIC_MEMORY TRUE +#define GAP_DYNAMIC_MEMORY TRUE #define RFC_DYNAMIC_MEMORY TRUE #define TCS_DYNAMIC_MEMORY TRUE #define BNEP_DYNAMIC_MEMORY TRUE @@ -51,11 +55,18 @@ #define SLIP_DYNAMIC_MEMORY TRUE #define LLCP_DYNAMIC_MEMORY TRUE #define BTC_SBC_DEC_DYNAMIC_MEMORY TRUE -#define BTC_SBC_ENC_DYNAMIC_MEMORY TRUE - -#else /* #if BT_BLE_DYNAMIC_ENV_MEMORY */ +#else /* #if UC_BT_BLE_DYNAMIC_ENV_MEMORY */ +#define BT_BLE_DYNAMIC_ENV_MEMORY FALSE +#define BTU_DYNAMIC_MEMORY FALSE +#define BTM_DYNAMIC_MEMORY FALSE +#define L2C_DYNAMIC_MEMORY FALSE +#define GATT_DYNAMIC_MEMORY FALSE +#define SMP_DYNAMIC_MEMORY FALSE +#define BTA_DYNAMIC_MEMORY FALSE +#define BTC_DYNAMIC_MENDRY FALSE #define SDP_DYNAMIC_MEMORY FALSE +#define GAP_DYNAMIC_MEMORY FALSE #define RFC_DYNAMIC_MEMORY FALSE #define TCS_DYNAMIC_MEMORY FALSE #define BNEP_DYNAMIC_MEMORY FALSE @@ -80,9 +91,13 @@ #define SLIP_DYNAMIC_MEMORY FALSE #define LLCP_DYNAMIC_MEMORY FALSE #define BTC_SBC_DEC_DYNAMIC_MEMORY FALSE -#define BTC_SBC_ENC_DYNAMIC_MEMORY FALSE -#endif /* #if BT_BLE_DYNAMIC_ENV_MEMORY */ +#endif /* #if UC_BT_BLE_DYNAMIC_ENV_MEMORY */ + +#ifndef BT_BLE_DYNAMIC_ENV_MEMORY +#define BT_BLE_DYNAMIC_ENV_MEMORY FALSE +#endif + /**************************************************************************** ** Define memory usage for each CORE component (if not defined in bdroid_buildcfg.h) ** The default for each component is to use static memory allocations. @@ -99,6 +114,10 @@ #define SDP_DYNAMIC_MEMORY FALSE #endif +#ifndef GAP_DYNAMIC_MEMORY +#define GAP_DYNAMIC_MEMORY FALSE +#endif + #ifndef L2C_DYNAMIC_MEMORY #define L2C_DYNAMIC_MEMORY FALSE #endif @@ -208,12 +227,15 @@ #endif /**************************************************************************** -** Define memory usage for BTA (if not defined in bdroid_buildcfg.h) +** Define memory usage for BTA and BTC (if not defined in bdroid_buildcfg.h) ** The default for each component is to use static memory allocations. */ #ifndef BTA_DYNAMIC_MEMORY #define BTA_DYNAMIC_MEMORY FALSE #endif -#endif /* #ifdef DYN_MEM_H */ +#ifndef BTC_DYNAMIC_MENDRY +#define BTC_DYNAMIC_MENDRY FALSE +#endif +#endif /* #ifdef DYN_MEM_H */ diff --git a/components/bt/bluedroid/stack/include/stack/gap_api.h b/components/bt/bluedroid/stack/include/stack/gap_api.h index 5d8d87645d..03af2956ce 100644 --- a/components/bt/bluedroid/stack/include/stack/gap_api.h +++ b/components/bt/bluedroid/stack/include/stack/gap_api.h @@ -325,6 +325,18 @@ extern UINT8 GAP_SetTraceLevel (UINT8 new_level); *******************************************************************************/ extern void GAP_Init(void); +/******************************************************************************* +** +** Function GAP_Deinit +** +** Description This function is called to deinitialize the control block +** for this layer. +** +** Returns void +** +*******************************************************************************/ +extern void GAP_Deinit(void); + #if (BLE_INCLUDED == TRUE) /******************************************************************************* ** diff --git a/components/bt/bluedroid/stack/include/stack/gatt_api.h b/components/bt/bluedroid/stack/include/stack/gatt_api.h index b2cecb0578..c250f9a3c1 100644 --- a/components/bt/bluedroid/stack/include/stack/gatt_api.h +++ b/components/bt/bluedroid/stack/include/stack/gatt_api.h @@ -707,7 +707,7 @@ extern BOOLEAN GATTS_AddHandleRange(tGATTS_HNDL_RANGE *p_hndl_range); ** Returns TRUE if registered OK, else FALSE ** *******************************************************************************/ -extern BOOLEAN GATTS_NVRegister (tGATT_APPL_INFO *p_cb_info); +extern BOOLEAN GATTS_NVRegister (const tGATT_APPL_INFO *p_cb_info); /******************************************************************************* @@ -1059,7 +1059,7 @@ extern void GATT_SetIdleTimeout (BD_ADDR bd_addr, UINT16 idle_tout, ** Returns 0 for error, otherwise the index of the client registered with GATT ** *******************************************************************************/ -extern tGATT_IF GATT_Register (tBT_UUID *p_app_uuid128, tGATT_CBACK *p_cb_info); +extern tGATT_IF GATT_Register (tBT_UUID *p_app_uuid128, const tGATT_CBACK *p_cb_info); /******************************************************************************* ** diff --git a/components/bt/bluedroid/stack/include/stack/port_api.h b/components/bt/bluedroid/stack/include/stack/port_api.h index 10b0378681..8145a177a0 100644 --- a/components/bt/bluedroid/stack/include/stack/port_api.h +++ b/components/bt/bluedroid/stack/include/stack/port_api.h @@ -623,6 +623,17 @@ extern int PORT_Test (UINT16 handle, UINT8 *p_data, UINT16 len); *******************************************************************************/ extern void RFCOMM_Init (void); +/******************************************************************************* +** +** Function RFCOMM_Deinit +** +** Description This function is called to deinitialize the control block +** for this layer. +** +** Returns void +** +*******************************************************************************/ +extern void RFCOMM_Deinit(void); /******************************************************************************* ** diff --git a/components/bt/bluedroid/stack/l2cap/l2c_main.c b/components/bt/bluedroid/stack/l2cap/l2c_main.c index 1035b44c6a..45baeaa3b9 100644 --- a/components/bt/bluedroid/stack/l2cap/l2c_main.c +++ b/components/bt/bluedroid/stack/l2cap/l2c_main.c @@ -52,6 +52,7 @@ tL2C_CB l2cb; tL2C_CB *l2c_cb_ptr; #endif +#if 0 //Unused /******************************************************************************* ** ** Function l2c_bcst_msg @@ -104,7 +105,7 @@ void l2c_bcst_msg( BT_HDR *p_buf, UINT16 psm ) bte_main_hci_send(p_buf, BT_EVT_TO_LM_HCI_ACL); } } - +#endif /******************************************************************************* ** diff --git a/components/bt/bluedroid/stack/rfcomm/port_api.c b/components/bt/bluedroid/stack/rfcomm/port_api.c index fd8246c53d..64f09149e6 100644 --- a/components/bt/bluedroid/stack/rfcomm/port_api.c +++ b/components/bt/bluedroid/stack/rfcomm/port_api.c @@ -826,6 +826,8 @@ int PORT_FlowControl (UINT16 handle, BOOLEAN enable) } return (PORT_SUCCESS); } + +#if 0 //Unused /******************************************************************************* ** ** Function PORT_FlowControl_MaxCredit @@ -839,7 +841,6 @@ int PORT_FlowControl (UINT16 handle, BOOLEAN enable) ** enable - enables data flow ** *******************************************************************************/ - int PORT_FlowControl_MaxCredit (UINT16 handle, BOOLEAN enable) { tPORT *p_port; @@ -896,7 +897,7 @@ int PORT_FlowControl_MaxCredit (UINT16 handle, BOOLEAN enable) } return (PORT_SUCCESS); } - +#endif /******************************************************************************* ** @@ -1713,7 +1714,7 @@ int PORT_Test (UINT16 handle, UINT8 *p_data, UINT16 len) *******************************************************************************/ void RFCOMM_Init (void) { -#if (RFC_DYNAMIC_MEMORY) +#if RFC_DYNAMIC_MEMORY == TRUE rfc_cb_ptr = (tRFC_CB *)osi_malloc(sizeof(tRFC_CB)); #endif /* #if (RFC_DYNAMIC_MEMORY) */ memset (&rfc_cb, 0, sizeof (tRFC_CB)); /* Init RFCOMM control block */ @@ -1729,6 +1730,24 @@ void RFCOMM_Init (void) rfcomm_l2cap_if_init (); } +/******************************************************************************* +** +** Function RFCOMM_Deinit +** +** Description This function is called to deinitialize the control block +** for this layer. +** +** Returns void +** +*******************************************************************************/ +void RFCOMM_Deinit(void) +{ +#if RFC_DYNAMIC_MEMORY == TRUE + osi_free(rfc_cb_ptr); + rfc_cb_ptr = NULL; +#endif +} + /******************************************************************************* ** ** Function PORT_SetTraceLevel diff --git a/components/bt/bluedroid/stack/smp/include/p_256_ecc_pp.h b/components/bt/bluedroid/stack/smp/include/p_256_ecc_pp.h index f91d6056b2..172dd24f10 100644 --- a/components/bt/bluedroid/stack/smp/include/p_256_ecc_pp.h +++ b/components/bt/bluedroid/stack/smp/include/p_256_ecc_pp.h @@ -25,6 +25,7 @@ #pragma once #include "p_256_multprecision.h" +#include "common/bt_target.h" typedef unsigned long DWORD; @@ -53,8 +54,16 @@ typedef struct { } elliptic_curve_t; +#if SMP_DYNAMIC_MEMORY == FALSE extern elliptic_curve_t curve; extern elliptic_curve_t curve_p256; +#else +extern elliptic_curve_t *curve_ptr; +extern elliptic_curve_t *curve_p256_ptr; +#define curve (*curve_ptr) +#define curve_p256 (*curve_p256_ptr) +#endif + void ECC_PointMult_Bin_NAF(Point *q, Point *p, DWORD *n, uint32_t keyLength); diff --git a/components/bt/bluedroid/stack/smp/include/smp_int.h b/components/bt/bluedroid/stack/smp/include/smp_int.h index a261e14cba..cf3683c07c 100644 --- a/components/bt/bluedroid/stack/smp/include/smp_int.h +++ b/components/bt/bluedroid/stack/smp/include/smp_int.h @@ -479,7 +479,7 @@ extern void smp_proc_pairing_cmpl(tSMP_CB *p_cb); extern void smp_convert_string_to_tk(BT_OCTET16 tk, UINT32 passkey); extern void smp_mask_enc_key(UINT8 loc_enc_size, UINT8 *p_data); extern void smp_rsp_timeout(TIMER_LIST_ENT *p_tle); -extern void smp_xor_128(BT_OCTET16 a, BT_OCTET16 b); +extern void smp_xor_128(BT_OCTET16 a, const BT_OCTET16 b); extern BOOLEAN smp_encrypt_data (UINT8 *key, UINT8 key_len, UINT8 *plain_text, UINT8 pt_len, tSMP_ENC *p_out); diff --git a/components/bt/bluedroid/stack/smp/p_256_ecc_pp.c b/components/bt/bluedroid/stack/smp/p_256_ecc_pp.c index 0f7ab3ec41..aceb0b209a 100644 --- a/components/bt/bluedroid/stack/smp/p_256_ecc_pp.c +++ b/components/bt/bluedroid/stack/smp/p_256_ecc_pp.c @@ -26,9 +26,15 @@ #include #include "p_256_ecc_pp.h" #include "p_256_multprecision.h" +#include "common/bt_target.h" +#if SMP_DYNAMIC_MEMORY == FALSE elliptic_curve_t curve; elliptic_curve_t curve_p256; +#else +elliptic_curve_t *curve_ptr; +elliptic_curve_t *curve_p256_ptr; +#endif static void p_256_init_point(Point *q) { @@ -248,7 +254,7 @@ bool ECC_CheckPointIsInElliCur_P256(Point *p) DWORD x_x_q[KEY_LENGTH_DWORDS_P256] = {0x0}; /* x % q */ DWORD x_q[KEY_LENGTH_DWORDS_P256] = {0x0}; - /* x^2, To prevent overflow, the length of the x square here needs to + /* x^2, To prevent overflow, the length of the x square here needs to be expanded to two times the original one. */ DWORD x_x[2*KEY_LENGTH_DWORDS_P256] = {0x0}; /* y_y_q =(p->y)^2(mod q) */ @@ -259,7 +265,7 @@ bool ECC_CheckPointIsInElliCur_P256(Point *p) y^2 = (x^2 - 3)*x + b (mod q), so we calculate the x^2 - 3 value here */ x_x[0] -= 3; - /* Using math relations. (a*b) % q = ((a%q)*(b%q)) % q ==> + /* Using math relations. (a*b) % q = ((a%q)*(b%q)) % q ==> (x^2 - 3)*x = (((x^2 - 3) % q) * x % q) % q */ multiprecision_fast_mod_P256(x_x_q, x_x); /* x_x = x_x_q * x_q */ diff --git a/components/bt/bluedroid/stack/smp/smp_act.c b/components/bt/bluedroid/stack/smp/smp_act.c index d11506e650..d9f3369500 100644 --- a/components/bt/bluedroid/stack/smp/smp_act.c +++ b/components/bt/bluedroid/stack/smp/smp_act.c @@ -283,6 +283,7 @@ void smp_send_confirm(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) smp_send_cmd(SMP_OPCODE_CONFIRM, p_cb); } +#if 0 //Unused /******************************************************************************* ** Function smp_send_init ** Description process pairing initializer to slave device @@ -292,6 +293,7 @@ void smp_send_init(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) SMP_TRACE_DEBUG("%s\n", __func__); smp_send_cmd(SMP_OPCODE_INIT, p_cb); } +#endif /******************************************************************************* ** Function smp_send_rand @@ -675,6 +677,7 @@ void smp_proc_confirm(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) p_cb->flags |= SMP_PAIR_FLAGS_CMD_CONFIRM; } +#if 0 //Unused /******************************************************************************* ** Function smp_proc_init ** Description process pairing initializer from peer device @@ -694,6 +697,7 @@ void smp_proc_init(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) /* save the SRand for comparison */ STREAM_TO_ARRAY(p_cb->rrand, p, BT_OCTET16_LEN); } +#endif /******************************************************************************* ** Function smp_proc_rand diff --git a/components/bt/bluedroid/stack/smp/smp_api.c b/components/bt/bluedroid/stack/smp/smp_api.c index 8c3c3d8bc7..37aa2b9111 100644 --- a/components/bt/bluedroid/stack/smp/smp_api.c +++ b/components/bt/bluedroid/stack/smp/smp_api.c @@ -51,8 +51,12 @@ void SMP_Init(void) { #if SMP_DYNAMIC_MEMORY smp_cb_ptr = (tSMP_CB *)osi_malloc(sizeof(tSMP_CB)); + curve_ptr = (elliptic_curve_t *)osi_malloc(sizeof(elliptic_curve_t)); + curve_p256_ptr = (elliptic_curve_t *)osi_malloc(sizeof(elliptic_curve_t)); #endif memset(&smp_cb, 0, sizeof(tSMP_CB)); + memset(&curve, 0, sizeof(elliptic_curve_t)); + memset(&curve_p256, 0, sizeof(elliptic_curve_t)); #if defined(SMP_INITIAL_TRACE_LEVEL) smp_cb.trace_level = SMP_INITIAL_TRACE_LEVEL; @@ -71,6 +75,8 @@ void SMP_Free(void) memset(&smp_cb, 0, sizeof(tSMP_CB)); #if SMP_DYNAMIC_MEMORY FREE_AND_RESET(smp_cb_ptr); + FREE_AND_RESET(curve_ptr); + FREE_AND_RESET(curve_p256_ptr); #endif /* #if SMP_DYNAMIC_MEMORY */ } diff --git a/components/bt/bluedroid/stack/smp/smp_cmac.c b/components/bt/bluedroid/stack/smp/smp_cmac.c index 753c5188f2..0e7bab02c1 100644 --- a/components/bt/bluedroid/stack/smp/smp_cmac.c +++ b/components/bt/bluedroid/stack/smp/smp_cmac.c @@ -42,7 +42,7 @@ typedef struct { tCMAC_CB cmac_cb; /* Rb for AES-128 as block cipher, LSB as [0] */ -BT_OCTET16 const_Rb = { +const BT_OCTET16 const_Rb = { 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; diff --git a/components/bt/bluedroid/stack/smp/smp_keys.c b/components/bt/bluedroid/stack/smp/smp_keys.c index 4c523787c1..c993c7387f 100644 --- a/components/bt/bluedroid/stack/smp/smp_keys.c +++ b/components/bt/bluedroid/stack/smp/smp_keys.c @@ -93,6 +93,7 @@ void smp_debug_print_nbyte_little_endian(UINT8 *p, const UINT8 *key_name, UINT8 #endif } +#if 0 //Unused void smp_debug_print_nbyte_big_endian (UINT8 *p, const UINT8 *key_name, UINT8 len) { #if SMP_DEBUG == TRUE @@ -115,6 +116,7 @@ void smp_debug_print_nbyte_big_endian (UINT8 *p, const UINT8 *key_name, UINT8 le } #endif } +#endif /******************************************************************************* ** diff --git a/components/bt/bluedroid/stack/smp/smp_utils.c b/components/bt/bluedroid/stack/smp/smp_utils.c index 19c2cde99b..6d64f44abc 100644 --- a/components/bt/bluedroid/stack/smp/smp_utils.c +++ b/components/bt/bluedroid/stack/smp/smp_utils.c @@ -866,9 +866,10 @@ void smp_mask_enc_key(UINT8 loc_enc_size, UINT8 *p_data) ** Returns void ** *******************************************************************************/ -void smp_xor_128(BT_OCTET16 a, BT_OCTET16 b) +void smp_xor_128(BT_OCTET16 a, const BT_OCTET16 b) { - UINT8 i, *aa = a, *bb = b; + UINT8 i, *aa = a; + const UINT8 *bb = b; SMP_TRACE_EVENT("smp_xor_128\n"); for (i = 0; i < BT_OCTET16_LEN; i++) { From 5fd4cbb9d288f6d06c4e4279a1d592e8aaa353fd Mon Sep 17 00:00:00 2001 From: baohongde Date: Sat, 29 Sep 2018 16:04:50 +0800 Subject: [PATCH 088/486] component/bt: Add functions for heap memory debug --- components/bt/bluedroid/osi/allocator.c | 21 +++++++++++++++++++ .../bt/bluedroid/osi/include/osi/allocator.h | 8 ++++--- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/components/bt/bluedroid/osi/allocator.c b/components/bt/bluedroid/osi/allocator.c index de8c8da722..624a11c176 100644 --- a/components/bt/bluedroid/osi/allocator.c +++ b/components/bt/bluedroid/osi/allocator.c @@ -38,6 +38,8 @@ typedef struct { static uint32_t mem_dbg_count = 0; static uint32_t mem_dbg_count2 = 0; static osi_mem_dbg_info_t mem_dbg_info[OSI_MEM_DBG_INFO_MAX]; +static uint32_t mem_dbg_total_size = 0; +static uint32_t mem_dbg_max_size = 0; void osi_mem_dbg_init(void) { @@ -51,6 +53,8 @@ void osi_mem_dbg_init(void) } mem_dbg_count = 0; mem_dbg_count2 = 0; + mem_dbg_total_size = 0; + mem_dbg_max_size = 0; } void osi_mem_dbg_record(void *p, int size, const char *func, int line) @@ -76,6 +80,11 @@ void osi_mem_dbg_record(void *p, int size, const char *func, int line) if (i >= OSI_MEM_DBG_INFO_MAX) { OSI_TRACE_ERROR("%s full %s %d !!\n", __func__, func, line); } + + mem_dbg_total_size += size; + if(mem_dbg_max_size < mem_dbg_total_size) { + mem_dbg_max_size = mem_dbg_total_size; + } } void osi_mem_dbg_clean(void *p, const char *func, int line) @@ -89,6 +98,7 @@ void osi_mem_dbg_clean(void *p, const char *func, int line) for (i = 0; i < OSI_MEM_DBG_INFO_MAX; i++) { if (mem_dbg_info[i].p == p) { + mem_dbg_total_size -= mem_dbg_info[i].size; mem_dbg_info[i].p = NULL; mem_dbg_info[i].size = 0; mem_dbg_info[i].func = NULL; @@ -113,6 +123,17 @@ void osi_mem_dbg_show(void) } } OSI_TRACE_ERROR("--> count %d\n", mem_dbg_count); + OSI_TRACE_ERROR("--> size %dB\n--> max size %dB\n", mem_dbg_total_size, mem_dbg_max_size); +} + +uint32_t osi_mem_dbg_get_max_size(void) +{ + return mem_dbg_max_size; +} + +uint32_t osi_mem_dbg_get_total_size(void) +{ + return mem_dbg_total_size; } #endif diff --git a/components/bt/bluedroid/osi/include/osi/allocator.h b/components/bt/bluedroid/osi/include/osi/allocator.h index f30987e058..f5e87a473a 100644 --- a/components/bt/bluedroid/osi/include/osi/allocator.h +++ b/components/bt/bluedroid/osi/include/osi/allocator.h @@ -35,6 +35,8 @@ void osi_mem_dbg_init(void); void osi_mem_dbg_record(void *p, int size, const char *func, int line); void osi_mem_dbg_clean(void *p, const char *func, int line); void osi_mem_dbg_show(void); +uint32_t osi_mem_dbg_get_max_size(void); +uint32_t osi_mem_dbg_get_total_size(void); #if HEAP_ALLOCATION_FROM_SPIRAM_FIRST #define osi_malloc(size) \ @@ -67,9 +69,9 @@ void osi_mem_dbg_show(void); (void *)p; \ }) -#define osi_calloc(size) \ -({ \ - void *p; \ +#define osi_calloc(size) \ +({ \ + void *p; \ p = calloc(1, (size)); \ osi_mem_dbg_record(p, size, __func__, __LINE__); \ (void *)p; \ From 930c304a57b618d9710264cb87560542b53734fc Mon Sep 17 00:00:00 2001 From: baohongde Date: Wed, 10 Oct 2018 16:10:20 +0800 Subject: [PATCH 089/486] components/bt: Combine two hci task into one --- components/bt/bluedroid/hci/hci_hal_h4.c | 18 +++++------------- components/bt/bluedroid/hci/hci_layer.c | 6 +++--- .../bt/bluedroid/hci/include/hci/hci_hal.h | 2 +- 3 files changed, 9 insertions(+), 17 deletions(-) diff --git a/components/bt/bluedroid/hci/hci_hal_h4.c b/components/bt/bluedroid/hci/hci_hal_h4.c index b31deda79c..5985e32ae2 100644 --- a/components/bt/bluedroid/hci/hci_hal_h4.c +++ b/components/bt/bluedroid/hci/hci_hal_h4.c @@ -27,12 +27,6 @@ #include "esp_bt.h" #include "stack/hcimsgs.h" -#define HCI_H4_TASK_PINNED_TO_CORE (TASK_PINNED_TO_CORE) -#define HCI_H4_TASK_STACK_SIZE (2048 + BT_TASK_EXTRA_STACK_SIZE) -#define HCI_H4_TASK_PRIO (BT_TASK_MAX_PRIORITIES - 4) -#define HCI_H4_TASK_NAME "hciH4T" - - #if (C2H_FLOW_CONTROL_INCLUDED == TRUE) #include "l2c_int.h" #endif ///C2H_FLOW_CONTROL_INCLUDED == TRUE @@ -105,9 +99,11 @@ static void hci_hal_env_deinit(void) hci_hal_env.rx_q = NULL; } -static bool hal_open(const hci_hal_callbacks_t *upper_callbacks) +static bool hal_open(const hci_hal_callbacks_t *upper_callbacks, void *task_thread) { assert(upper_callbacks != NULL); + assert(task_thread != NULL); + callbacks = upper_callbacks; #if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE) hci_hal_env_init(HCI_HAL_SERIAL_BUFFER_SIZE, BLE_ADV_REPORT_FLOW_CONTROL_NUM + L2CAP_HOST_FC_ACL_BUFS + QUEUE_SIZE_MAX); // adv flow control num + ACL flow control num + hci cmd numeber @@ -115,10 +111,7 @@ static bool hal_open(const hci_hal_callbacks_t *upper_callbacks) hci_hal_env_init(HCI_HAL_SERIAL_BUFFER_SIZE, QUEUE_SIZE_MAX); #endif - hci_h4_thread = osi_thread_create(HCI_H4_TASK_NAME, HCI_H4_TASK_STACK_SIZE, HCI_H4_TASK_PRIO, HCI_H4_TASK_PINNED_TO_CORE, 1); - if (hci_h4_thread == NULL) { - return false; - } + hci_h4_thread = (osi_thread_t *)task_thread; //register vhci host cb if (esp_vhci_host_register_callback(&vhci_host_cb) != ESP_OK) { @@ -132,7 +125,6 @@ static void hal_close() { hci_hal_env_deinit(); - osi_thread_free(hci_h4_thread); hci_h4_thread = NULL; } @@ -180,7 +172,7 @@ static void hci_hal_h4_rx_handler(void *arg) bool hci_hal_h4_task_post(osi_thread_blocking_t blocking) { - return osi_thread_post(hci_h4_thread, hci_hal_h4_rx_handler, NULL, 0, blocking); + return osi_thread_post(hci_h4_thread, hci_hal_h4_rx_handler, NULL, 1, blocking); } #if (C2H_FLOW_CONTROL_INCLUDED == TRUE) diff --git a/components/bt/bluedroid/hci/hci_layer.c b/components/bt/bluedroid/hci/hci_layer.c index 00c6f19e7c..e426528d45 100644 --- a/components/bt/bluedroid/hci/hci_layer.c +++ b/components/bt/bluedroid/hci/hci_layer.c @@ -37,7 +37,7 @@ #define HCI_HOST_TASK_PINNED_TO_CORE (TASK_PINNED_TO_CORE) #define HCI_HOST_TASK_STACK_SIZE (2048 + BT_TASK_EXTRA_STACK_SIZE) #define HCI_HOST_TASK_PRIO (BT_TASK_MAX_PRIORITIES - 3) -#define HCI_HOST_TASK_NAME "hciHostT" +#define HCI_HOST_TASK_NAME "hciT" typedef struct { uint16_t opcode; @@ -105,13 +105,13 @@ int hci_start_up(void) goto error; } - hci_host_thread = osi_thread_create(HCI_HOST_TASK_NAME, HCI_HOST_TASK_STACK_SIZE, HCI_HOST_TASK_PRIO, HCI_HOST_TASK_PINNED_TO_CORE, 1); + hci_host_thread = osi_thread_create(HCI_HOST_TASK_NAME, HCI_HOST_TASK_STACK_SIZE, HCI_HOST_TASK_PRIO, HCI_HOST_TASK_PINNED_TO_CORE, 2); if (hci_host_thread == NULL) { return -2; } packet_fragmenter->init(&packet_fragmenter_callbacks); - hal->open(&hal_callbacks); + hal->open(&hal_callbacks, hci_host_thread); hci_host_startup_flag = true; return 0; diff --git a/components/bt/bluedroid/hci/include/hci/hci_hal.h b/components/bt/bluedroid/hci/include/hci/hci_hal.h index 2928f29ad3..daf3dfb81a 100644 --- a/components/bt/bluedroid/hci/include/hci/hci_hal.h +++ b/components/bt/bluedroid/hci/include/hci/hci_hal.h @@ -51,7 +51,7 @@ typedef struct hci_hal_t { //bool (*init)(const hci_hal_callbacks_t *upper_callbacks); // Connect to the underlying hardware, and let data start flowing. - bool (*open)(const hci_hal_callbacks_t *upper_callbacks); + bool (*open)(const hci_hal_callbacks_t *upper_callbacks, void *task_thread); // Disconnect from the underlying hardware, and close the HAL. // "Daisy, Daisy..." void (*close)(void); From 485c896740883007dbee7a0a937ba6563d15e7d3 Mon Sep 17 00:00:00 2001 From: baohongde Date: Wed, 31 Oct 2018 17:11:54 +0800 Subject: [PATCH 090/486] components/bt: Combine A2DP sink task and A2DP source task into btc task --- components/bt/bluedroid/btc/core/btc_task.c | 8 +++---- .../btc/profile/std/a2dp/btc_a2dp_sink.c | 18 +++------------- .../btc/profile/std/a2dp/btc_a2dp_source.c | 21 +++++++------------ 3 files changed, 14 insertions(+), 33 deletions(-) diff --git a/components/bt/bluedroid/btc/core/btc_task.c b/components/bt/bluedroid/btc/core/btc_task.c index e77f5ac4b9..72866dc3c5 100644 --- a/components/bt/bluedroid/btc/core/btc_task.c +++ b/components/bt/bluedroid/btc/core/btc_task.c @@ -55,7 +55,7 @@ #define BTC_TASK_NAME "btcT" #define BTC_TASK_PRIO (BT_TASK_MAX_PRIORITIES - 6) -static osi_thread_t *btc_thread; +osi_thread_t *btc_thread; static const btc_func_t profile_tab[BTC_PID_NUM] = { [BTC_PID_MAIN_INIT] = {btc_main_call_handler, NULL }, @@ -135,7 +135,7 @@ static bt_status_t btc_task_post(btc_msg_t *msg, osi_thread_blocking_t blocking) memcpy(lmsg, msg, sizeof(btc_msg_t)); - if (osi_thread_post(btc_thread, btc_thread_handler, lmsg, 0, blocking) == false) { + if (osi_thread_post(btc_thread, btc_thread_handler, lmsg, 2, blocking) == false) { return BT_STATUS_BUSY; } @@ -262,7 +262,7 @@ static void btc_deinit_mem(void) { int btc_init(void) { - btc_thread = osi_thread_create("BTC_TASK", BTC_TASK_STACK_SIZE, BTC_TASK_PRIO, BTC_TASK_PINNED_TO_CORE, 1); + btc_thread = osi_thread_create("BTC_TASK", BTC_TASK_STACK_SIZE, BTC_TASK_PRIO, BTC_TASK_PINNED_TO_CORE, 3); if (btc_thread == NULL) { return BT_STATUS_NOMEM; } @@ -297,7 +297,7 @@ void btc_deinit(void) bool btc_check_queue_is_congest(void) { - if (osi_thread_queue_wait_size(btc_thread, 0) >= QUEUE_CONGEST_SIZE) { + if (osi_thread_queue_wait_size(btc_thread, 2) >= QUEUE_CONGEST_SIZE) { return true; } diff --git a/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_sink.c b/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_sink.c index ad1f5558b6..f1d51d80e3 100644 --- a/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_sink.c +++ b/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_sink.c @@ -45,12 +45,7 @@ #if (BTC_AV_SINK_INCLUDED == TRUE) -/* Macro */ -#define BTC_A2DP_SINK_TASK_PINNED_TO_CORE (TASK_PINNED_TO_CORE) -#define BTC_A2DP_SINK_TASK_STACK_SIZE (A2DP_SINK_TASK_STACK_SIZE + BT_TASK_EXTRA_STACK_SIZE) // by menuconfig -#define BTC_A2DP_SINK_TASK_NAME "BtA2dSinkT" -#define BTC_A2DP_SINK_TASK_PRIO (BT_TASK_MAX_PRIORITIES - 3) - +extern osi_thread_t *btc_thread; /***************************************************************************** ** Constants @@ -242,10 +237,7 @@ bool btc_a2dp_sink_startup(void) APPL_TRACE_EVENT("## A2DP SINK START MEDIA THREAD ##"); - a2dp_sink_local_param.btc_aa_snk_task_hdl = osi_thread_create(BTC_A2DP_SINK_TASK_NAME, BTC_A2DP_SINK_TASK_STACK_SIZE, BTC_A2DP_SINK_TASK_PRIO, BTC_A2DP_SINK_TASK_PINNED_TO_CORE, 2); - if (a2dp_sink_local_param.btc_aa_snk_task_hdl == NULL) { - goto error_exit; - } + a2dp_sink_local_param.btc_aa_snk_task_hdl = btc_thread; if (btc_a2dp_sink_ctrl_post(BTC_MEDIA_TASK_SINK_INIT, NULL) == false) { goto error_exit; @@ -257,10 +249,7 @@ bool btc_a2dp_sink_startup(void) error_exit:; APPL_TRACE_ERROR("%s unable to start up media thread\n", __func__); - if (a2dp_sink_local_param.btc_aa_snk_task_hdl != NULL) { - osi_thread_free(a2dp_sink_local_param.btc_aa_snk_task_hdl); - a2dp_sink_local_param.btc_aa_snk_task_hdl = NULL; - } + a2dp_sink_local_param.btc_aa_snk_task_hdl = NULL; #if A2D_DYNAMIC_MEMORY == TRUE osi_free(a2dp_sink_local_param_ptr); @@ -282,7 +271,6 @@ void btc_a2dp_sink_shutdown(void) future_await(a2dp_sink_local_param.btc_a2dp_sink_future); a2dp_sink_local_param.btc_a2dp_sink_future = NULL; - osi_thread_free(a2dp_sink_local_param.btc_aa_snk_task_hdl); a2dp_sink_local_param.btc_aa_snk_task_hdl = NULL; #if A2D_DYNAMIC_MEMORY == TRUE diff --git a/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_source.c b/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_source.c index 430d6f5fff..3591901002 100644 --- a/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_source.c +++ b/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_source.c @@ -50,12 +50,7 @@ #if BTC_AV_SRC_INCLUDED -/* Macro */ -#define BTC_A2DP_SOURCE_TASK_PINNED_TO_CORE (TASK_PINNED_TO_CORE) -#define BTC_A2DP_SOURCE_TASK_STACK_SIZE (A2DP_SOURCE_TASK_STACK_SIZE + BT_TASK_EXTRA_STACK_SIZE) // by menuconfig -#define BTC_A2DP_SOURCE_TASK_NAME "BtA2dSourceT" -#define BTC_A2DP_SOURCE_TASK_PRIO (BT_TASK_MAX_PRIORITIES - 3) - +extern osi_thread_t *btc_thread; /***************************************************************************** ** Constants @@ -243,18 +238,18 @@ bool btc_a2dp_source_is_task_shutting_down(void) return btc_a2dp_source_state == BTC_A2DP_SOURCE_STATE_SHUTTING_DOWN; } -static void btc_a2dp_source_ctrl_post(uint32_t sig, void *param) +static bool btc_a2dp_source_ctrl_post(uint32_t sig, void *param) { a2dp_src_task_evt_t *evt = (a2dp_src_task_evt_t *)osi_malloc(sizeof(a2dp_src_task_evt_t)); if (evt == NULL) { - return; + return false; } evt->sig = sig; evt->param = param; - osi_thread_post(a2dp_source_local_param.btc_aa_src_task_hdl, btc_a2dp_source_ctrl_handler, evt, 0, OSI_THREAD_BLOCKING); + return osi_thread_post(a2dp_source_local_param.btc_aa_src_task_hdl, btc_a2dp_source_ctrl_handler, evt, 0, OSI_THREAD_BLOCKING); } static void btc_a2dp_source_ctrl_handler(void *arg) @@ -318,19 +313,18 @@ bool btc_a2dp_source_startup(void) APPL_TRACE_EVENT("## A2DP SOURCE START MEDIA THREAD ##"); - a2dp_source_local_param.btc_aa_src_task_hdl = osi_thread_create(BTC_A2DP_SOURCE_TASK_NAME, BTC_A2DP_SOURCE_TASK_STACK_SIZE, BTC_A2DP_SOURCE_TASK_PRIO, BTC_A2DP_SOURCE_TASK_PINNED_TO_CORE, 2); - if (a2dp_source_local_param.btc_aa_src_task_hdl == NULL) { + a2dp_source_local_param.btc_aa_src_task_hdl = btc_thread; + + if (btc_a2dp_source_ctrl_post(BTC_MEDIA_TASK_INIT, NULL) == false) { goto error_exit; } - btc_a2dp_source_ctrl_post(BTC_MEDIA_TASK_INIT, NULL); APPL_TRACE_EVENT("## A2DP SOURCE MEDIA THREAD STARTED ##\n"); return true; error_exit:; APPL_TRACE_ERROR("%s unable to start up media thread\n", __func__); - osi_thread_free(a2dp_source_local_param.btc_aa_src_task_hdl); a2dp_source_local_param.btc_aa_src_task_hdl = NULL; #if A2D_DYNAMIC_MEMORY == TRUE @@ -353,7 +347,6 @@ void btc_a2dp_source_shutdown(void) future_await(a2dp_source_local_param.btc_a2dp_source_future); a2dp_source_local_param.btc_a2dp_source_future = NULL; - osi_thread_free(a2dp_source_local_param.btc_aa_src_task_hdl); a2dp_source_local_param.btc_aa_src_task_hdl = NULL; #if A2D_DYNAMIC_MEMORY == TRUE From 84248221509670c132e96289dd9061b970499f00 Mon Sep 17 00:00:00 2001 From: Renz Christian Bagaporo Date: Sat, 15 Jun 2019 11:17:16 +0800 Subject: [PATCH 091/486] cmake: set CONFIG_DIR build property Add CONFIG_DIR as a build property, so that components don't have to derive it from one of the generated config files. --- components/esp32/CMakeLists.txt | 3 +-- components/esp_wifi/CMakeLists.txt | 3 ++- tools/cmake/component.cmake | 7 +++---- tools/cmake/kconfig.cmake | 1 + 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/components/esp32/CMakeLists.txt b/components/esp32/CMakeLists.txt index abcc0ada17..9d763b41b9 100644 --- a/components/esp32/CMakeLists.txt +++ b/components/esp32/CMakeLists.txt @@ -70,8 +70,7 @@ else() #symbols in it. target_link_libraries(${COMPONENT_LIB} INTERFACE "-u ld_include_panic_highint_hdl") - idf_build_get_property(sdkconfig_header SDKCONFIG_HEADER) - get_filename_component(config_dir ${sdkconfig_header} DIRECTORY) + idf_build_get_property(config_dir CONFIG_DIR) # Preprocess esp32.ld linker script to include configuration, becomes esp32_out.ld set(LD_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ld) add_custom_command( diff --git a/components/esp_wifi/CMakeLists.txt b/components/esp_wifi/CMakeLists.txt index e4ba2db060..5349e905ac 100644 --- a/components/esp_wifi/CMakeLists.txt +++ b/components/esp_wifi/CMakeLists.txt @@ -44,11 +44,12 @@ if(CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION) # To get the phy_init_data.bin file, compile phy_init_data.h as a C file and then objcopy # the object file to a raw binary + idf_build_get_property(config_dir CONFIG_DIR) add_custom_command( OUTPUT ${phy_init_data_bin} DEPENDS ${CMAKE_CURRENT_LIST_DIR}/${idf_target}/include/phy_init_data.h COMMAND ${CMAKE_C_COMPILER} -x c -c - -I ${esp_common_dir}/include -I ${CMAKE_CURRENT_LIST_DIR}/include -I ${build_dir}/config + -I ${esp_common_dir}/include -I ${CMAKE_CURRENT_LIST_DIR}/include -I ${config_dir} -o phy_init_data.obj ${CMAKE_CURRENT_LIST_DIR}/${idf_target}/include/phy_init_data.h COMMAND ${CMAKE_OBJCOPY} -O binary phy_init_data.obj ${phy_init_data_bin} diff --git a/tools/cmake/component.cmake b/tools/cmake/component.cmake index 22b3528ec6..39b45a738f 100644 --- a/tools/cmake/component.cmake +++ b/tools/cmake/component.cmake @@ -427,8 +427,7 @@ function(idf_component_register) list(REMOVE_ITEM common_reqs ${component_lib}) link_libraries(${common_reqs}) - idf_build_get_property(sdkconfig_h SDKCONFIG_HEADER) - get_filename_component(sdkconfig_h ${sdkconfig_h} DIRECTORY) + idf_build_get_property(config_dir CONFIG_DIR) # The contents of 'sources' is from the __component_add_sources call if(sources OR __EMBED_FILES OR __EMBED_TXTFILES) @@ -436,14 +435,14 @@ function(idf_component_register) __component_set_property(${component_target} COMPONENT_TYPE LIBRARY) target_include_directories(${component_lib} PUBLIC ${__INCLUDE_DIRS}) target_include_directories(${component_lib} PRIVATE ${__PRIV_INCLUDE_DIRS}) - target_include_directories(${component_lib} PUBLIC ${sdkconfig_h}) + target_include_directories(${component_lib} PUBLIC ${config_dir}) set_target_properties(${component_lib} PROPERTIES OUTPUT_NAME ${COMPONENT_NAME}) __ldgen_add_component(${component_lib}) else() add_library(${component_lib} INTERFACE) __component_set_property(${component_target} COMPONENT_TYPE CONFIG_ONLY) target_include_directories(${component_lib} INTERFACE ${__INCLUDE_DIRS}) - target_include_directories(${component_lib} INTERFACE ${sdkconfig_h}) + target_include_directories(${component_lib} INTERFACE ${config_dir}) endif() # Alias the static/interface library created for linking to external targets. diff --git a/tools/cmake/kconfig.cmake b/tools/cmake/kconfig.cmake index 28be99371e..08344e78d9 100644 --- a/tools/cmake/kconfig.cmake +++ b/tools/cmake/kconfig.cmake @@ -189,6 +189,7 @@ function(__kconfig_generate_config sdkconfig sdkconfig_defaults) idf_build_set_property(SDKCONFIG_JSON ${sdkconfig_json}) idf_build_set_property(SDKCONFIG_CMAKE ${sdkconfig_cmake}) idf_build_set_property(SDKCONFIG_JSON_MENUS ${sdkconfig_json_menus}) + idf_build_set_property(CONFIG_DIR ${config_dir}) idf_build_get_property(menuconfig_depends __MENUCONFIG_DEPENDS) From 2fff500a1c60f44741c4279b6ad177eba6331438 Mon Sep 17 00:00:00 2001 From: Renz Christian Bagaporo Date: Sat, 15 Jun 2019 11:20:43 +0800 Subject: [PATCH 092/486] cmake: export IDF_TARGET to menuconfig Pass value of IDF_TARGET to menuconfig invocation. --- tools/cmake/kconfig.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/cmake/kconfig.cmake b/tools/cmake/kconfig.cmake index 08344e78d9..97702935ee 100644 --- a/tools/cmake/kconfig.cmake +++ b/tools/cmake/kconfig.cmake @@ -205,6 +205,7 @@ function(__kconfig_generate_config sdkconfig sdkconfig_defaults) "COMPONENT_KCONFIGS_PROJBUILD=${kconfig_projbuilds}" "IDF_CMAKE=y" "KCONFIG_CONFIG=${sdkconfig}" + "IDF_TARGET=${idf_target}" ${mconf} ${root_kconfig} # VERBATIM cannot be used here because it cannot handle ${mconf}="winpty mconf-idf" and the escaping must be # done manually From 64d37f5cb9d3e6b09bc37562c5bd9ad272516bab Mon Sep 17 00:00:00 2001 From: Renz Christian Bagaporo Date: Sat, 15 Jun 2019 11:26:32 +0800 Subject: [PATCH 093/486] cmake: fix issues with build process Fix issue when COMPONENTS are is not specified for idf_build_process, no component is included in the build. --- tools/cmake/build.cmake | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tools/cmake/build.cmake b/tools/cmake/build.cmake index 84f6f154fc..ff43ba1023 100644 --- a/tools/cmake/build.cmake +++ b/tools/cmake/build.cmake @@ -367,15 +367,20 @@ macro(idf_build_process target) __build_check_python() idf_build_set_property(__COMPONENT_REQUIRES_COMMON ${target} APPEND) - __component_get_requirements() # Perform early expansion of component CMakeLists.txt in CMake scripting mode. # It is here we retrieve the public and private requirements of each component. # It is also here we add the common component requirements to each component's # own requirements. + __component_get_requirements() + + idf_build_get_property(component_targets __COMPONENT_TARGETS) # Finally, do component expansion. In this case it simply means getting a final list # of build component targets given the requirements set by each component. + + # Check if we need to trim the components first, and build initial components list + # from that. if(__COMPONENTS) unset(component_targets) foreach(component ${__COMPONENTS}) @@ -390,7 +395,7 @@ macro(idf_build_process target) foreach(component_target ${component_targets}) __build_expand_requirements(${component_target}) endforeach() - unset(__COMPONENT_TARGETS_SEEN) + idf_build_unset_property(__COMPONENT_TARGETS_SEEN) # Get a list of common component requirements in component targets form (previously # we just have a list of component names) From 7a19894aec84dd18ead58687212ff3fb4e15f6ea Mon Sep 17 00:00:00 2001 From: Renz Christian Bagaporo Date: Mon, 17 Jun 2019 12:20:12 +0800 Subject: [PATCH 094/486] esptool_py: better display logs when generating binary Since OUTPUT argument of custom command does not currently support generator expressions, the project image is only generated as a side effect. The primary generated file is a timestamp file. Unfortunately as a consequence the output logs when the binary is about to be generated is not as helpful anymore. Set a custom comment that is more descriptive of what is happening, and provide more feedback as to what has been generated. --- components/esptool_py/project_include.cmake | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/components/esptool_py/project_include.cmake b/components/esptool_py/project_include.cmake index d6c888f437..b847680c88 100644 --- a/components/esptool_py/project_include.cmake +++ b/components/esptool_py/project_include.cmake @@ -75,28 +75,33 @@ set(PROJECT_BIN "${elf_name}.bin") # # Add 'app.bin' target - generates with elf2image # -add_custom_command(OUTPUT "${build_dir}/.app_hash" +add_custom_command(OUTPUT "${build_dir}/.bin_timestamp" COMMAND ${ESPTOOLPY} elf2image ${ESPTOOLPY_ELF2IMAGE_FLASH_OPTIONS} ${ESPTOOLPY_ELF2IMAGE_OPTIONS} -o "${build_dir}/${unsigned_project_binary}" "${elf}" - COMMAND ${CMAKE_COMMAND} -E md5sum "${build_dir}/${unsigned_project_binary}" > "${build_dir}/.app_hash" + COMMAND ${CMAKE_COMMAND} -E echo "Generated ${build_dir}/${unsigned_project_binary}" + COMMAND ${CMAKE_COMMAND} -E md5sum "${build_dir}/${unsigned_project_binary}" > "${build_dir}/.bin_timestamp" DEPENDS ${elf} VERBATIM WORKING_DIRECTORY ${build_dir} + COMMENT "Generating binary image from built executable" ) -add_custom_target(gen_project_binary DEPENDS "${build_dir}/.app_hash") +add_custom_target(gen_project_binary DEPENDS "${build_dir}/.bin_timestamp") if(NOT BOOTLOADER_BUILD AND CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES) # for locally signed secure boot image, add a signing step to get from unsigned app to signed app - add_custom_command(OUTPUT "${build_dir}/.signed_app_hash" + add_custom_command(OUTPUT "${build_dir}/.signed_bin_timestamp" COMMAND ${ESPSECUREPY} sign_data --keyfile ${secure_boot_signing_key} -o "${build_dir}/${PROJECT_BIN}" "${build_dir}/${unsigned_project_binary}" - COMMAND ${CMAKE_COMMAND} -E md5sum "${build_dir}/${PROJECT_BIN}" > "${build_dir}/.signed_app_hash" - DEPENDS "${build_dir}/.app_hash" + COMMAND ${CMAKE_COMMAND} -E echo "Generated signed binary image ${build_dir}/${PROJECT_BIN}" + "from ${build_dir}/${unsigned_project_binary}" + COMMAND ${CMAKE_COMMAND} -E md5sum "${build_dir}/${PROJECT_BIN}" > "${build_dir}/.signed_bin_timestamp" + DEPENDS "${build_dir}/.bin_timestamp" VERBATIM + COMMENT "Generating signed binary image" ) - add_custom_target(gen_signed_project_binary DEPENDS "${build_dir}/.signed_app_hash") + add_custom_target(gen_signed_project_binary DEPENDS "${build_dir}/.signed_bin_timestamp") add_dependencies(gen_project_binary gen_signed_project_binary) endif() @@ -106,6 +111,7 @@ else() add_custom_target(bootloader ALL DEPENDS gen_project_binary) endif() + if(NOT BOOTLOADER_BUILD AND CONFIG_SECURE_BOOT_ENABLED AND NOT CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES) From 11924d76cb8b81c0b060bb1cd4b3a40ef44caff2 Mon Sep 17 00:00:00 2001 From: Renz Christian Bagaporo Date: Mon, 17 Jun 2019 12:14:02 +0800 Subject: [PATCH 095/486] cmake: clarify build trimming docs How idf_build_component and the COMPONENTS argument to idf_build_process interact is not clear/misleading. Clarify their interaction in the docs. Closes: https://github.com/espressif/esp-idf/issues/3630 --- docs/en/api-guides/build-system-cmake.rst | 10 ++++++++-- tools/cmake/build.cmake | 21 ++++++++++++++++----- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/docs/en/api-guides/build-system-cmake.rst b/docs/en/api-guides/build-system-cmake.rst index 08f9e3a396..455149fec5 100644 --- a/docs/en/api-guides/build-system-cmake.rst +++ b/docs/en/api-guides/build-system-cmake.rst @@ -943,7 +943,10 @@ the first element/member instead. idf_build_component(component_dir) -Add a directory *component_dir* that contains a component to the build. +Present a directory *component_dir* that contains a component to the build system. Relative paths are converted to absolute paths with respect to current directory. +All calls to this command must be performed before `idf_build_process`. + +This command does not guarantee that the component will be processed during build (see the `COMPONENTS` argument description for `idf_build_process`) .. code-block:: none @@ -970,7 +973,10 @@ The call requires the target chip to be specified with *target* argument. Option - SDKCONFIG - output path of generated sdkconfig file; defaults to PROJECT_DIR/sdkconfig or CMAKE_SOURCE_DIR/sdkconfig depending if PROJECT_DIR is set - SDKCONFIG_DEFAULTS - defaults file to use for the build; defaults to empty - BUILD_DIR - directory to place ESP-IDF build-related artifacts, such as generated binaries, text files, components; defaults to CMAKE_BINARY_DIR -- COMPONENTS - starting components for trimming the build; components not in the list are automatically if they are required in the expanded dependency tree +- COMPONENTS - select components to process among the components known by the build system (added via `idf_build_component`). This argument is used to trim the build. + Other components are automatically added if they are required in the dependency chain, i.e. + the public and private requirements of the components in this list are automatically added, and in turn the public and private requirements of those requirements, + so on and so forth. If not specified, all components known to the build system are processed. .. code-block:: none diff --git a/tools/cmake/build.cmake b/tools/cmake/build.cmake index ff43ba1023..5383581f12 100644 --- a/tools/cmake/build.cmake +++ b/tools/cmake/build.cmake @@ -163,12 +163,14 @@ endfunction() # idf_build_component # -# @brief Specify component directory for the build system to process. +# @brief Present a directory that contains a component to the build system. # Relative paths are converted to absolute paths with respect to current directory. -# Any component that needs to be processed has to be specified using this -# command before calling idf_build_process. +# All calls to this command must be performed before idf_build_process. # -# @param[in] component_dir directory of the component to process +# @note This command does not guarantee that the component will be processed +# during build (see the COMPONENTS argument description for command idf_build_process) +# +# @param[in] component_dir directory of the component function(idf_build_component component_dir) idf_build_get_property(prefix __PREFIX) __component_add(${component_dir} ${prefix} 0) @@ -335,7 +337,16 @@ endfunction() # @param[in, optional] SDKCONFIG_DEFAULTS (single value) config defaults file to use for the build; defaults # to none (Kconfig defaults or previously generated config are used) # @param[in, optional] BUILD_DIR (single value) directory for build artifacts; defautls to CMAKE_BINARY_DIR -# @param[in, optional] COMPONENTS (multivalue) starting components for trimming build +# @param[in, optional] COMPONENTS (multivalue) select components to process among the components +# known by the build system +# (added via `idf_build_component`). This argument is used to trim the build. +# Other components are automatically added if they are required +# in the dependency chain, i.e. +# the public and private requirements of the components in this list +# are automatically added, and in +# turn the public and private requirements of those requirements, +# so on and so forth. If not specified, all components known to the build system +# are processed. macro(idf_build_process target) set(options) set(single_value PROJECT_DIR PROJECT_VER PROJECT_NAME BUILD_DIR SDKCONFIG SDKCONFIG_DEFAULTS) From 07fef85a23111a4a238222a0273eae089e8faa05 Mon Sep 17 00:00:00 2001 From: baohongde Date: Mon, 12 Nov 2018 11:08:19 +0800 Subject: [PATCH 096/486] components/bt: Separation of BT and BLE --- components/bt/Kconfig | 16 +++-- components/bt/bluedroid/bta/dm/bta_dm_act.c | 47 +++++++++++++-- components/bt/bluedroid/bta/dm/bta_dm_api.c | 17 ++++-- .../bt/bluedroid/bta/dm/include/bta_dm_int.h | 17 ++++-- .../bt/bluedroid/bta/include/bta/bta_api.h | 10 ++-- .../bt/bluedroid/bta/include/bta/bta_dm_co.h | 1 + .../bt/bluedroid/btc/core/btc_ble_storage.c | 4 ++ components/bt/bluedroid/btc/core/btc_dm.c | 8 ++- components/bt/bluedroid/btc/core/btc_main.c | 5 ++ components/bt/bluedroid/btc/core/btc_task.c | 9 +++ .../btc/profile/std/gap/btc_gap_ble.c | 2 + .../common/include/common/bt_target.h | 22 +++---- .../common/include/common/bt_user_config.h | 6 ++ components/bt/bluedroid/stack/btm/btm_acl.c | 3 + components/bt/bluedroid/stack/btm/btm_ble.c | 26 ++++---- .../bt/bluedroid/stack/btm/btm_devctl.c | 4 +- components/bt/bluedroid/stack/btm/btm_sec.c | 41 +++++++++++-- .../bt/bluedroid/stack/btm/include/btm_int.h | 18 ++++-- components/bt/bluedroid/stack/btu/btu_hcif.c | 2 + .../bluedroid/stack/include/stack/btm_api.h | 10 ++-- .../bluedroid/stack/include/stack/l2c_api.h | 2 +- .../bluedroid/stack/include/stack/smp_api.h | 4 +- components/bt/bluedroid/stack/l2cap/l2c_api.c | 4 +- components/bt/bluedroid/stack/l2cap/l2c_ble.c | 44 +++++++------- .../bt/bluedroid/stack/l2cap/l2c_link.c | 8 ++- .../bt/bluedroid/stack/l2cap/l2c_main.c | 4 ++ .../bt/bluedroid/stack/l2cap/l2c_utils.c | 7 ++- .../bt/bluedroid/stack/smp/include/smp_int.h | 4 +- components/bt/bluedroid/stack/smp/smp_act.c | 59 +++++++++++++++++-- components/bt/bluedroid/stack/smp/smp_api.c | 4 ++ .../bt/bluedroid/stack/smp/smp_br_main.c | 2 +- components/bt/bluedroid/stack/smp/smp_keys.c | 9 ++- components/bt/bluedroid/stack/smp/smp_l2c.c | 15 ++++- components/bt/bluedroid/stack/smp/smp_main.c | 8 ++- components/bt/bluedroid/stack/smp/smp_utils.c | 21 +++++-- .../bluetooth/a2dp_sink/sdkconfig.defaults | 4 +- .../bluetooth/a2dp_source/sdkconfig.defaults | 4 +- .../bluetooth/bt_discovery/sdkconfig.defaults | 4 +- .../bt_spp_acceptor/sdkconfig.defaults | 1 + .../bt_spp_initiator/sdkconfig.defaults | 1 + .../bt_spp_vfs_acceptor/sdkconfig.defaults | 1 + .../bt_spp_vfs_initiator/sdkconfig.defaults | 1 + 42 files changed, 354 insertions(+), 125 deletions(-) diff --git a/components/bt/Kconfig b/components/bt/Kconfig index a15bd7dc2f..d292e66c20 100644 --- a/components/bt/Kconfig +++ b/components/bt/Kconfig @@ -1,6 +1,5 @@ menu Bluetooth - config BT_ENABLED bool "Bluetooth" help @@ -475,9 +474,16 @@ menu Bluetooth help This enables the Secure Simple Pairing. If disable this option, Bluedroid will only support Legacy Pairing + config BT_BLE_ENABLED + bool "Bluetooth Low Energy" + depends on BT_BLUEDROID_ENABLED + default y + help + This enables Bluetooth Low Energy + config BT_GATTS_ENABLE bool "Include GATT server module(GATTS)" - depends on BT_BLUEDROID_ENABLED && (BTDM_CTRL_MODE_BTDM || BTDM_CTRL_MODE_BLE_ONLY) + depends on BT_BLE_ENABLED default y help This option can be disabled when the app work only on gatt client mode @@ -510,21 +516,21 @@ menu Bluetooth config BT_GATTC_ENABLE bool "Include GATT client module(GATTC)" - depends on BT_BLUEDROID_ENABLED && (BTDM_CTRL_MODE_BTDM || BTDM_CTRL_MODE_BLE_ONLY) + depends on BT_BLE_ENABLED default y help This option can be close when the app work only on gatt server mode config BT_GATTC_CACHE_NVS_FLASH bool "Save gattc cache data to nvs flash" - depends on BT_GATTC_ENABLE && (BTDM_CTRL_MODE_BTDM || BTDM_CTRL_MODE_BLE_ONLY) + depends on BT_GATTC_ENABLE default n help This select can save gattc cache data to nvs flash config BT_BLE_SMP_ENABLE bool "Include BLE security module(SMP)" - depends on BT_BLUEDROID_ENABLED && (BTDM_CTRL_MODE_BTDM || BTDM_CTRL_MODE_BLE_ONLY) + depends on BT_BLE_ENABLED default y help This option can be close when the app not used the ble security connect. diff --git a/components/bt/bluedroid/bta/dm/bta_dm_act.c b/components/bt/bluedroid/bta/dm/bta_dm_act.c index efb685ecd6..a6fefeacfe 100644 --- a/components/bt/bluedroid/bta/dm/bta_dm_act.c +++ b/components/bt/bluedroid/bta/dm/bta_dm_act.c @@ -60,7 +60,9 @@ static void bta_dm_sdp_callback (UINT16 sdp_status); #endif ///SDP_INCLUDED == TRUE #if (SMP_INCLUDED == TRUE) static UINT8 bta_dm_authorize_cback (BD_ADDR bd_addr, DEV_CLASS dev_class, BD_NAME bd_name, UINT8 *service_name, UINT8 service_id, BOOLEAN is_originator); +#if (CLASSIC_BT_INCLUDED == TRUE) static UINT8 bta_dm_pin_cback (BD_ADDR bd_addr, DEV_CLASS dev_class, BD_NAME bd_name, BOOLEAN min_16_digit); +#endif /// CLASSIC_BT_INCLUDED == TRUE static UINT8 bta_dm_new_link_key_cback(BD_ADDR bd_addr, DEV_CLASS dev_class, BD_NAME bd_name, LINK_KEY key, UINT8 key_type); static UINT8 bta_dm_authentication_complete_cback(BD_ADDR bd_addr, DEV_CLASS dev_class, BD_NAME bd_name, int result); #endif ///SMP_INCLUDED == TRUE @@ -125,9 +127,12 @@ static void bta_dm_ctrl_features_rd_cmpl_cback(tBTM_STATUS result); #if (SMP_INCLUDED == TRUE) static void bta_dm_remove_sec_dev_entry(BD_ADDR remote_bd_addr); #endif ///SMP_INCLUDED == TRUE +#if (BLE_INCLUDED == TRUE) static void bta_dm_observe_results_cb(tBTM_INQ_RESULTS *p_inq, UINT8 *p_eir); static void bta_dm_observe_cmpl_cb(void *p_result); static void bta_dm_observe_discard_cb (uint32_t num_dis); +#endif ///BLE_INCLUDED == TRUE + static void bta_dm_delay_role_switch_cback(TIMER_LIST_ENT *p_tle); extern void sdpu_uuid16_to_uuid128(UINT16 uuid16, UINT8 *p_uuid128); static void bta_dm_disable_timer_cback(TIMER_LIST_ENT *p_tle); @@ -215,7 +220,11 @@ const UINT32 bta_service_id_to_btm_srv_id_lkup_tbl [BTA_MAX_SERVICE_ID] = { #if (SMP_INCLUDED == TRUE) const tBTM_APPL_INFO bta_security = { &bta_dm_authorize_cback, +#if (CLASSIC_BT_INCLUDED == TRUE) &bta_dm_pin_cback, +#else + NULL, +#endif &bta_dm_new_link_key_cback, &bta_dm_authentication_complete_cback, &bta_dm_bond_cancel_complete_cback, @@ -581,7 +590,7 @@ void bta_dm_disable (tBTA_DM_MSG *p_data) bta_sys_start_timer(&bta_dm_cb.disable_timer, 0, 5000); } -#if BLE_PRIVACY_SPT == TRUE +#if BLE_INCLUDED == TRUE && BLE_PRIVACY_SPT == TRUE btm_ble_resolving_list_cleanup (); //by TH, because cmn_ble_vsc_cb.max_filter has something mistake as btm_ble_adv_filter_cleanup #endif @@ -698,25 +707,31 @@ void bta_dm_config_eir (tBTA_DM_MSG *p_data) void bta_dm_update_white_list(tBTA_DM_MSG *p_data) { +#if (BLE_INCLUDED == TRUE) BTM_BleUpdateAdvWhitelist(p_data->white_list.add_remove, p_data->white_list.remote_addr, p_data->white_list.addr_type, p_data->white_list.add_wl_cb); +#endif ///BLE_INCLUDED == TRUE } void bta_dm_ble_read_adv_tx_power(tBTA_DM_MSG *p_data) { +#if (BLE_INCLUDED == TRUE) if (p_data->read_tx_power.read_tx_power_cb != NULL) { BTM_BleReadAdvTxPower(p_data->read_tx_power.read_tx_power_cb); } else { APPL_TRACE_ERROR("%s(), the callback function can't be NULL.", __func__); } +#endif ///BLE_INCLUDED == TRUE } void bta_dm_ble_read_rssi(tBTA_DM_MSG *p_data) { +#if (BLE_INCLUDED == TRUE) if (p_data->rssi.read_rssi_cb != NULL) { BTM_ReadRSSI(p_data->rssi.remote_addr, p_data->rssi.transport, p_data->rssi.read_rssi_cb); } else { APPL_TRACE_ERROR("%s(), the callback function can't be NULL.", __func__); } +#endif ///BLE_INCLUDED == TRUE } /******************************************************************************* @@ -732,42 +747,52 @@ void bta_dm_ble_read_rssi(tBTA_DM_MSG *p_data) void bta_dm_set_visibility(tBTA_DM_MSG *p_data) { UINT16 window, interval; - UINT16 le_disc_mode = BTM_BleReadDiscoverability(); UINT16 disc_mode = BTM_ReadDiscoverability(&window, &interval); - UINT16 le_conn_mode = BTM_BleReadConnectability(); UINT16 conn_mode = BTM_ReadConnectability(&window, &interval); +#if (BLE_INCLUDED == TRUE) + UINT16 le_disc_mode = BTM_BleReadDiscoverability(); + UINT16 le_conn_mode = BTM_BleReadConnectability(); +#endif ///BLE_INCLUDED == TRUE /* set modes for Discoverability and connectability if not ignore */ if (p_data->set_visibility.disc_mode != (BTA_DM_IGNORE | BTA_DM_LE_IGNORE)) { +#if (BLE_INCLUDED == TRUE) if ((p_data->set_visibility.disc_mode & BTA_DM_LE_IGNORE) == BTA_DM_LE_IGNORE) { p_data->set_visibility.disc_mode = ((p_data->set_visibility.disc_mode & ~BTA_DM_LE_IGNORE) | le_disc_mode); } +#endif ///BLE_INCLUDED == TRUE if ((p_data->set_visibility.disc_mode & BTA_DM_IGNORE) == BTA_DM_IGNORE) { p_data->set_visibility.disc_mode = ((p_data->set_visibility.disc_mode & ~BTA_DM_IGNORE) | disc_mode); } +#if (CLASSIC_BT_INCLUDED == TRUE) BTM_SetDiscoverability(p_data->set_visibility.disc_mode, bta_dm_cb.inquiry_scan_window, bta_dm_cb.inquiry_scan_interval); +#endif } if (p_data->set_visibility.conn_mode != (BTA_DM_IGNORE | BTA_DM_LE_IGNORE)) { +#if (BLE_INCLUDED == TRUE) if ((p_data->set_visibility.conn_mode & BTA_DM_LE_IGNORE) == BTA_DM_LE_IGNORE) { p_data->set_visibility.conn_mode = ((p_data->set_visibility.conn_mode & ~BTA_DM_LE_IGNORE) | le_conn_mode); } +#endif ///BLE_INCLUDED == TRUE if ((p_data->set_visibility.conn_mode & BTA_DM_IGNORE) == BTA_DM_IGNORE) { p_data->set_visibility.conn_mode = ((p_data->set_visibility.conn_mode & ~BTA_DM_IGNORE) | conn_mode); } +#if (CLASSIC_BT_INCLUDED == TRUE) BTM_SetConnectability(p_data->set_visibility.conn_mode, bta_dm_cb.page_scan_window, bta_dm_cb.page_scan_interval); +#endif } /* Send False or True if not ignore */ @@ -1096,7 +1121,9 @@ void bta_dm_bond_cancel (tBTA_DM_MSG *p_data) *******************************************************************************/ void bta_dm_set_pin_type (tBTA_DM_MSG *p_data) { +#if (CLASSIC_BT_INCLUDED == TRUE) BTM_SetPinType (p_data->set_pin_type.pin_type, p_data->set_pin_type.p_pin, p_data->set_pin_type.pin_len); +#endif ///CLASSIC_BT_INCLUDED == TRUE } /******************************************************************************* @@ -1111,6 +1138,7 @@ void bta_dm_set_pin_type (tBTA_DM_MSG *p_data) *******************************************************************************/ void bta_dm_pin_reply (tBTA_DM_MSG *p_data) { +#if (CLASSIC_BT_INCLUDED == TRUE) UINT32 trusted_mask[BTM_SEC_SERVICE_ARRAY_SIZE]; UINT32 *current_trusted_mask; @@ -1128,7 +1156,7 @@ void bta_dm_pin_reply (tBTA_DM_MSG *p_data) } else { BTM_PINCodeReply(p_data->pin_reply.bd_addr, BTM_NOT_AUTHORIZED, 0, NULL, trusted_mask ); } - +#endif ///CLASSIC_BT_INCLUDED == TRUE } #endif ///SMP_INCLUDED == TRUE @@ -2792,6 +2820,8 @@ static UINT8 bta_dm_authorize_cback (BD_ADDR bd_addr, DEV_CLASS dev_class, BD_NA } } + +#if (CLASSIC_BT_INCLUDED == TRUE) #if (BT_SSP_INCLUDED == TRUE) /******************************************************************************* ** @@ -2878,6 +2908,7 @@ static UINT8 bta_dm_pin_cback (BD_ADDR bd_addr, DEV_CLASS dev_class, BD_NAME bd_ bta_dm_cb.p_sec_cback(BTA_DM_PIN_REQ_EVT, &sec_event); return BTM_CMD_STARTED; } +#endif ///CLASSIC_BT_INCLUDED == TRUE /******************************************************************************* ** @@ -5797,6 +5828,7 @@ static void bta_dm_gattc_register(void) } } #endif /* GATTC_INCLUDED == TRUE */ + /******************************************************************************* ** ** Function btm_dm_start_disc_gatt_services @@ -5806,6 +5838,7 @@ static void bta_dm_gattc_register(void) ** Parameters: ** *******************************************************************************/ +#if (GATTC_INCLUDED == TRUE) static void btm_dm_start_disc_gatt_services (UINT16 conn_id) { tBT_UUID *p_uuid = bta_dm_search_cb.p_srvc_uuid + @@ -5817,6 +5850,7 @@ static void btm_dm_start_disc_gatt_services (UINT16 conn_id) /* always search for all services */ BTA_GATTC_ServiceSearchRequest(conn_id, p_uuid); } +#endif /* GATTC_INCLUDED == TRUE */ /******************************************************************************* ** @@ -5877,6 +5911,7 @@ static void bta_dm_gatt_disc_result(tBTA_GATT_ID service_id) ** Parameters: ** *******************************************************************************/ +#if (GATTC_INCLUDED == TRUE) static void bta_dm_gatt_disc_complete(UINT16 conn_id, tBTA_GATT_STATUS status) { tBTA_DM_MSG *p_msg; @@ -5934,6 +5969,7 @@ static void bta_dm_gatt_disc_complete(UINT16 conn_id, tBTA_GATT_STATUS status) bta_dm_search_cb.gatt_disc_active = FALSE; } } +#endif /* #if (GATTC_INCLUDED == TRUE) */ /******************************************************************************* ** @@ -6003,6 +6039,7 @@ static void bta_dm_cancel_gatt_discovery(BD_ADDR bd_addr) bta_dm_gatt_disc_complete(bta_dm_search_cb.conn_id, (tBTA_GATT_STATUS) BTA_GATT_ERROR); } #endif /* #if (GATTC_INCLUDED == TRUE) */ + /******************************************************************************* ** ** Function bta_dm_proc_open_evt @@ -6012,6 +6049,7 @@ static void bta_dm_cancel_gatt_discovery(BD_ADDR bd_addr) ** Parameters: ** *******************************************************************************/ +#if (GATTC_INCLUDED == TRUE) void bta_dm_proc_open_evt(tBTA_GATTC_OPEN *p_data) { UINT8 *p1; @@ -6042,6 +6080,7 @@ void bta_dm_proc_open_evt(tBTA_GATTC_OPEN *p_data) bta_dm_gatt_disc_complete(BTA_GATT_INVALID_CONN_ID, p_data->status); } } +#endif /* #if (GATTC_INCLUDED == TRUE) */ /******************************************************************************* ** diff --git a/components/bt/bluedroid/bta/dm/bta_dm_api.c b/components/bt/bluedroid/bta/dm/bta_dm_api.c index 927257733f..141d9ce2dd 100644 --- a/components/bt/bluedroid/bta/dm/bta_dm_api.c +++ b/components/bt/bluedroid/bta/dm/bta_dm_api.c @@ -225,6 +225,7 @@ void BTA_DmConfigEir(tBTA_DM_EIR_CONF *eir_config) } } +#if (BLE_INCLUDED == TRUE) void BTA_DmUpdateWhiteList(BOOLEAN add_remove, BD_ADDR remote_addr, tBLE_ADDR_TYPE addr_type, tBTA_ADD_WHITELIST_CBACK *add_wl_cb) { tBTA_DM_API_UPDATE_WHITE_LIST *p_msg; @@ -248,6 +249,7 @@ void BTA_DmBleReadAdvTxPower(tBTA_CMPL_CB *cmpl_cb) bta_sys_sendmsg(p_msg); } } +#endif ///BLE_INCLUDED == TRUE void BTA_DmBleReadRSSI(BD_ADDR remote_addr, tBTA_TRANSPORT transport, tBTA_CMPL_CB *cmpl_cb) { @@ -475,10 +477,10 @@ void BTA_DmBondCancel(BD_ADDR bd_addr) bdcpy(p_msg->bd_addr, bd_addr); bta_sys_sendmsg(p_msg); } - - } +#endif ///SMP_INCLUDED == TRUE +#if (CLASSIC_BT_INCLUDED == TRUE) /******************************************************************************* ** ** Function BTA_DMSetPinType @@ -529,6 +531,7 @@ void BTA_DmPinReply(BD_ADDR bd_addr, BOOLEAN accept, UINT8 pin_len, UINT8 *p_pin } } +#endif ///CLASSIC_BT_INCLUDED == TRUE #if (BTM_OOB_INCLUDED == TRUE && SMP_INCLUDED == TRUE) /******************************************************************************* @@ -594,6 +597,7 @@ void BTA_DmOobReply(BD_ADDR bd_addr, UINT8 len, UINT8 *p_value) ** Returns void ** *******************************************************************************/ +#if (SMP_INCLUDED == TRUE) void BTA_DmConfirm(BD_ADDR bd_addr, BOOLEAN accept) { tBTA_DM_API_CONFIRM *p_msg; @@ -629,6 +633,7 @@ void BTA_DmPasskeyReqReply(BOOLEAN accept, BD_ADDR bd_addr, UINT32 passkey) } } #endif ///BT_SSP_INCLUDED == TRUE +#endif ///SMP_INCLUDED == TRUE /******************************************************************************* ** ** Function BTA_DmAddDevice @@ -705,7 +710,7 @@ tBTA_STATUS BTA_DmRemoveDevice(BD_ADDR bd_addr, tBT_TRANSPORT transport) return BTA_SUCCESS; } -#endif ///SMP_INCLUDED == TRUE +// #endif ///SMP_INCLUDED == TRUE /******************************************************************************* ** @@ -989,7 +994,6 @@ void BTA_DmBleSecurityGrant(BD_ADDR bd_addr, tBTA_DM_BLE_SEC_GRANT res) } } #endif ///SMP_INCLUDED == TRUE -#endif ///BLE_INCLUDED == TRUE /******************************************************************************* @@ -1207,7 +1211,7 @@ void BTA_DmSetBleAdvParamsAll (UINT16 adv_int_min, UINT16 adv_int_max, } #endif } - +#endif ///BLE_INCLUDED == TRUE /******************************************************************************* @@ -1796,6 +1800,8 @@ void BTA_DmBleUpdateConnectionParam(BD_ADDR bd_addr, UINT16 min_int, } #endif } + +#if BLE_INCLUDED == TRUE /******************************************************************************* ** ** Function BTA_DmBleConfigLocalPrivacy @@ -1826,7 +1832,6 @@ void BTA_DmBleConfigLocalPrivacy(BOOLEAN privacy_enable, tBTA_SET_LOCAL_PRIVACY_ #endif } -#if BLE_INCLUDED == TRUE /******************************************************************************* ** ** Function BTA_DmBleConfigLocalIcon diff --git a/components/bt/bluedroid/bta/dm/include/bta_dm_int.h b/components/bt/bluedroid/bta/dm/include/bta_dm_int.h index 83fdb3bab7..4fc8e650ce 100644 --- a/components/bt/bluedroid/bta/dm/include/bta_dm_int.h +++ b/components/bt/bluedroid/bta/dm/include/bta_dm_int.h @@ -203,6 +203,7 @@ typedef struct { UINT8 data[]; }tBTA_DM_API_CONFIG_EIR; +#if (BLE_INCLUDED == TRUE) typedef struct { BT_HDR hdr; BOOLEAN add_remove; @@ -223,6 +224,7 @@ typedef struct { BT_HDR hdr; tBTA_CMPL_CB *read_tx_power_cb; }tBTA_DM_API_READ_ADV_TX_POWER; +#endif ///BLE_INCLUDED == TRUE typedef struct { BT_HDR hdr; @@ -402,8 +404,8 @@ typedef struct { UINT8 hci_status; #if BLE_INCLUDED == TRUE UINT16 handle; - tBT_TRANSPORT transport; #endif + tBT_TRANSPORT transport; } tBTA_DM_ACL_CHANGE; #if (BTA_DM_PM_INCLUDED == TRUE) @@ -801,9 +803,12 @@ typedef union { tBTA_DM_API_SET_NAME set_name; tBTA_DM_API_CONFIG_EIR config_eir; +#if (BLE_INCLUDED == TRUE) tBTA_DM_API_UPDATE_WHITE_LIST white_list; tBTA_DM_API_READ_ADV_TX_POWER read_tx_power; tBTA_DM_API_READ_RSSI rssi; +#endif ///BLE_INCLUDED == TRUE + tBTA_DM_API_SET_VISIBILITY set_visibility; tBTA_DM_API_ADD_DEVICE add_dev; @@ -939,8 +944,8 @@ typedef struct { BOOLEAN remove_dev_pending; #if BLE_INCLUDED == TRUE UINT16 conn_handle; - tBT_TRANSPORT transport; #endif + tBT_TRANSPORT transport; } tBTA_DM_PEER_DEVICE; @@ -1031,6 +1036,8 @@ typedef struct { BOOLEAN disable_pair_mode; /* disable pair mode or not */ BOOLEAN conn_paired_only; /* allow connectable to paired device only or not */ tBTA_DM_API_SEARCH search_msg; + +#if (CLASSIC_BT_INCLUDED == TRUE) UINT16 page_scan_interval; UINT16 page_scan_window; UINT16 inquiry_scan_interval; @@ -1040,8 +1047,10 @@ typedef struct { BD_ADDR pin_bd_addr; DEV_CLASS pin_dev_class; tBTA_DM_SEC_EVT pin_evt; - UINT32 num_val; /* the numeric value for comparison. If just_works, do not show this number to UI */ - BOOLEAN just_works; /* TRUE, if "Just Works" association model */ + UINT32 num_val; /* the numeric value for comparison. If just_works, do not show this number to UI */ + BOOLEAN just_works; /* TRUE, if "Just Works" association model */ +#endif + #if ( BTA_EIR_CANNED_UUID_LIST != TRUE ) /* store UUID list for EIR */ TIMER_LIST_ENT app_ready_timer; diff --git a/components/bt/bluedroid/bta/include/bta/bta_api.h b/components/bt/bluedroid/bta/include/bta/bta_api.h index aed71d7e46..c9349e80ec 100644 --- a/components/bt/bluedroid/bta/include/bta/bta_api.h +++ b/components/bt/bluedroid/bta/include/bta/bta_api.h @@ -31,9 +31,9 @@ // #include "uipc_msg.h" #include "stack/sdp_api.h" -#if BLE_INCLUDED == TRUE +// #if BLE_INCLUDED == TRUE #include "stack/btm_ble_api.h" -#endif +// #endif /***************************************************************************** ** Constants and data types @@ -323,7 +323,7 @@ typedef struct { tBTA_DM_CONFIG_EIR_CBACK *config_eir_callback; /* callback */ } tBTA_DM_EIR_CONF; -#if BLE_INCLUDED == TRUE +// #if BLE_INCLUDED == TRUE /* ADV data flag bit definition used for BTM_BLE_AD_TYPE_FLAG */ #define BTA_BLE_LIMIT_DISC_FLAG BTM_BLE_LIMIT_DISC_FLAG #define BTA_BLE_GEN_DISC_FLAG BTM_BLE_GEN_DISC_FLAG @@ -510,7 +510,7 @@ enum { typedef tBTM_BLE_BATCH_SCAN_EVT tBTA_BLE_BATCH_SCAN_EVT; typedef tBTM_BLE_TRACK_ADV_ACTION tBTA_BLE_TRACK_ADV_ACTION; -#endif +// #endif /* BLE customer specific feature function type definitions */ /* data type used on customer specific feature for RSSI monitoring */ @@ -1473,9 +1473,11 @@ extern void BTA_DmSetDeviceName(const char *p_name); *******************************************************************************/ extern void BTA_DmConfigEir(tBTA_DM_EIR_CONF *eir_config); +#if (BLE_INCLUDED == TRUE) extern void BTA_DmUpdateWhiteList(BOOLEAN add_remove, BD_ADDR remote_addr, tBLE_ADDR_TYPE addr_type, tBTA_ADD_WHITELIST_CBACK *add_wl_cb); extern void BTA_DmBleReadAdvTxPower(tBTA_CMPL_CB *cmpl_cb); +#endif ///BLE_INCLUDED == TRUE extern void BTA_DmBleReadRSSI(BD_ADDR remote_addr, tBTA_TRANSPORT transport, tBTA_CMPL_CB *cmpl_cb); diff --git a/components/bt/bluedroid/bta/include/bta/bta_dm_co.h b/components/bt/bluedroid/bta/include/bta/bta_dm_co.h index b9e98b4653..3ef102c5aa 100644 --- a/components/bt/bluedroid/bta/include/bta/bta_dm_co.h +++ b/components/bt/bluedroid/bta/include/bta/bta_dm_co.h @@ -25,6 +25,7 @@ #define BTA_DM_CO_H #include "bta/bta_sys.h" +#include "esp_err.h" /***************************************************************************** ** Function Declarations diff --git a/components/bt/bluedroid/btc/core/btc_ble_storage.c b/components/bt/bluedroid/btc/core/btc_ble_storage.c index 9730dc612c..ecbe709364 100644 --- a/components/bt/bluedroid/btc/core/btc_ble_storage.c +++ b/components/bt/bluedroid/btc/core/btc_ble_storage.c @@ -96,6 +96,7 @@ void btc_storage_save(void) btc_config_unlock(); } +#if (BLE_INCLUDED == TRUE) static bt_status_t _btc_storage_add_ble_bonding_key(bt_bdaddr_t *remote_bd_addr, char *key, uint8_t key_type, @@ -552,6 +553,7 @@ bt_status_t btc_storage_remove_ble_dev_type(bt_bdaddr_t *remote_bd_addr, bool fl return ret; } +#endif ///BLE_INCLUDED == TRUE static bt_status_t _btc_storage_set_ble_dev_auth_mode(bt_bdaddr_t *remote_bd_addr, uint8_t auth_mode, bool flush) { @@ -725,6 +727,7 @@ bt_status_t btc_storage_get_remote_addr_type(bt_bdaddr_t *remote_bd_addr, return ret; } +#if (BLE_INCLUDED == TRUE) static void _btc_read_le_key(const uint8_t key_type, const size_t key_len, bt_bdaddr_t bd_addr, const uint8_t addr_type, const bool add_key, bool *device_added, bool *key_found) { @@ -926,5 +929,6 @@ int btc_storage_get_num_ble_bond_devices(void) return num_dev; } +#endif ///BLE_INCLUDED == TRUE #endif ///SMP_INCLUDED == TRUE diff --git a/components/bt/bluedroid/btc/core/btc_dm.c b/components/bt/bluedroid/btc/core/btc_dm.c index ecc8f383a6..011d1ff93d 100644 --- a/components/bt/bluedroid/btc/core/btc_dm.c +++ b/components/bt/bluedroid/btc/core/btc_dm.c @@ -129,6 +129,7 @@ static void btc_disable_bluetooth_evt(void) } #if (SMP_INCLUDED == TRUE) +#if (BLE_INCLUDED == TRUE) void btc_dm_load_ble_local_keys(void) { memset(&btc_dm_cb.ble_local_key_cb, 0, sizeof(btc_dm_local_key_cb_t)); @@ -296,6 +297,7 @@ static void btc_dm_ble_auth_cmpl_evt (tBTA_DM_AUTH_CMPL *p_auth_cmpl) return; } +#endif ///BLE_INCLUDED == TRUE #endif ///SMP_INCLUDED == TRUE static void btc_dm_auth_cmpl_evt (tBTA_DM_AUTH_CMPL *p_auth_cmpl) @@ -569,8 +571,10 @@ void btc_dm_sec_cb_handler(btc_msg_t *msg) btc_clear_services_mask(); #if (SMP_INCLUDED == TRUE) btc_storage_load_bonded_devices(); +#if (BLE_INCLUDED == TRUE) //load the bonding device to the btm layer btc_storage_load_bonded_ble_devices(); +#endif ///BLE_INCLUDED == TRUE #endif ///SMP_INCLUDED == TRUE /* Set initial device name, it can be overwritten later */ @@ -633,6 +637,7 @@ void btc_dm_sec_cb_handler(btc_msg_t *msg) #endif /* #if (SMP_INCLUDED == TRUE) */ break; } +#if (BLE_INCLUDED == TRUE) case BTA_DM_BLE_DEV_UNPAIRED_EVT: { #if (SMP_INCLUDED == TRUE) bt_bdaddr_t bd_addr; @@ -654,6 +659,7 @@ void btc_dm_sec_cb_handler(btc_msg_t *msg) #endif /* #if (SMP_INCLUDED == TRUE) */ break; } +#endif ///BLE_INCLUDED == TRUE case BTA_DM_BUSY_LEVEL_EVT: #if (BTC_GAP_BT_INCLUDED == TRUE) { @@ -668,7 +674,7 @@ void btc_dm_sec_cb_handler(btc_msg_t *msg) case BTA_DM_HW_ERROR_EVT: BTC_TRACE_DEBUG( "btc_dm_sec_cback : unhandled event (%d)\n", msg->act ); break; -#if (defined(BLE_INCLUDED) && (BLE_INCLUDED == TRUE) && (SMP_INCLUDED == TRUE)) +#if ((BLE_INCLUDED == TRUE) && (SMP_INCLUDED == TRUE)) case BTA_DM_BLE_AUTH_CMPL_EVT: { rsp_app = true; ble_msg.act = ESP_GAP_BLE_AUTH_CMPL_EVT; diff --git a/components/bt/bluedroid/btc/core/btc_main.c b/components/bt/bluedroid/btc/core/btc_main.c index 75a2cd45cf..c91aedfb18 100644 --- a/components/bt/bluedroid/btc/core/btc_main.c +++ b/components/bt/bluedroid/btc/core/btc_main.c @@ -64,15 +64,20 @@ static void btc_init_bluetooth(void) bte_main_boot_entry(btc_init_callback); #if (SMP_INCLUDED) btc_config_init(); + +#if (BLE_INCLUDED == TRUE) //load the ble local key which has been stored in the flash btc_dm_load_ble_local_keys(); +#endif ///BLE_INCLUDED == TRUE #endif /* #if (SMP_INCLUDED) */ } static void btc_deinit_bluetooth(void) { +#if (BLE_INCLUDED == TRUE) btc_gap_ble_deinit(); +#endif ///BLE_INCLUDED == TRUE bta_dm_sm_deinit(); #if (GATTC_INCLUDED) bta_gattc_deinit(); diff --git a/components/bt/bluedroid/btc/core/btc_task.c b/components/bt/bluedroid/btc/core/btc_task.c index 72866dc3c5..911f1941b6 100644 --- a/components/bt/bluedroid/btc/core/btc_task.c +++ b/components/bt/bluedroid/btc/core/btc_task.c @@ -69,7 +69,11 @@ static const btc_func_t profile_tab[BTC_PID_NUM] = { #if (GATTS_INCLUDED == TRUE || GATTC_INCLUDED == TRUE) [BTC_PID_GATT_COMMON] = {btc_gatt_com_call_handler, NULL }, #endif //GATTC_INCLUDED == TRUE || GATTS_INCLUDED == TRUE +#if (BLE_INCLUDED == TRUE) [BTC_PID_GAP_BLE] = {btc_gap_ble_call_handler, btc_gap_ble_cb_handler }, +#else + [BTC_PID_GAP_BLE] = {NULL, NULL}, +#endif ///BLE_INCLUDED == TRUE [BTC_PID_BLE_HID] = {NULL, NULL}, [BTC_PID_SPPLIKE] = {NULL, NULL}, #if (GATTS_INCLUDED == TRUE) @@ -183,6 +187,7 @@ static bt_status_t btc_init_mem(void) { } memset((void *)btc_profile_cb_tab, 0, sizeof(void *) * BTC_PID_NUM); +#if (BLE_INCLUDED == TRUE) if ((gl_bta_adv_data_ptr = (tBTA_BLE_ADV_DATA *)osi_malloc(sizeof(tBTA_BLE_ADV_DATA))) == NULL) { return BT_STATUS_NOMEM; } @@ -192,6 +197,7 @@ static bt_status_t btc_init_mem(void) { return BT_STATUS_NOMEM; } memset((void *)gl_bta_scan_rsp_data_ptr, 0, sizeof(tBTA_BLE_ADV_DATA)); +#endif ///BLE_INCLUDED == TRUE #if GATTS_INCLUDED == TRUE && GATT_DYNAMIC_MEMORY == TRUE if ((btc_creat_tab_env_ptr = (esp_btc_creat_tab_t *)osi_malloc(sizeof(esp_btc_creat_tab_t))) == NULL) { @@ -273,7 +279,10 @@ int btc_init(void) } #endif +#if (BLE_INCLUDED == TRUE) btc_gap_callback_init(); +#endif ///BLE_INCLUDED == TRUE + #if SCAN_QUEUE_CONGEST_CHECK btc_adv_list_init(); #endif diff --git a/components/bt/bluedroid/btc/profile/std/gap/btc_gap_ble.c b/components/bt/bluedroid/btc/profile/std/gap/btc_gap_ble.c index be270a430c..3355a5924b 100644 --- a/components/bt/bluedroid/btc/profile/std/gap/btc_gap_ble.c +++ b/components/bt/bluedroid/btc/profile/std/gap/btc_gap_ble.c @@ -31,6 +31,7 @@ #include "osi/mutex.h" #include "esp_bt.h" +#if (BLE_INCLUDED == TRUE) #if BTC_DYNAMIC_MENDRY == FALSE static tBTA_BLE_ADV_DATA gl_bta_adv_data; static tBTA_BLE_ADV_DATA gl_bta_scan_rsp_data; @@ -1372,3 +1373,4 @@ void btc_adv_list_unlock(void) osi_mutex_unlock(&adv_list_lock); } #endif +#endif ///BLE_INCLUDED == TRUE \ No newline at end of file diff --git a/components/bt/bluedroid/common/include/common/bt_target.h b/components/bt/bluedroid/common/include/common/bt_target.h index c8f15e8694..6adf32a9f5 100644 --- a/components/bt/bluedroid/common/include/common/bt_target.h +++ b/components/bt/bluedroid/common/include/common/bt_target.h @@ -119,6 +119,12 @@ ** BLE features ** ******************************************************************************/ +#if (UC_BT_BLE_ENABLED ==TRUE) +#define BLE_INCLUDED TRUE +#else +#define BLE_INCLUDED FALSE +#endif /* UC_BT_BLE_ENABLED */ + #if (UC_BT_GATTS_ENABLE) #define GATTS_INCLUDED TRUE #else @@ -139,8 +145,12 @@ #if (UC_BT_SMP_ENABLE) #define SMP_INCLUDED TRUE +#if (BLE_INCLUDED == TRUE) #define BLE_PRIVACY_SPT TRUE #else +#define BLE_PRIVACY_SPT FALSE +#endif /*BLE_INCLUDED*/ +#else #define SMP_INCLUDED FALSE #define BLE_PRIVACY_SPT FALSE #endif /* UC_BT_SMP_ENABLE */ @@ -1024,7 +1034,7 @@ ******************************************************************************/ #ifndef BLE_INCLUDED -#define BLE_INCLUDED TRUE +#define BLE_INCLUDED FALSE #endif #ifndef BLE_ANDROID_CONTROLLER_SCAN_FILTER @@ -1181,12 +1191,6 @@ #endif #endif - -#if SMP_INCLUDED == TRUE && BLE_INCLUDED == FALSE -#error "can't have SMP without BLE" -#endif - - /****************************************************************************** ** ** SMP @@ -1200,10 +1204,6 @@ #endif #endif -#if SMP_INCLUDED == TRUE && BLE_INCLUDED == FALSE -#error "can't have SMP without BLE" -#endif - #ifndef SMP_DEBUG #define SMP_DEBUG FALSE #endif diff --git a/components/bt/bluedroid/common/include/common/bt_user_config.h b/components/bt/bluedroid/common/include/common/bt_user_config.h index 10568fbd07..5fdc1c2183 100644 --- a/components/bt/bluedroid/common/include/common/bt_user_config.h +++ b/components/bt/bluedroid/common/include/common/bt_user_config.h @@ -86,6 +86,12 @@ #define UC_BT_SSP_ENABLED CONFIG_BT_SSP_ENABLED #else #define UC_BT_SSP_ENABLED FALSE + +//BLE +#ifdef CONFIG_BT_BLE_ENABLED +#define UC_BT_BLE_ENABLED CONFIG_BT_BLE_ENABLED +#else +#define UC_BT_BLE_ENABLED FALSE #endif //GATTS diff --git a/components/bt/bluedroid/stack/btm/btm_acl.c b/components/bt/bluedroid/stack/btm/btm_acl.c index e42724f235..6f7ce092a6 100644 --- a/components/bt/bluedroid/stack/btm/btm_acl.c +++ b/components/bt/bluedroid/stack/btm/btm_acl.c @@ -2038,6 +2038,8 @@ tBTM_STATUS BTM_ReadTxPower (BD_ADDR remote_bda, tBT_TRANSPORT transport, tBTM_C /* If here, no BD Addr found */ return (BTM_UNKNOWN_ADDR); } + +#if (BLE_INCLUDED == TRUE) tBTM_STATUS BTM_BleReadAdvTxPower(tBTM_CMPL_CB *p_cb) { BOOLEAN ret; @@ -2074,6 +2076,7 @@ void BTM_BleGetWhiteListSize(uint16_t *length) *length = p_cb->white_list_avail_size; return; } +#endif ///BLE_INCLUDED == TRUE /******************************************************************************* ** diff --git a/components/bt/bluedroid/stack/btm/btm_ble.c b/components/bt/bluedroid/stack/btm/btm_ble.c index 2b62cde415..78accb1a4c 100644 --- a/components/bt/bluedroid/stack/btm/btm_ble.c +++ b/components/bt/bluedroid/stack/btm/btm_ble.c @@ -24,8 +24,6 @@ ******************************************************************************/ #include "common/bt_target.h" -#if BLE_INCLUDED == TRUE - #include #include "stack/bt_types.h" @@ -41,7 +39,7 @@ //#define LOG_TAG "bt_btm_ble" //#include "osi/include/log.h" - +#if BLE_INCLUDED == TRUE #if SMP_INCLUDED == TRUE // The temp variable to pass parameter between functions when in the connected event callback. static BOOLEAN temp_enhanced = FALSE; @@ -667,7 +665,7 @@ void BTM_ReadDevInfo (BD_ADDR remote_bda, tBT_DEVICE_TYPE *p_dev_type, tBLE_ADDR BTM_TRACE_DEBUG ("btm_find_dev_type - device_type = %d addr_type = %d", *p_dev_type , *p_addr_type); } - +#endif ///BLE_INCLUDED == TRUE /******************************************************************************* ** @@ -703,7 +701,7 @@ BOOLEAN BTM_ReadConnectedTransportAddress(BD_ADDR remote_bda, tBT_TRANSPORT tran } return FALSE; } - +#if (BLE_INCLUDED == TRUE) if (transport == BT_TRANSPORT_LE) { memcpy(remote_bda, p_dev_rec->ble.pseudo_addr, BD_ADDR_LEN); if (btm_bda_to_acl(p_dev_rec->ble.pseudo_addr, transport) != NULL) { @@ -712,10 +710,11 @@ BOOLEAN BTM_ReadConnectedTransportAddress(BD_ADDR remote_bda, tBT_TRANSPORT tran return FALSE; } } - +#endif ///BLE_INCLUDED == TRUE return FALSE; } +#if (BLE_INCLUDED == TRUE) /******************************************************************************* ** ** Function BTM_BleReceiverTest @@ -1104,6 +1103,7 @@ void btm_ble_increment_sign_ctr(BD_ADDR bd_addr, BOOLEAN is_local ) } } #endif ///SMP_INCLUDED == TRUE +#endif ///BLE_INCLUDED == TRUE /******************************************************************************* ** @@ -1116,6 +1116,7 @@ void btm_ble_increment_sign_ctr(BD_ADDR bd_addr, BOOLEAN is_local ) ** *******************************************************************************/ #if (SMP_INCLUDED == TRUE) +#if (BLE_INCLUDED == TRUE) BOOLEAN btm_ble_get_enc_key_type(BD_ADDR bd_addr, UINT8 *p_key_types) { tBTM_SEC_DEV_REC *p_dev_rec; @@ -1160,7 +1161,6 @@ BOOLEAN btm_get_local_div (BD_ADDR bd_addr, UINT16 *p_div) return status; } - /******************************************************************************* ** ** Function btm_sec_save_le_key @@ -1420,9 +1420,10 @@ void btm_ble_link_sec_check(BD_ADDR bd_addr, tBTM_LE_AUTH_REQ auth_req, tBTM_BLE } +#endif ///BLE_INCLUDED == TRUE #endif ///SMP_INCLUDED == TRUE - +#if (BLE_INCLUDED == TRUE) /******************************************************************************* ** ** Function btm_ble_set_encryption @@ -2113,10 +2114,9 @@ UINT8 btm_proc_smp_cback(tSMP_EVT event, BD_ADDR bd_addr, tSMP_EVT_DATA *p_data) } #endif - BTM_TRACE_DEBUG ("btm_cb pairing_state=%x pairing_flags=%x pin_code_len=%x", + BTM_TRACE_DEBUG ("btm_cb pairing_state=%x pairing_flags=%x", btm_cb.pairing_state, - btm_cb.pairing_flags, - btm_cb.pin_code_len ); + btm_cb.pairing_flags); BTM_TRACE_DEBUG ("btm_cb.pairing_bda %02x:%02x:%02x:%02x:%02x:%02x", btm_cb.pairing_bda[0], btm_cb.pairing_bda[1], btm_cb.pairing_bda[2], btm_cb.pairing_bda[3], btm_cb.pairing_bda[4], btm_cb.pairing_bda[5]); @@ -2737,7 +2737,7 @@ void btm_ble_set_keep_rfu_in_auth_req(BOOLEAN keep_rfu) ** Function btm_get_current_conn_params ** ** Description This function is called to get current connection parameters -** information of the device +** information of the device ** ** Returns TRUE if the information is geted, else FALSE ** @@ -2758,7 +2758,7 @@ BOOLEAN btm_get_current_conn_params(BD_ADDR bda, UINT16 *interval, UINT16 *laten return TRUE; } BTM_TRACE_WARNING("%s Device is not connected", __func__); - + return FALSE; } diff --git a/components/bt/bluedroid/stack/btm/btm_devctl.c b/components/bt/bluedroid/stack/btm/btm_devctl.c index dfd2c3bf60..23be3eb303 100644 --- a/components/bt/bluedroid/stack/btm/btm_devctl.c +++ b/components/bt/bluedroid/stack/btm/btm_devctl.c @@ -190,9 +190,9 @@ static void reset_complete(void) l2c_link_processs_ble_num_bufs(controller->get_acl_buffer_count_ble()); } #endif -#if (SMP_INCLUDED == TRUE) +#if (SMP_INCLUDED == TRUE && CLASSIC_BT_INCLUDED == TRUE) BTM_SetPinType (btm_cb.cfg.pin_type, btm_cb.cfg.pin_code, btm_cb.cfg.pin_code_len); -#endif ///SMP_INCLUDED == TRUE +#endif ///SMP_INCLUDED == TRUE && CLASSIC_BT_INCLUDED == TRUE for (int i = 0; i <= controller->get_last_features_classic_index(); i++) { btm_decode_ext_features_page(i, controller->get_features_classic(i)->as_array); } diff --git a/components/bt/bluedroid/stack/btm/btm_sec.c b/components/bt/bluedroid/stack/btm/btm_sec.c index 0a61eac6fd..1d7b3276c6 100644 --- a/components/bt/bluedroid/stack/btm/btm_sec.c +++ b/components/bt/bluedroid/stack/btm/btm_sec.c @@ -35,6 +35,7 @@ #include "l2c_int.h" #include "osi/fixed_queue.h" #include "osi/alarm.h" +#include "stack/btm_ble_api.h" #if (BT_USE_TRACES == TRUE && BT_TRACE_VERBOSE == FALSE) /* needed for sprintf() */ @@ -368,6 +369,7 @@ BOOLEAN BTM_GetSecurityFlagsByTransport (BD_ADDR bd_addr, UINT8 *p_sec_flags, return (FALSE); } +#if (CLASSIC_BT_INCLUDED == TRUE) /******************************************************************************* ** ** Function BTM_SetPinType @@ -392,6 +394,7 @@ void BTM_SetPinType (UINT8 pin_type, PIN_CODE pin_code, UINT8 pin_code_len) btm_cb.cfg.pin_code_len = pin_code_len; memcpy (btm_cb.cfg.pin_code, pin_code, pin_code_len); } +#endif ///CLASSIC_BT_INCLUDED == TRUE /******************************************************************************* ** @@ -824,6 +827,7 @@ void btm_sec_clr_temp_auth_service (BD_ADDR bda) ** *******************************************************************************/ #if (SMP_INCLUDED == TRUE) +#if (CLASSIC_BT_INCLUDED == TRUE) void BTM_PINCodeReply (BD_ADDR bd_addr, UINT8 res, UINT8 pin_len, UINT8 *p_pin, UINT32 trusted_mask[]) { tBTM_SEC_DEV_REC *p_dev_rec; @@ -930,6 +934,7 @@ void BTM_PINCodeReply (BD_ADDR bd_addr, UINT8 res, UINT8 pin_len, UINT8 *p_pin, #endif btsnd_hcic_pin_code_req_reply (bd_addr, pin_len, p_pin); } +#endif ///CLASSIC_BT_INCLUDED == TRUE #endif ///SMP_INCLUDED == TRUE @@ -995,12 +1000,14 @@ tBTM_STATUS btm_sec_bond_by_transport (BD_ADDR bd_addr, tBT_TRANSPORT transport, return (BTM_NO_RESOURCES); } +#if (CLASSIC_BT_INCLUDED == TRUE) /* Save the PIN code if we got a valid one */ if (p_pin && (pin_len <= PIN_CODE_LEN) && (pin_len != 0)) { btm_cb.pin_code_len = pin_len; p_dev_rec->pin_code_length = pin_len; memcpy (btm_cb.pin_code, p_pin, PIN_CODE_LEN); } +#endif ///CLASSIC_BT_INCLUDED == TRUE memcpy (btm_cb.pairing_bda, bd_addr, BD_ADDR_LEN); @@ -1034,6 +1041,8 @@ tBTM_STATUS btm_sec_bond_by_transport (BD_ADDR bd_addr, tBT_TRANSPORT transport, BTM_TRACE_DEBUG ("after update sec_flags=0x%x\n", p_dev_rec->sec_flags); + +#if (CLASSIC_BT_INCLUDED == TRUE) if (!controller_get_interface()->supports_simple_pairing()) { /* The special case when we authenticate keyboard. Set pin type to fixed */ /* It would be probably better to do it from the application, but it is */ @@ -1045,6 +1054,7 @@ tBTM_STATUS btm_sec_bond_by_transport (BD_ADDR bd_addr, tBT_TRANSPORT transport, btsnd_hcic_write_pin_type (HCI_PIN_TYPE_FIXED); } } +#endif ///CLASSIC_BT_INCLUDED == TRUE for (ii = 0; ii <= HCI_EXT_FEATURES_PAGE_MAX; ii++) { #if (!CONFIG_BT_STACK_NO_LOG) @@ -1133,6 +1143,7 @@ tBTM_STATUS btm_sec_bond_by_transport (BD_ADDR bd_addr, tBT_TRANSPORT transport, tBTM_STATUS BTM_SecBondByTransport (BD_ADDR bd_addr, tBT_TRANSPORT transport, UINT8 pin_len, UINT8 *p_pin, UINT32 trusted_mask[]) { +#if (BLE_INCLUDED == TRUE) tBT_DEVICE_TYPE dev_type; tBLE_ADDR_TYPE addr_type; @@ -1142,6 +1153,8 @@ tBTM_STATUS BTM_SecBondByTransport (BD_ADDR bd_addr, tBT_TRANSPORT transport, (transport == BT_TRANSPORT_BR_EDR && (dev_type & BT_DEVICE_TYPE_BREDR) == 0)) { return BTM_ILLEGAL_ACTION; } +#endif ///BLE_INCLUDED == TRUE + return btm_sec_bond_by_transport(bd_addr, transport, pin_len, p_pin, trusted_mask); } #endif ///SMP_INCLUDED == TRUE @@ -1165,9 +1178,11 @@ tBTM_STATUS BTM_SecBondByTransport (BD_ADDR bd_addr, tBT_TRANSPORT transport, tBTM_STATUS BTM_SecBond (BD_ADDR bd_addr, UINT8 pin_len, UINT8 *p_pin, UINT32 trusted_mask[]) { tBT_TRANSPORT transport = BT_TRANSPORT_BR_EDR; +#if (BLE_INCLUDED == TRUE) if (BTM_UseLeLink(bd_addr)) { transport = BT_TRANSPORT_LE; } +#endif ///BLE_INCLUDED == TRUE return btm_sec_bond_by_transport(bd_addr, transport, pin_len, p_pin, trusted_mask); } /******************************************************************************* @@ -4117,8 +4132,10 @@ void btm_sec_encrypt_change (UINT16 handle, UINT8 status, UINT8 encr_enable) if (p_dev_rec->no_smp_on_br) { BTM_TRACE_DEBUG ("%s NO SM over BR/EDR\n", __func__); } else { +#if (CLASSIC_BT_INCLUDED == TRUE) BTM_TRACE_DEBUG ("%s start SM over BR/EDR\n", __func__); SMP_BR_PairWith(p_dev_rec->bd_addr); +#endif ///CLASSIC_BT_INCLUDED == TRUE } } } else { @@ -4924,6 +4941,7 @@ static void btm_sec_pairing_timeout (TIMER_LIST_ENT *p_tle) } } +#if (CLASSIC_BT_INCLUDED == TRUE) /******************************************************************************* ** ** Function btm_sec_pin_code_request @@ -5061,6 +5079,7 @@ void btm_sec_pin_code_request (UINT8 *p_bda) } return; } +#endif ///CLASSIC_BT_INCLUDED == TRUE #endif ///SMP_INCLUDED == TRUE @@ -5589,10 +5608,12 @@ static void btm_restore_mode(void) btsnd_hcic_write_auth_enable ((UINT8)(btm_cb.security_mode == BTM_SEC_MODE_LINK)); } +#if (CLASSIC_BT_INCLUDED == TRUE) if (btm_cb.pin_type_changed) { btm_cb.pin_type_changed = FALSE; btsnd_hcic_write_pin_type (btm_cb.cfg.pin_type); } +#endif ///CLASSIC_BT_INCLUDED == TRUE } #endif ///SMP_INCLUDED == TRUE @@ -5644,7 +5665,9 @@ static void btm_sec_change_pairing_state (tBTM_PAIRING_STATE new_state) btu_stop_timer (&btm_cb.pairing_tle); btm_cb.pairing_flags = 0; +#if (CLASSIC_BT_INCLUDED == TRUE) btm_cb.pin_code_len = 0; +#endif ///CLASSIC_BT_INCLUDED == TRUE /* Make sure the the lcb shows we are not bonding */ l2cu_update_lcb_4_bonding (btm_cb.pairing_bda, FALSE); @@ -5770,9 +5793,11 @@ static BOOLEAN btm_sec_queue_mx_request (BD_ADDR bd_addr, UINT16 psm, BOOLEAN } static BOOLEAN btm_sec_check_prefetch_pin (tBTM_SEC_DEV_REC *p_dev_rec) { + BOOLEAN rv = FALSE; +#if (CLASSIC_BT_INCLUDED == TRUE) UINT8 major = (UINT8)(p_dev_rec->dev_class[1] & BTM_COD_MAJOR_CLASS_MASK); UINT8 minor = (UINT8)(p_dev_rec->dev_class[2] & BTM_COD_MINOR_CLASS_MASK); - BOOLEAN rv = FALSE; + rv = TRUE; if ((major == BTM_COD_MAJOR_AUDIO) && ((minor == BTM_COD_MINOR_CONFM_HANDSFREE) || (minor == BTM_COD_MINOR_CAR_AUDIO)) ) { @@ -5810,7 +5835,8 @@ static BOOLEAN btm_sec_check_prefetch_pin (tBTM_SEC_DEV_REC *p_dev_rec) rv = TRUE; } - +#endif ///CLASSIC_BT_INCLUDED == TRUE +# return rv; } @@ -5993,6 +6019,7 @@ static UINT16 btm_sec_set_serv_level4_flags(UINT16 cur_security, BOOLEAN is_orig return cur_security | sec_level4_flags; } #endif ///SMP_INCLUDED == TRUE + /******************************************************************************* ** ** Function btm_sec_clear_ble_keys @@ -6004,6 +6031,7 @@ static UINT16 btm_sec_set_serv_level4_flags(UINT16 cur_security, BOOLEAN is_orig ** Returns void ** *******************************************************************************/ +#if (BLE_INCLUDED == TRUE) void btm_sec_clear_ble_keys (tBTM_SEC_DEV_REC *p_dev_rec) { @@ -6017,7 +6045,7 @@ void btm_sec_clear_ble_keys (tBTM_SEC_DEV_REC *p_dev_rec) #endif #endif } - +#endif ///BLE_INCLUDED == TRUE /******************************************************************************* ** ** Function btm_sec_is_a_bonded_dev @@ -6034,7 +6062,7 @@ BOOLEAN btm_sec_is_a_bonded_dev (BD_ADDR bda) BOOLEAN is_bonded = FALSE; if (p_dev_rec && -#if (SMP_INCLUDED == TRUE) +#if (SMP_INCLUDED == TRUE && BLE_INCLUDED == TRUE) ((p_dev_rec->ble.key_type && (p_dev_rec->sec_flags & BTM_SEC_LE_LINK_KEY_KNOWN)) || #else ( @@ -6057,10 +6085,10 @@ BOOLEAN btm_sec_is_a_bonded_dev (BD_ADDR bda) *******************************************************************************/ BOOLEAN btm_sec_is_le_capable_dev (BD_ADDR bda) { - tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bda); BOOLEAN le_capable = FALSE; #if (BLE_INCLUDED== TRUE) + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bda); if (p_dev_rec && (p_dev_rec->device_type & BT_DEVICE_TYPE_BLE) == BT_DEVICE_TYPE_BLE) { le_capable = TRUE; } @@ -6077,6 +6105,7 @@ BOOLEAN btm_sec_is_le_capable_dev (BD_ADDR bda) ** Returns TRUE - found a bonded device ** *******************************************************************************/ +#if (BLE_INCLUDED == TRUE) BOOLEAN btm_sec_find_bonded_dev (UINT8 start_idx, UINT8 *p_found_idx, tBTM_SEC_DEV_REC **p_rec) { BOOLEAN found = FALSE; @@ -6101,7 +6130,7 @@ BOOLEAN btm_sec_find_bonded_dev (UINT8 start_idx, UINT8 *p_found_idx, tBTM_SEC_D #endif return (found); } - +#endif ///BLE_INCLUDED == TRUE /******************************************************************************* ** ** Function btm_sec_use_smp_br_chnl diff --git a/components/bt/bluedroid/stack/btm/include/btm_int.h b/components/bt/bluedroid/stack/btm/include/btm_int.h index 455ce7ca36..f575d8958a 100644 --- a/components/bt/bluedroid/stack/btm/include/btm_int.h +++ b/components/bt/bluedroid/stack/btm/include/btm_int.h @@ -32,13 +32,14 @@ #include "stack/rfcdefs.h" #include "stack/btm_api.h" +#include "osi/fixed_queue.h" #if (BLE_INCLUDED == TRUE) #include "btm_ble_int.h" +#endif #if (SMP_INCLUDED == TRUE) #include "stack/smp_api.h" #endif -#endif #if BTM_MAX_LOC_BD_NAME_LEN > 0 typedef char tBTM_LOC_BD_NAME[BTM_MAX_LOC_BD_NAME_LEN + 1]; @@ -855,9 +856,12 @@ typedef struct { BOOLEAN pairing_disabled; BOOLEAN connect_only_paired; BOOLEAN security_mode_changed; /* mode changed during bonding */ - BOOLEAN pin_type_changed; /* pin type changed during bonding */ BOOLEAN sec_req_pending; /* TRUE if a request is pending */ +#if (CLASSIC_BT_INCLUDED == TRUE) + BOOLEAN pin_type_changed; /* pin type changed during bonding */ +#endif ///CLASSIC_BT_INCLUDED == TRUE #if (SMP_INCLUDED == TRUE) +#if (CLASSIC_BT_INCLUDED == TRUE) // btla-specific ++ #ifdef PORCHE_PAIRING_CONFLICT UINT8 pin_code_len_saved; /* for legacy devices */ @@ -866,12 +870,14 @@ typedef struct { UINT8 pin_code_len; /* for legacy devices */ PIN_CODE pin_code; /* for legacy devices */ + UINT8 disc_reason; /* for legacy devices */ + UINT16 disc_handle; /* for legacy devices */ +#endif ///CLASSIC_BT_INCLUDED == TRUE tBTM_PAIRING_STATE pairing_state; /* The current pairing state */ UINT8 pairing_flags; /* The current pairing flags */ BD_ADDR pairing_bda; /* The device currently pairing */ TIMER_LIST_ENT pairing_tle; /* Timer for pairing process */ - UINT16 disc_handle; /* for legacy devices */ - UINT8 disc_reason; /* for legacy devices */ + #endif ///SMP_INCLUDED == TRUE #if SMP_INCLUDED == TRUE || CLASSIC_BT_INCLUDED == TRUE tBTM_SEC_SERV_REC sec_serv_rec[BTM_SEC_MAX_SERVICE_RECORDS]; @@ -1122,10 +1128,10 @@ BOOLEAN btm_sec_is_le_capable_dev (BD_ADDR bda); BOOLEAN btm_ble_init_pseudo_addr (tBTM_SEC_DEV_REC *p_dev_rec, BD_ADDR new_pseudo_addr); extern BOOLEAN btm_ble_start_sec_check(BD_ADDR bd_addr, UINT16 psm, BOOLEAN is_originator, tBTM_SEC_CALLBACK *p_callback, void *p_ref_data); -extern tBTM_SEC_SERV_REC *btm_sec_find_first_serv (CONNECTION_TYPE conn_type, UINT16 psm); - #endif /* BLE_INCLUDED */ +extern tBTM_SEC_SERV_REC *btm_sec_find_first_serv (CONNECTION_TYPE conn_type, UINT16 psm); + tINQ_DB_ENT *btm_inq_db_new (BD_ADDR p_bda); #if BTM_OOB_INCLUDED == TRUE diff --git a/components/bt/bluedroid/stack/btu/btu_hcif.c b/components/bt/bluedroid/stack/btu/btu_hcif.c index 7d1ab95f96..5677491c5c 100644 --- a/components/bt/bluedroid/stack/btu/btu_hcif.c +++ b/components/bt/bluedroid/stack/btu/btu_hcif.c @@ -1436,6 +1436,7 @@ static void btu_hcif_ssr_evt (UINT8 *p, UINT16 evt_len) #if (SMP_INCLUDED == TRUE) static void btu_hcif_pin_code_request_evt (UINT8 *p) { +#if (CLASSIC_BT_INCLUDED == TRUE) BD_ADDR bda; STREAM_TO_BDADDR (bda, p); @@ -1445,6 +1446,7 @@ static void btu_hcif_pin_code_request_evt (UINT8 *p) l2c_pin_code_request (bda); btm_sec_pin_code_request (bda); +#endif ///CLASSIC_BT_INCLUDED == TRUE } diff --git a/components/bt/bluedroid/stack/include/stack/btm_api.h b/components/bt/bluedroid/stack/include/stack/btm_api.h index bf2b033bc0..b51ecb0069 100644 --- a/components/bt/bluedroid/stack/include/stack/btm_api.h +++ b/components/bt/bluedroid/stack/include/stack/btm_api.h @@ -645,8 +645,8 @@ typedef struct { INT8 rssi; /* Set to BTM_INQ_RES_IGNORE_RSSI if not valid */ UINT32 eir_uuid[BTM_EIR_SERVICE_ARRAY_SIZE]; BOOLEAN eir_complete_list; -#if (BLE_INCLUDED == TRUE) tBT_DEVICE_TYPE device_type; +#if (BLE_INCLUDED == TRUE) UINT8 inq_result_type; UINT8 ble_addr_type; tBTM_BLE_EVT_TYPE ble_evt_type; @@ -1394,12 +1394,12 @@ typedef UINT8 tBTM_SP_EVT; #define BTM_IO_CAP_IO 1 /* DisplayYesNo */ #define BTM_IO_CAP_IN 2 /* KeyboardOnly */ #define BTM_IO_CAP_NONE 3 /* NoInputNoOutput */ -#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE +// #if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE #define BTM_IO_CAP_KBDISP 4 /* Keyboard display */ #define BTM_IO_CAP_MAX 5 -#else -#define BTM_IO_CAP_MAX 4 -#endif +// #else +// #define BTM_IO_CAP_MAX 4 +// #endif typedef UINT8 tBTM_IO_CAP; diff --git a/components/bt/bluedroid/stack/include/stack/l2c_api.h b/components/bt/bluedroid/stack/include/stack/l2c_api.h index 147ed6c74c..33abaec7b3 100644 --- a/components/bt/bluedroid/stack/include/stack/l2c_api.h +++ b/components/bt/bluedroid/stack/include/stack/l2c_api.h @@ -1210,6 +1210,7 @@ extern BOOLEAN L2CA_EnableUpdateBleConnParams (BD_ADDR rem_bda, BOOLEAN enable); ** *******************************************************************************/ extern UINT8 L2CA_GetBleConnRole (BD_ADDR bd_addr); +#endif /* (BLE_INCLUDED == TRUE) */ /******************************************************************************* ** @@ -1228,7 +1229,6 @@ extern UINT16 L2CA_GetDisconnectReason (BD_ADDR remote_bda, tBT_TRANSPORT transp extern BOOLEAN L2CA_CheckIsCongest(UINT16 fixed_cid, UINT16 handle); -#endif /* (BLE_INCLUDED == TRUE) */ #ifdef __cplusplus } diff --git a/components/bt/bluedroid/stack/include/stack/smp_api.h b/components/bt/bluedroid/stack/include/stack/smp_api.h index 390f6209e6..1064237c8d 100644 --- a/components/bt/bluedroid/stack/include/stack/smp_api.h +++ b/components/bt/bluedroid/stack/include/stack/smp_api.h @@ -29,7 +29,7 @@ #define SMP_PIN_CODE_LEN_MAX PIN_CODE_LEN #define SMP_PIN_CODE_LEN_MIN 6 -#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE +// #if SMP_INCLUDED == TRUE /* SMP command code */ #define SMP_OPCODE_PAIRING_REQ 0x01 #define SMP_OPCODE_PAIRING_RSP 0x02 @@ -48,7 +48,7 @@ #define SMP_OPCODE_MAX SMP_OPCODE_PAIR_KEYPR_NOTIF #define SMP_OPCODE_MIN SMP_OPCODE_PAIRING_REQ #define SMP_OPCODE_PAIR_COMMITM 0x0F -#endif +// #endif /* SMP event type */ #define SMP_IO_CAP_REQ_EVT 1 /* IO capability request event */ diff --git a/components/bt/bluedroid/stack/l2cap/l2c_api.c b/components/bt/bluedroid/stack/l2cap/l2c_api.c index 906b961d20..9a50d4ba72 100644 --- a/components/bt/bluedroid/stack/l2cap/l2c_api.c +++ b/components/bt/bluedroid/stack/l2cap/l2c_api.c @@ -1301,6 +1301,7 @@ UINT8 L2CA_GetChnlFcrMode (UINT16 lcid) #endif ///CLASSIC_BT_INCLUDED == TRUE +#if (BLE_INCLUDED == TRUE) /******************************************************************************* ** ** Function L2CA_RegisterLECoc @@ -1610,8 +1611,7 @@ BOOLEAN L2CA_GetPeerLECocConfig (UINT16 lcid, tL2CAP_LE_CFG_INFO* peer_cfg) return TRUE; } - - +#endif ///BLE_INCLUDED == TRUE #if (L2CAP_NUM_FIXED_CHNLS > 0) /******************************************************************************* diff --git a/components/bt/bluedroid/stack/l2cap/l2c_ble.c b/components/bt/bluedroid/stack/l2cap/l2c_ble.c index 9a26969fac..b4e347335f 100644 --- a/components/bt/bluedroid/stack/l2cap/l2c_ble.c +++ b/components/bt/bluedroid/stack/l2cap/l2c_ble.c @@ -223,28 +223,6 @@ UINT8 L2CA_GetBleConnRole (BD_ADDR bd_addr) return role; } -/******************************************************************************* -** -** Function L2CA_GetDisconnectReason -** -** Description This function returns the disconnect reason code. -** -** Returns disconnect reason -** -*******************************************************************************/ -UINT16 L2CA_GetDisconnectReason (BD_ADDR remote_bda, tBT_TRANSPORT transport) -{ - tL2C_LCB *p_lcb; - UINT16 reason = 0; - - if ((p_lcb = l2cu_find_lcb_by_bd_addr (remote_bda, transport)) != NULL) { - reason = p_lcb->disc_reason; - } - - L2CAP_TRACE_DEBUG ("L2CA_GetDisconnectReason=%d ", reason); - - return reason; -} /******************************************************************************* ** @@ -1491,3 +1469,25 @@ BOOLEAN l2ble_sec_access_req(BD_ADDR bd_addr, UINT16 psm, BOOLEAN is_originator, } #endif /* #if (SMP_INCLUDED == TRUE) */ #endif /* (BLE_INCLUDED == TRUE) */ +/******************************************************************************* +** +** Function L2CA_GetDisconnectReason +** +** Description This function returns the disconnect reason code. +** +** Returns disconnect reason +** +*******************************************************************************/ +UINT16 L2CA_GetDisconnectReason (BD_ADDR remote_bda, tBT_TRANSPORT transport) +{ + tL2C_LCB *p_lcb; + UINT16 reason = 0; + + if ((p_lcb = l2cu_find_lcb_by_bd_addr (remote_bda, transport)) != NULL) { + reason = p_lcb->disc_reason; + } + + L2CAP_TRACE_DEBUG ("L2CA_GetDisconnectReason=%d ", reason); + + return reason; +} \ No newline at end of file diff --git a/components/bt/bluedroid/stack/l2cap/l2c_link.c b/components/bt/bluedroid/stack/l2cap/l2c_link.c index 15de6060b1..1117a0abae 100644 --- a/components/bt/bluedroid/stack/l2cap/l2c_link.c +++ b/components/bt/bluedroid/stack/l2cap/l2c_link.c @@ -357,7 +357,9 @@ BOOLEAN l2c_link_hci_disc_comp (UINT16 handle, UINT8 reason) p_lcb = l2cu_find_lcb_by_handle (handle); /* If we don't have one, maybe an SCO link. Send to MM */ if (!p_lcb) { +#if (BLE_INCLUDED == TRUE) BTM_Recovery_Pre_State(); +#endif ///BLE_INCLUDED == TRUE status = FALSE; } else { /* There can be a case when we rejected PIN code authentication */ @@ -1026,8 +1028,10 @@ void l2c_link_check_send_pkts (tL2C_LCB *p_lcb, tL2C_CCB *p_ccb, BT_HDR *p_buf) /* Loop through, starting at the next */ for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p_lcb++) { - /* If controller window is full, nothing to do */ +#if (BLE_INCLUDED == TRUE) L2CAP_TRACE_DEBUG("window = %d,robin_unacked = %d,robin_quota=%d",l2cb.controller_le_xmit_window,l2cb.ble_round_robin_unacked,l2cb.ble_round_robin_quota); +#endif ///BLE_INCLUDED == TRUE + /* If controller window is full, nothing to do */ if (((l2cb.controller_xmit_window == 0 || (l2cb.round_robin_unacked >= l2cb.round_robin_quota)) #if (BLE_INCLUDED == TRUE) @@ -1038,7 +1042,7 @@ void l2c_link_check_send_pkts (tL2C_LCB *p_lcb, tL2C_CCB *p_ccb, BT_HDR *p_buf) l2cb.controller_le_xmit_window == 0 ))) #else )) -#endif +#endif ///BLE_INCLUDED == TRUE break; diff --git a/components/bt/bluedroid/stack/l2cap/l2c_main.c b/components/bt/bluedroid/stack/l2cap/l2c_main.c index 45baeaa3b9..e50e59dceb 100644 --- a/components/bt/bluedroid/stack/l2cap/l2c_main.c +++ b/components/bt/bluedroid/stack/l2cap/l2c_main.c @@ -295,7 +295,9 @@ void l2c_rcv_acl_data (BT_HDR *p_msg) /* we have received credits more than max coc credits, * so disconnecting the Le Coc Channel */ +#if (BLE_INCLUDED == TRUE) l2cble_send_peer_disc_req (p_ccb); +#endif ///BLE_INCLUDED == TRUE } else { p_ccb->peer_conn_cfg.credits += credit; l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, NULL); @@ -926,6 +928,7 @@ void l2c_process_timeout (TIMER_LIST_ENT *p_tle) l2c_info_timeout((tL2C_LCB *)p_tle->param); break; case BTU_TTYPE_L2CAP_UPDA_CONN_PARAMS: { +#if (BLE_INCLUDED == TRUE) UINT8 status = HCI_ERR_HOST_TIMEOUT; tL2C_LCB *p_lcb = (tL2C_LCB *)p_tle->param; if (p_lcb){ @@ -933,6 +936,7 @@ void l2c_process_timeout (TIMER_LIST_ENT *p_tle) p_lcb->conn_update_mask &= ~L2C_BLE_UPDATE_PARAM_FULL; } l2c_send_update_conn_params_cb(p_lcb, status); +#endif ///BLE_INCLUDED == TRUE break; } } diff --git a/components/bt/bluedroid/stack/l2cap/l2c_utils.c b/components/bt/bluedroid/stack/l2cap/l2c_utils.c index 4768d8808a..92e1eede37 100644 --- a/components/bt/bluedroid/stack/l2cap/l2c_utils.c +++ b/components/bt/bluedroid/stack/l2cap/l2c_utils.c @@ -243,6 +243,7 @@ void l2cu_release_lcb (tL2C_LCB *p_lcb) (*p_cb) (L2CAP_PING_RESULT_NO_LINK); } +#if (BLE_INCLUDED == TRUE) /* Check and release all the LE COC connections waiting for security */ if (p_lcb->le_sec_pending_q) { @@ -257,6 +258,7 @@ void l2cu_release_lcb (tL2C_LCB *p_lcb) fixed_queue_free(p_lcb->le_sec_pending_q, NULL); p_lcb->le_sec_pending_q = NULL; } +#endif ///BLE_INCLUDED == TRUE #if (C2H_FLOW_CONTROL_INCLUDED == TRUE) p_lcb->completed_packets = 0; @@ -1765,6 +1767,7 @@ tL2C_RCB *l2cu_allocate_rcb (UINT16 psm) return (NULL); } +#if (BLE_INCLUDED == TRUE) /******************************************************************************* ** ** Function l2cu_allocate_ble_rcb @@ -1796,6 +1799,7 @@ tL2C_RCB *l2cu_allocate_ble_rcb (UINT16 psm) /* If here, no free RCB found */ return (NULL); } +#endif ///BLE_INCLUDED == TRUE /******************************************************************************* ** @@ -1867,6 +1871,7 @@ tL2C_RCB *l2cu_find_rcb_by_psm (UINT16 psm) return (NULL); } +#if (BLE_INCLUDED == TRUE) /******************************************************************************* ** ** Function l2cu_find_ble_rcb_by_psm @@ -1892,7 +1897,7 @@ tL2C_RCB *l2cu_find_ble_rcb_by_psm (UINT16 psm) /* If here, no match found */ return (NULL); } - +#endif ///BLE_INCLUDED == TRUE /******************************************************************************* diff --git a/components/bt/bluedroid/stack/smp/include/smp_int.h b/components/bt/bluedroid/stack/smp/include/smp_int.h index cf3683c07c..d8fb55c381 100644 --- a/components/bt/bluedroid/stack/smp/include/smp_int.h +++ b/components/bt/bluedroid/stack/smp/include/smp_int.h @@ -24,7 +24,7 @@ #ifndef SMP_INT_H #define SMP_INT_H -#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE) +// #if (SMP_INCLUDED == TRUE) #include "stack/btu.h" #include "stack/btm_ble_api.h" @@ -537,6 +537,6 @@ extern BOOLEAN aes_cipher_msg_auth_code(BT_OCTET16 key, UINT8 *input, UINT16 len UINT16 tlen, UINT8 *p_signature); extern void print128(BT_OCTET16 x, const UINT8 *key_name); -#endif ///BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE +// #endif ///BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE #endif /* SMP_INT_H */ diff --git a/components/bt/bluedroid/stack/smp/smp_act.c b/components/bt/bluedroid/stack/smp/smp_act.c index d9f3369500..487dadf62c 100644 --- a/components/bt/bluedroid/stack/smp/smp_act.c +++ b/components/bt/bluedroid/stack/smp/smp_act.c @@ -46,10 +46,17 @@ const UINT8 smp_association_table[2][SMP_IO_CAP_MAX][SMP_IO_CAP_MAX] = { #define SMP_KEY_DIST_TYPE_MAX 4 const tSMP_ACT smp_distribute_act [] = { +#if (BLE_INCLUDED == TRUE) smp_generate_ltk, smp_send_id_info, smp_generate_csrk, smp_set_derive_link_key +#else + NULL, + NULL, + NULL, + NULL +#endif ///BLE_INCLUDED == TRUE }; extern UINT8 bta_dm_co_ble_get_accept_auth_enable(void); @@ -191,7 +198,7 @@ void smp_send_app_cback(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) smp_sm_event(p_cb, SMP_IO_RSP_EVT, NULL); break; - +#if (CLASSIC_BT_INCLUDED == TRUE) case SMP_BR_KEYS_REQ_EVT: p_cb->loc_enc_size = cb_data.io_req.max_key_size; p_cb->local_i_key = cb_data.io_req.init_keys; @@ -206,6 +213,7 @@ void smp_send_app_cback(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) smp_br_state_machine_event(p_cb, SMP_BR_KEYS_RSP_EVT, NULL); break; +#endif ///CLASSIC_BT_INCLUDED == TRUE } } } @@ -241,13 +249,15 @@ void smp_send_pair_fail(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) *******************************************************************************/ void smp_send_pair_req(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) { - tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (p_cb->pairing_bda); SMP_TRACE_DEBUG("%s\n", __func__); +#if (BLE_INCLUDED == TRUE) + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (p_cb->pairing_bda); /* erase all keys when master sends pairing req*/ if (p_dev_rec) { btm_sec_clear_ble_keys(p_dev_rec); } +#endif ///BLE_INCLUDED == TRUE /* do not manipulate the key, let app decide, leave out to BTM to mandate key distribution for bonding case */ smp_send_cmd(SMP_OPCODE_PAIRING_REQ, p_cb); @@ -261,6 +271,7 @@ void smp_send_pair_rsp(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) { SMP_TRACE_DEBUG("%s\n", __func__); +#if (BLE_INCLUDED == TRUE) p_cb->local_i_key &= p_cb->peer_i_key; p_cb->local_r_key &= p_cb->peer_r_key; @@ -271,6 +282,7 @@ void smp_send_pair_rsp(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) smp_decide_association_model(p_cb, NULL); } } +#endif ///BLE_INCLUDED == TRUE } /******************************************************************************* @@ -365,6 +377,7 @@ void smp_send_enc_info(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) le_key.key_size = p_cb->loc_enc_size; le_key.sec_level = p_cb->sec_level; +#if (BLE_INCLUDED == TRUE) if ((p_cb->peer_auth_req & SMP_AUTH_BOND) && (p_cb->loc_auth_req & SMP_AUTH_BOND)) { btm_sec_save_le_key(p_cb->pairing_bda, BTM_LE_KEY_LENC, (tBTM_LE_KEY_VALUE *)&le_key, TRUE); @@ -373,6 +386,7 @@ void smp_send_enc_info(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) SMP_TRACE_DEBUG ("%s\n", __func__); smp_key_distribution(p_cb, NULL); +#endif ///BLE_INCLUDED == TRUE } /******************************************************************************* @@ -381,21 +395,24 @@ void smp_send_enc_info(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) *******************************************************************************/ void smp_send_id_info(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) { - tBTM_LE_KEY_VALUE le_key; SMP_TRACE_DEBUG("%s\n", __func__); smp_update_key_mask (p_cb, SMP_SEC_KEY_TYPE_ID, FALSE); smp_send_cmd(SMP_OPCODE_IDENTITY_INFO, p_cb); smp_send_cmd(SMP_OPCODE_ID_ADDR, p_cb); +#if (BLE_INCLUDED == TRUE) + tBTM_LE_KEY_VALUE le_key; if ((p_cb->peer_auth_req & SMP_AUTH_BOND) && (p_cb->loc_auth_req & SMP_AUTH_BOND)) { btm_sec_save_le_key(p_cb->pairing_bda, BTM_LE_KEY_LID, &le_key, TRUE); } +#endif ///BLE_INCLUDED == TRUE smp_key_distribution_by_transport(p_cb, NULL); } +#if (BLE_INCLUDED == TRUE) /******************************************************************************* ** Function smp_send_csrk_info ** Description send CSRK command. @@ -441,7 +458,6 @@ void smp_proc_sec_req(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) SMP_TRACE_DEBUG("%s auth_req=0x%x", __func__, auth_req); p_cb->cb_evt = 0; - btm_ble_link_sec_check(p_cb->pairing_bda, auth_req, &sec_req_act); SMP_TRACE_DEBUG("%s sec_req_act=0x%x", __func__, sec_req_act); @@ -478,6 +494,7 @@ void smp_proc_sec_req(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) break; } } +#endif ///BLE_INCLUDED == TRUE /******************************************************************************* ** Function smp_proc_sec_grant @@ -532,6 +549,7 @@ uint16_t smp_get_auth_mode (tSMP_ASSO_MODEL model) return auth; } +#if (BLE_INCLUDED == TRUE) /******************************************************************************* ** Function smp_proc_pair_cmd ** Description Process the SMP pairing request/response from peer device @@ -652,6 +670,7 @@ void smp_proc_pair_cmd(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) } } } +#endif ///BLE_INCLUDED == TRUE /******************************************************************************* ** Function smp_proc_confirm @@ -823,6 +842,7 @@ void smp_process_keypress_notification(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) p_cb->cb_evt = SMP_PEER_KEYPR_NOT_EVT; } +#if (CLASSIC_BT_INCLUDED == TRUE) /******************************************************************************* ** Function smp_br_process_pairing_command ** Description Process the SMP pairing request/response from peer device via @@ -842,10 +862,12 @@ void smp_br_process_pairing_command(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) return; } +#if (BLE_INCLUDED == TRUE) /* erase all keys if it is slave proc pairing req*/ if (p_dev_rec && (p_cb->role == HCI_ROLE_SLAVE)) { btm_sec_clear_ble_keys(p_dev_rec); } +#endif ///BLE_INCLUDED == TRUE p_cb->flags |= SMP_PAIR_FLAG_ENC_AFTER_PAIR; @@ -966,7 +988,9 @@ void smp_br_select_next_key(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) } } } +#endif ///CLASSIC_BT_INCLUDED == TRUE +#if (BLE_INCLUDED == TRUE) /******************************************************************************* ** Function smp_proc_enc_info ** Description process encryption information from peer device @@ -980,6 +1004,8 @@ void smp_proc_enc_info(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) smp_key_distribution(p_cb, NULL); } +#endif ///BLE_INCLUDED == TRUE + /******************************************************************************* ** Function smp_proc_master_id ** Description process master ID from slave device @@ -1001,6 +1027,7 @@ void smp_proc_master_id(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) le_key.sec_level = p_cb->sec_level; le_key.key_size = p_cb->loc_enc_size; +#if (BLE_INCLUDED == TRUE) if ((p_cb->peer_auth_req & SMP_AUTH_BOND) && (p_cb->loc_auth_req & SMP_AUTH_BOND)) { btm_sec_save_le_key(p_cb->pairing_bda, BTM_LE_KEY_PENC, @@ -1008,6 +1035,7 @@ void smp_proc_master_id(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) } smp_key_distribution(p_cb, NULL); +#endif ///BLE_INCLUDED == TRUE } /******************************************************************************* @@ -1044,11 +1072,14 @@ void smp_proc_id_addr(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) p_cb->id_addr_type = pid_key.addr_type; memcpy(p_cb->id_addr, pid_key.static_addr, BD_ADDR_LEN); +#if (BLE_INCLUDED == TRUE) /* store the ID key from peer device */ if ((p_cb->peer_auth_req & SMP_AUTH_BOND) && (p_cb->loc_auth_req & SMP_AUTH_BOND)) { btm_sec_save_le_key(p_cb->pairing_bda, BTM_LE_KEY_PID, (tBTM_LE_KEY_VALUE *)&pid_key, TRUE); } +#endif ///BLE_INCLUDED == TRUE + smp_key_distribution_by_transport(p_cb, NULL); } @@ -1058,6 +1089,7 @@ void smp_proc_id_addr(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) *******************************************************************************/ void smp_proc_srk_info(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) { +#if (BLE_INCLUDED == TRUE) tBTM_LE_PCSRK_KEYS le_key; SMP_TRACE_DEBUG("%s", __func__); @@ -1073,6 +1105,8 @@ void smp_proc_srk_info(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) BTM_LE_KEY_PCSRK, (tBTM_LE_KEY_VALUE *)&le_key, TRUE); } + +#endif ///BLE_INCLUDED == TRUE smp_key_distribution_by_transport(p_cb, NULL); } @@ -1107,6 +1141,7 @@ void smp_proc_compare(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) } } +#if (BLE_INCLUDED == TRUE) /******************************************************************************* ** Function smp_proc_sl_key ** Description process key ready events. @@ -1147,6 +1182,7 @@ void smp_start_enc(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason); } } +#endif ///BLE_INCLUDED == TRUE /******************************************************************************* ** Function smp_proc_discard @@ -1246,7 +1282,7 @@ void smp_key_pick_key(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) while (i < SMP_KEY_DIST_TYPE_MAX) { SMP_TRACE_DEBUG("key to send = %02x, i = %d\n", key_to_dist, i); - if (key_to_dist & (1 << i)) { + if (key_to_dist & (1 << i) && smp_distribute_act[i] != NULL) { SMP_TRACE_DEBUG("smp_distribute_act[%d]\n", i); (* smp_distribute_act[i])(p_cb, p_data); break; @@ -1254,6 +1290,8 @@ void smp_key_pick_key(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) i ++; } } + +#if (BLE_INCLUDED == TRUE) /******************************************************************************* ** Function smp_key_distribution ** Description start key distribution if required. @@ -1417,6 +1455,7 @@ void smp_process_io_response(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) smp_send_pair_rsp(p_cb, NULL); } } +#endif ///BLE_INCLUDED == TRUE /******************************************************************************* ** Function smp_br_process_slave_keys_response @@ -1481,6 +1520,7 @@ void smp_idle_terminate(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) } } +#if (BLE_INCLUDED == TRUE) /******************************************************************************* ** Function smp_fast_conn_param ** Description apply default connection parameter for pairing process @@ -1706,6 +1746,7 @@ void smp_process_peer_nonce(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) SMP_TRACE_DEBUG("%s end\n ", __FUNCTION__); } +#endif ///BLE_INCLUDED == TRUE /******************************************************************************* ** Function smp_match_dhkey_checks @@ -1793,6 +1834,7 @@ void smp_wait_for_both_public_keys(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) } } +#if (BLE_INCLUDED == TRUE) /******************************************************************************* ** Function smp_start_passkey_verification ** Description Starts SC passkey entry verification. @@ -2059,7 +2101,9 @@ void smp_derive_link_key_from_long_term_key(tSMP_CB *p_cb, tSMP_INT_DATA *p_data return; } } +#endif ///BLE_INCLUDED == TRUE +#if (CLASSIC_BT_INCLUDED == TRUE) /******************************************************************************* ** ** Function smp_br_process_link_key @@ -2087,6 +2131,7 @@ void smp_br_process_link_key(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) smp_update_key_mask (p_cb, SMP_SEC_KEY_TYPE_ENC, FALSE); smp_br_select_next_key(p_cb, NULL); } +#endif ///CLASSIC_BT_INCLUDED == TRUE /******************************************************************************* ** Function smp_key_distribution_by_transport @@ -2097,9 +2142,13 @@ void smp_key_distribution_by_transport(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) { SMP_TRACE_DEBUG("%s\n", __func__); if (p_cb->smp_over_br) { +#if (CLASSIC_BT_INCLUDED == TRUE) smp_br_select_next_key(p_cb, NULL); +#endif ///CLASSIC_BT_INCLUDED == TRUE } else { +#if (BLE_INCLUDED == TRUE) smp_key_distribution(p_cb, NULL); +#endif ///BLE_INCLUDED == TRUE } } diff --git a/components/bt/bluedroid/stack/smp/smp_api.c b/components/bt/bluedroid/stack/smp/smp_api.c index 37aa2b9111..89a32308e5 100644 --- a/components/bt/bluedroid/stack/smp/smp_api.c +++ b/components/bt/bluedroid/stack/smp/smp_api.c @@ -183,6 +183,7 @@ tSMP_STATUS SMP_Pair (BD_ADDR bd_addr) ** Returns SMP_STARTED if pairing started, otherwise reason for failure. ** *******************************************************************************/ +#if (CLASSIC_BT_INCLUDED == TRUE) tSMP_STATUS SMP_BR_PairWith (BD_ADDR bd_addr) { tSMP_CB *p_cb = &smp_cb; @@ -212,6 +213,7 @@ tSMP_STATUS SMP_BR_PairWith (BD_ADDR bd_addr) return SMP_STARTED; } +#endif ///CLASSIC_BT_INCLUDED == TRUE /******************************************************************************* ** @@ -258,6 +260,7 @@ void SMP_SecurityGrant(BD_ADDR bd_addr, UINT8 res) { SMP_TRACE_EVENT ("SMP_SecurityGrant "); +#if (CLASSIC_BT_INCLUDED == TRUE) if (smp_cb.smp_over_br) { if (smp_cb.br_state != SMP_BR_STATE_WAIT_APP_RSP || smp_cb.cb_evt != SMP_SEC_REQUEST_EVT || @@ -271,6 +274,7 @@ void SMP_SecurityGrant(BD_ADDR bd_addr, UINT8 res) smp_br_state_machine_event(&smp_cb, SMP_BR_API_SEC_GRANT_EVT, &res); return; } +#endif ///CLASSIC_BT_INCLUDED == TRUE if (smp_cb.state != SMP_STATE_WAIT_APP_RSP || smp_cb.cb_evt != SMP_SEC_REQUEST_EVT || diff --git a/components/bt/bluedroid/stack/smp/smp_br_main.c b/components/bt/bluedroid/stack/smp/smp_br_main.c index ba2eb97456..5ef7a7c694 100644 --- a/components/bt/bluedroid/stack/smp/smp_br_main.c +++ b/components/bt/bluedroid/stack/smp/smp_br_main.c @@ -21,7 +21,7 @@ #include #include "smp_int.h" -#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE) +#if ( CLASSIC_BT_INCLUDED== TRUE && SMP_INCLUDED == TRUE) const char *const smp_br_state_name [SMP_BR_STATE_MAX + 1] = { "SMP_BR_STATE_IDLE", diff --git a/components/bt/bluedroid/stack/smp/smp_keys.c b/components/bt/bluedroid/stack/smp/smp_keys.c index c993c7387f..4c39635d9f 100644 --- a/components/bt/bluedroid/stack/smp/smp_keys.c +++ b/components/bt/bluedroid/stack/smp/smp_keys.c @@ -23,7 +23,7 @@ ******************************************************************************/ #include "common/bt_target.h" -#if SMP_INCLUDED == TRUE +#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE) #if SMP_DEBUG == TRUE #include #endif @@ -380,10 +380,13 @@ void smp_generate_ltk(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) BOOLEAN div_status; SMP_TRACE_DEBUG ("%s\n", __FUNCTION__); +#if (CLASSIC_BT_INCLUDED == TRUE) if (smp_get_br_state() == SMP_BR_STATE_BOND_PENDING) { smp_br_process_link_key(p_cb, NULL); return; - } else if (p_cb->le_secure_connections_mode_is_used) { + } +#endif ///CLASSIC_BT_INCLUDED == TRUE + if (p_cb->le_secure_connections_mode_is_used) { smp_process_secure_connection_long_term_key(); return; } @@ -432,7 +435,9 @@ void smp_compute_csrk(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) if (!SMP_Encrypt(er, BT_OCTET16_LEN, buffer, 4, &output)) { SMP_TRACE_ERROR("smp_generate_csrk failed\n"); if (p_cb->smp_over_br) { +#if (CLASSIC_BT_INCLUDED == TRUE) smp_br_state_machine_event(p_cb, SMP_BR_AUTH_CMPL_EVT, &status); +#endif ///CLASSIC_BT_INCLUDED == TRUE } else { smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &status); } diff --git a/components/bt/bluedroid/stack/smp/smp_l2c.c b/components/bt/bluedroid/stack/smp/smp_l2c.c index a3ac356aca..67dd1ac5ae 100644 --- a/components/bt/bluedroid/stack/smp/smp_l2c.c +++ b/components/bt/bluedroid/stack/smp/smp_l2c.c @@ -35,10 +35,12 @@ static void smp_tx_complete_callback(UINT16 cid, UINT16 num_pkt); +#if (BLE_INCLUDED == TRUE) static void smp_connect_callback(UINT16 channel, BD_ADDR bd_addr, BOOLEAN connected, UINT16 reason, tBT_TRANSPORT transport); static void smp_data_received(UINT16 channel, BD_ADDR bd_addr, BT_HDR *p_buf); +#endif ///BLE_INCLUDED == TRUE #if (CLASSIC_BT_INCLUDED == TRUE) static void smp_br_connect_callback(UINT16 channel, BD_ADDR bd_addr, BOOLEAN connected, UINT16 reason, tBT_TRANSPORT transport); @@ -64,8 +66,6 @@ void smp_l2cap_if_init (void) fixed_reg.fixed_chnl_opts.mps = 0; fixed_reg.fixed_chnl_opts.tx_win_sz = 0; - fixed_reg.pL2CA_FixedConn_Cb = smp_connect_callback; - fixed_reg.pL2CA_FixedData_Cb = smp_data_received; fixed_reg.pL2CA_FixedTxComplete_Cb = smp_tx_complete_callback; fixed_reg.pL2CA_FixedCong_Cb = NULL; /* do not handle congestion on this channel */ @@ -75,7 +75,12 @@ void smp_l2cap_if_init (void) will cause the disconnect event to go back up for a long time. Set to 0 will be disconnected directly, and it will come up pairing failure, so it will not cause adverse effects. */ +#if (BLE_INCLUDED == TRUE) + fixed_reg.pL2CA_FixedConn_Cb = smp_connect_callback; + fixed_reg.pL2CA_FixedData_Cb = smp_data_received; L2CA_RegisterFixedChannel (L2CAP_SMP_CID, &fixed_reg); +#endif ///BLE_INCLUDED == TRUE + #if (CLASSIC_BT_INCLUDED == TRUE) fixed_reg.pL2CA_FixedConn_Cb = smp_br_connect_callback; fixed_reg.pL2CA_FixedData_Cb = smp_br_data_received; @@ -84,6 +89,7 @@ void smp_l2cap_if_init (void) #endif ///CLASSIC_BT_INCLUDED == TRUE } +#if (BLE_INCLUDED == TRUE) /******************************************************************************* ** ** Function smp_connect_callback @@ -107,7 +113,7 @@ static void smp_connect_callback (UINT16 channel, BD_ADDR bd_addr, BOOLEAN conne } if(!connected && &p_cb->rsp_timer_ent) { //free timer - btu_free_timer(&p_cb->rsp_timer_ent); + btu_free_timer(&p_cb->rsp_timer_ent); } if (memcmp(bd_addr, p_cb->pairing_bda, BD_ADDR_LEN) == 0) { SMP_TRACE_EVENT ("%s() for pairing BDA: %08x%04x Event: %s\n", @@ -200,6 +206,7 @@ static void smp_data_received(UINT16 channel, BD_ADDR bd_addr, BT_HDR *p_buf) osi_free (p_buf); } +#endif ///BLE_INCLUDED == TRUE /******************************************************************************* ** @@ -223,7 +230,9 @@ static void smp_tx_complete_callback (UINT16 cid, UINT16 num_pkt) if (cid == L2CAP_SMP_CID) { smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason); } else { +#if (CLASSIC_BT_INCLUDED == TRUE) smp_br_state_machine_event(p_cb, SMP_BR_AUTH_CMPL_EVT, &reason); +#endif ///CLASSIC_BT_INCLUDED == TRUE } } } diff --git a/components/bt/bluedroid/stack/smp/smp_main.c b/components/bt/bluedroid/stack/smp/smp_main.c index 0880fe910f..1becb36f01 100644 --- a/components/bt/bluedroid/stack/smp/smp_main.c +++ b/components/bt/bluedroid/stack/smp/smp_main.c @@ -163,7 +163,8 @@ enum { SMP_SM_NO_ACTION }; -static const tSMP_ACT smp_sm_action[] = { +#if (BLE_INCLUDED == TRUE) +static const tSMP_ACT smp_sm_action[SMP_SM_NO_ACTION] = { smp_proc_sec_req, smp_send_pair_req, smp_send_pair_rsp, @@ -226,6 +227,9 @@ static const tSMP_ACT smp_sm_action[] = { smp_idle_terminate, smp_fast_conn_param }; +#else +static const tSMP_ACT smp_sm_action[SMP_SM_NO_ACTION] = {NULL}; +#endif ///BLE_INCLUDED == TRUE /************ SMP Master FSM State/Event Indirection Table **************/ static const UINT8 smp_master_entry_map[][SMP_STATE_MAX] = { @@ -766,7 +770,7 @@ void smp_sm_event(tSMP_CB *p_cb, tSMP_EVENT event, void *p_data) /* execute action */ /* execute action functions */ for (i = 0; i < SMP_NUM_ACTIONS; i++) { - if ((action = state_table[entry - 1][i]) != SMP_SM_NO_ACTION) { + if ((action = state_table[entry - 1][i]) != SMP_SM_NO_ACTION && smp_sm_action[action] != NULL) { (*smp_sm_action[action])(p_cb, (tSMP_INT_DATA *)p_data); } else { break; diff --git a/components/bt/bluedroid/stack/smp/smp_utils.c b/components/bt/bluedroid/stack/smp/smp_utils.c index 6d64f44abc..5edf0b8ab2 100644 --- a/components/bt/bluedroid/stack/smp/smp_utils.c +++ b/components/bt/bluedroid/stack/smp/smp_utils.c @@ -367,7 +367,9 @@ BOOLEAN smp_send_cmd(UINT8 cmd_code, tSMP_CB *p_cb) if (!sent) { if (p_cb->smp_over_br) { +#if (CLASSIC_BT_INCLUDED == TRUE) smp_br_state_machine_event(p_cb, SMP_BR_AUTH_CMPL_EVT, &failure); +#endif ///CLASSIC_BT_INCLUDED == TRUE } else { smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &failure); } @@ -393,7 +395,9 @@ void smp_rsp_timeout(TIMER_LIST_ENT *p_tle) SMP_TRACE_EVENT("%s state:%d br_state:%d", __FUNCTION__, p_cb->state, p_cb->br_state); if (p_cb->smp_over_br) { +#if (CLASSIC_BT_INCLUDED == TRUE) smp_br_state_machine_event(p_cb, SMP_BR_AUTH_CMPL_EVT, &failure); +#endif ///CLASSIC_BT_INCLUDED == TRUE } else { smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &failure); } @@ -550,6 +554,7 @@ static BT_HDR *smp_build_master_id_cmd(UINT8 cmd_code, tSMP_CB *p_cb) static BT_HDR *smp_build_identity_info_cmd(UINT8 cmd_code, tSMP_CB *p_cb) { BT_HDR *p_buf = NULL ; +#if (BLE_INCLUDED == TRUE) UINT8 *p; BT_OCTET16 irk; UNUSED(cmd_code); @@ -568,6 +573,7 @@ static BT_HDR *smp_build_identity_info_cmd(UINT8 cmd_code, tSMP_CB *p_cb) p_buf->len = SMP_ID_INFO_SIZE; } +#endif ///BLE_INCLUDED == TRUE return p_buf; } @@ -590,13 +596,16 @@ static BT_HDR *smp_build_id_addr_cmd(UINT8 cmd_code, tSMP_CB *p_cb) p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; UINT8_TO_STREAM (p, SMP_OPCODE_ID_ADDR); - /* Identity Address Information is used in the Transport Specific Key Distribution phase to distribute + /* Identity Address Information is used in the Transport Specific Key Distribution phase to distribute its public device address or static random address. if slave using static random address is encrypted, it should distribute its static random address */ +#if (BLE_INCLUDED == TRUE) if(btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type == BLE_ADDR_RANDOM && memcmp(btm_cb.ble_ctr_cb.addr_mgnt_cb.static_rand_addr, btm_cb.ble_ctr_cb.addr_mgnt_cb.private_addr,6) == 0) { UINT8_TO_STREAM (p, 0x01); BDADDR_TO_STREAM (p, btm_cb.ble_ctr_cb.addr_mgnt_cb.static_rand_addr); - } else { + } else +#endif ///BLE_INCLUDED == TRUE + { UINT8_TO_STREAM (p, 0); BDADDR_TO_STREAM (p, controller_get_interface()->get_address()->address); } @@ -997,6 +1006,7 @@ void smp_proc_pairing_cmpl(tSMP_CB *p_cb) memcpy (pairing_bda, p_cb->pairing_bda, BD_ADDR_LEN); +#if (BLE_INCLUDED == TRUE) #if (SMP_SLAVE_CON_PARAMS_UPD_ENABLE == TRUE) if (p_cb->role == HCI_ROLE_SLAVE) { if(p_rec && p_rec->ble.skip_update_conn_param) { @@ -1006,7 +1016,10 @@ void smp_proc_pairing_cmpl(tSMP_CB *p_cb) L2CA_EnableUpdateBleConnParams(p_cb->pairing_bda, TRUE); } } + #endif +#endif ///BLE_INCLUDED == TRUE + smp_reset_control_value(p_cb); if (p_callback) { @@ -1405,7 +1418,7 @@ void smp_collect_peer_io_capabilities(UINT8 *iocap, tSMP_CB *p_cb) iocap[1] = p_cb->peer_oob_flag; iocap[2] = p_cb->peer_auth_req; } - +#if (BLE_INCLUDED == TRUE) /******************************************************************************* ** Function smp_collect_local_ble_address ** @@ -1558,7 +1571,7 @@ BOOLEAN smp_calculate_f5_mackey_and_long_term_key(tSMP_CB *p_cb) SMP_TRACE_EVENT ("%s is completed\n", __func__); return TRUE; } - +#endif ///BLE_INCLUDED == TRUE /******************************************************************************* ** ** Function smp_request_oob_data diff --git a/examples/bluetooth/a2dp_sink/sdkconfig.defaults b/examples/bluetooth/a2dp_sink/sdkconfig.defaults index 132f512cb2..e27427a3bd 100644 --- a/examples/bluetooth/a2dp_sink/sdkconfig.defaults +++ b/examples/bluetooth/a2dp_sink/sdkconfig.defaults @@ -8,6 +8,4 @@ CONFIG_BT_BLUEDROID_ENABLED=y CONFIG_BT_CLASSIC_ENABLED=y CONFIG_BT_A2DP_ENABLE=y CONFIG_BT_SPP_ENABLED=n -CONFIG_BT_GATTS_ENABLE=n -CONFIG_BT_GATTC_ENABLE=n -CONFIG_BT_BLE_SMP_ENABLE=n +CONFIG_BT_BLE_ENABLED=n diff --git a/examples/bluetooth/a2dp_source/sdkconfig.defaults b/examples/bluetooth/a2dp_source/sdkconfig.defaults index fbee626500..c7adef6a5f 100644 --- a/examples/bluetooth/a2dp_source/sdkconfig.defaults +++ b/examples/bluetooth/a2dp_source/sdkconfig.defaults @@ -8,6 +8,4 @@ CONFIG_BT_BLUEDROID_ENABLED=y CONFIG_BT_CLASSIC_ENABLED=y CONFIG_BT_A2DP_ENABLE=y CONFIG_BT_SPP_ENABLED=n -CONFIG_BT_GATTS_ENABLE=n -CONFIG_BT_GATTC_ENABLE=n -CONFIG_BT_BLE_SMP_ENABLE=n +CONFIG_BT_BLE_ENABLED=n diff --git a/examples/bluetooth/bt_discovery/sdkconfig.defaults b/examples/bluetooth/bt_discovery/sdkconfig.defaults index af5b5d9d66..00e3d9971e 100644 --- a/examples/bluetooth/bt_discovery/sdkconfig.defaults +++ b/examples/bluetooth/bt_discovery/sdkconfig.defaults @@ -6,6 +6,4 @@ CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=y CONFIG_BTDM_CTRL_MODE_BTDM= CONFIG_BT_CLASSIC_ENABLED=y CONFIG_BT_A2DP_ENABLE=n -CONFIG_BT_GATTS_ENABLE=n -CONFIG_BT_GATTC_ENABLE=n -CONFIG_BT_BLE_SMP_ENABLE=n +CONFIG_BT_BLE_ENABLED=n diff --git a/examples/bluetooth/bt_spp_acceptor/sdkconfig.defaults b/examples/bluetooth/bt_spp_acceptor/sdkconfig.defaults index 7320428221..ea64364f53 100644 --- a/examples/bluetooth/bt_spp_acceptor/sdkconfig.defaults +++ b/examples/bluetooth/bt_spp_acceptor/sdkconfig.defaults @@ -7,3 +7,4 @@ CONFIG_BTDM_CTRL_MODE_BTDM= CONFIG_BT_CLASSIC_ENABLED=y CONFIG_WIFI_ENABLED=n CONFIG_BT_SPP_ENABLED=y +CONFIG_BT_BLE_ENABLED=n diff --git a/examples/bluetooth/bt_spp_initiator/sdkconfig.defaults b/examples/bluetooth/bt_spp_initiator/sdkconfig.defaults index 7320428221..ea64364f53 100644 --- a/examples/bluetooth/bt_spp_initiator/sdkconfig.defaults +++ b/examples/bluetooth/bt_spp_initiator/sdkconfig.defaults @@ -7,3 +7,4 @@ CONFIG_BTDM_CTRL_MODE_BTDM= CONFIG_BT_CLASSIC_ENABLED=y CONFIG_WIFI_ENABLED=n CONFIG_BT_SPP_ENABLED=y +CONFIG_BT_BLE_ENABLED=n diff --git a/examples/bluetooth/bt_spp_vfs_acceptor/sdkconfig.defaults b/examples/bluetooth/bt_spp_vfs_acceptor/sdkconfig.defaults index 7320428221..ea64364f53 100644 --- a/examples/bluetooth/bt_spp_vfs_acceptor/sdkconfig.defaults +++ b/examples/bluetooth/bt_spp_vfs_acceptor/sdkconfig.defaults @@ -7,3 +7,4 @@ CONFIG_BTDM_CTRL_MODE_BTDM= CONFIG_BT_CLASSIC_ENABLED=y CONFIG_WIFI_ENABLED=n CONFIG_BT_SPP_ENABLED=y +CONFIG_BT_BLE_ENABLED=n diff --git a/examples/bluetooth/bt_spp_vfs_initiator/sdkconfig.defaults b/examples/bluetooth/bt_spp_vfs_initiator/sdkconfig.defaults index 7320428221..ea64364f53 100644 --- a/examples/bluetooth/bt_spp_vfs_initiator/sdkconfig.defaults +++ b/examples/bluetooth/bt_spp_vfs_initiator/sdkconfig.defaults @@ -7,3 +7,4 @@ CONFIG_BTDM_CTRL_MODE_BTDM= CONFIG_BT_CLASSIC_ENABLED=y CONFIG_WIFI_ENABLED=n CONFIG_BT_SPP_ENABLED=y +CONFIG_BT_BLE_ENABLED=n From 9863565a6f3d38b3a0857b7135b652d7106dd7c3 Mon Sep 17 00:00:00 2001 From: baohongde Date: Wed, 13 Feb 2019 10:38:26 +0800 Subject: [PATCH 097/486] components/bt: Optimization and bugfix of previous commit --- components/bt/bluedroid/bta/gatt/bta_gattc_co.c | 9 +++++++-- components/bt/bluedroid/btc/core/btc_config.c | 3 +-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/components/bt/bluedroid/bta/gatt/bta_gattc_co.c b/components/bt/bluedroid/bta/gatt/bta_gattc_co.c index 98444458ae..06c51b07e8 100644 --- a/components/bt/bluedroid/bta/gatt/bta_gattc_co.c +++ b/components/bt/bluedroid/bta/gatt/bta_gattc_co.c @@ -96,7 +96,7 @@ typedef struct { cache_addr_info_t cache_addr[MAX_DEVICE_IN_CACHE]; }cache_env_t; -cache_env_t *cache_env = NULL; +static cache_env_t *cache_env = NULL; static void getFilename(char *buffer, hash_key_t hash) { @@ -382,10 +382,15 @@ void bta_gattc_co_cache_addr_init(void) UINT8 num_addr; size_t length = MAX_ADDR_LIST_CACHE_BUF; UINT8 *p_buf = osi_malloc(MAX_ADDR_LIST_CACHE_BUF); + if (p_buf == NULL) { + APPL_TRACE_ERROR("%s malloc failed!", __func__); + return; + } cache_env = (cache_env_t *)osi_malloc(sizeof(cache_env_t)); - if (cache_env == NULL || p_buf == NULL) { + if (cache_env == NULL) { APPL_TRACE_ERROR("%s malloc failed!", __func__); + osi_free(p_buf); return; } diff --git a/components/bt/bluedroid/btc/core/btc_config.c b/components/bt/bluedroid/btc/core/btc_config.c index b987a32e0c..fdababfe08 100644 --- a/components/bt/bluedroid/btc/core/btc_config.c +++ b/components/bt/bluedroid/btc/core/btc_config.c @@ -336,5 +336,4 @@ void btc_config_lock(void) void btc_config_unlock(void) { osi_mutex_unlock(&lock); -} - +} \ No newline at end of file From 2ad65bb434542dfab6a15658a82c177ef5de78f5 Mon Sep 17 00:00:00 2001 From: Tian Hao Date: Thu, 18 Apr 2019 11:57:28 +0800 Subject: [PATCH 098/486] Support timeout of fixed_queue and osi_thread 1. remove fixed_queue_try_dequeue and fixed_queue_try_enqueue, 2. add timeout parameter for fixed_queue_enqueue and fixed_queue_dequeue 3. replace where call fixed_queue_try_dequeue/enqueue to fixed_queue_dequeue/enqueue(..., timeout = 0) 4. replace where call fixed queue_enqueue/dequeue to fixed_queue_enqueue/dequeue( ..., timeout = FIXED_QUEUE_MAX_TIMEOUT) 5. modify the blocking_type of osi_thread_post to timeout. --- .../bt/bluedroid/bta/sys/bta_sys_main.c | 4 +- components/bt/bluedroid/btc/core/btc_task.c | 6 +-- .../btc/profile/std/a2dp/btc_a2dp_sink.c | 10 ++-- .../btc/profile/std/a2dp/btc_a2dp_source.c | 12 ++--- components/bt/bluedroid/hci/hci_hal_h4.c | 12 ++--- components/bt/bluedroid/hci/hci_layer.c | 24 +++++----- .../bt/bluedroid/hci/include/hci/hci_layer.h | 2 +- components/bt/bluedroid/osi/fixed_queue.c | 45 ++---------------- .../bluedroid/osi/include/osi/fixed_queue.h | 28 ++++------- .../bt/bluedroid/osi/include/osi/mutex.h | 4 +- .../bt/bluedroid/osi/include/osi/thread.h | 47 ++++++++++++++++--- components/bt/bluedroid/osi/thread.c | 14 ++---- .../bt/bluedroid/stack/avct/avct_lcb_act.c | 4 +- .../bt/bluedroid/stack/avdt/avdt_ccb_act.c | 8 ++-- components/bt/bluedroid/stack/avdt/avdt_msg.c | 8 ++-- .../bt/bluedroid/stack/avdt/avdt_scb_act.c | 12 ++--- components/bt/bluedroid/stack/btm/btm_acl.c | 8 ++-- .../bt/bluedroid/stack/btm/btm_ble_bgconn.c | 4 +- components/bt/bluedroid/stack/btm/btm_sco.c | 6 +-- components/bt/bluedroid/stack/btm/btm_sec.c | 6 +-- components/bt/bluedroid/stack/btu/btu_hcif.c | 4 +- components/bt/bluedroid/stack/btu/btu_init.c | 2 +- components/bt/bluedroid/stack/btu/btu_task.c | 10 ++-- components/bt/bluedroid/stack/gap/gap_ble.c | 6 +-- components/bt/bluedroid/stack/gap/gap_conn.c | 20 ++++---- .../bt/bluedroid/stack/gatt/gatt_auth.c | 6 +-- components/bt/bluedroid/stack/gatt/gatt_db.c | 2 +- components/bt/bluedroid/stack/gatt/gatt_sr.c | 8 ++-- .../bt/bluedroid/stack/gatt/gatt_utils.c | 18 +++---- .../bt/bluedroid/stack/include/stack/btu.h | 2 +- components/bt/bluedroid/stack/l2cap/l2c_api.c | 2 +- components/bt/bluedroid/stack/l2cap/l2c_ble.c | 6 +-- components/bt/bluedroid/stack/l2cap/l2c_csm.c | 2 +- components/bt/bluedroid/stack/l2cap/l2c_fcr.c | 20 ++++---- components/bt/bluedroid/stack/l2cap/l2c_ucd.c | 24 +++++----- .../bt/bluedroid/stack/l2cap/l2c_utils.c | 8 ++-- .../bt/bluedroid/stack/rfcomm/port_api.c | 10 ++-- .../bt/bluedroid/stack/rfcomm/port_rfc.c | 4 +- .../bt/bluedroid/stack/rfcomm/port_utils.c | 4 +- .../bt/bluedroid/stack/rfcomm/rfc_mx_fsm.c | 2 +- .../bt/bluedroid/stack/rfcomm/rfc_utils.c | 4 +- 41 files changed, 207 insertions(+), 221 deletions(-) diff --git a/components/bt/bluedroid/bta/sys/bta_sys_main.c b/components/bt/bluedroid/bta/sys/bta_sys_main.c index c9a7f450f4..c00abd7394 100644 --- a/components/bt/bluedroid/bta/sys/bta_sys_main.c +++ b/components/bt/bluedroid/bta/sys/bta_sys_main.c @@ -572,7 +572,7 @@ void bta_sys_sendmsg(void *p_msg) // there is a procedure in progress that can schedule a task via this // message queue. This causes |btu_bta_msg_queue| to get cleaned up before // it gets used here; hence we check for NULL before using it. - if (btu_task_post(SIG_BTU_BTA_MSG, p_msg, OSI_THREAD_BLOCKING) == false) { + if (btu_task_post(SIG_BTU_BTA_MSG, p_msg, OSI_THREAD_MAX_TIMEOUT) == false) { osi_free(p_msg); } } @@ -592,7 +592,7 @@ void bta_alarm_cb(void *data) assert(data != NULL); TIMER_LIST_ENT *p_tle = (TIMER_LIST_ENT *)data; - btu_task_post(SIG_BTU_BTA_ALARM, p_tle, OSI_THREAD_BLOCKING); + btu_task_post(SIG_BTU_BTA_ALARM, p_tle, OSI_THREAD_MAX_TIMEOUT); } void bta_sys_start_timer(TIMER_LIST_ENT *p_tle, UINT16 type, INT32 timeout_ms) diff --git a/components/bt/bluedroid/btc/core/btc_task.c b/components/bt/bluedroid/btc/core/btc_task.c index 911f1941b6..cb8616d9b0 100644 --- a/components/bt/bluedroid/btc/core/btc_task.c +++ b/components/bt/bluedroid/btc/core/btc_task.c @@ -128,7 +128,7 @@ static void btc_thread_handler(void *arg) osi_free(msg); } -static bt_status_t btc_task_post(btc_msg_t *msg, osi_thread_blocking_t blocking) +static bt_status_t btc_task_post(btc_msg_t *msg, uint32_t timeout) { btc_msg_t *lmsg; @@ -139,7 +139,7 @@ static bt_status_t btc_task_post(btc_msg_t *msg, osi_thread_blocking_t blocking) memcpy(lmsg, msg, sizeof(btc_msg_t)); - if (osi_thread_post(btc_thread, btc_thread_handler, lmsg, 2, blocking) == false) { + if (osi_thread_post(btc_thread, btc_thread_handler, lmsg, 2, timeout) == false) { return BT_STATUS_BUSY; } @@ -171,7 +171,7 @@ bt_status_t btc_transfer_context(btc_msg_t *msg, void *arg, int arg_len, btc_arg lmsg.arg = NULL; } - return btc_task_post(&lmsg, OSI_THREAD_BLOCKING); + return btc_task_post(&lmsg, OSI_THREAD_MAX_TIMEOUT); } diff --git a/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_sink.c b/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_sink.c index f1d51d80e3..1a65fbbdcf 100644 --- a/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_sink.c +++ b/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_sink.c @@ -182,7 +182,7 @@ static bool btc_a2dp_sink_ctrl_post(uint32_t sig, void *param) evt->sig = sig; evt->param = param; - return osi_thread_post(a2dp_sink_local_param.btc_aa_snk_task_hdl, btc_a2dp_sink_ctrl_handler, evt, 0, OSI_THREAD_BLOCKING); + return osi_thread_post(a2dp_sink_local_param.btc_aa_snk_task_hdl, btc_a2dp_sink_ctrl_handler, evt, 0, OSI_THREAD_MAX_TIMEOUT); } static void btc_a2dp_sink_ctrl_handler(void *arg) @@ -322,7 +322,7 @@ void btc_a2dp_sink_on_suspended(tBTA_AV_SUSPEND *p_av) static void btc_a2dp_sink_data_post(void) { - osi_thread_post(a2dp_sink_local_param.btc_aa_snk_task_hdl, btc_a2dp_sink_data_ready, NULL, 1, OSI_THREAD_BLOCKING); + osi_thread_post(a2dp_sink_local_param.btc_aa_snk_task_hdl, btc_a2dp_sink_data_ready, NULL, 1, OSI_THREAD_MAX_TIMEOUT); } /******************************************************************************* @@ -390,7 +390,7 @@ static void btc_a2dp_sink_data_ready(UNUSED_ATTR void *context) return; } btc_a2dp_sink_handle_inc_media(p_msg); - p_msg = (tBT_SBC_HDR *)fixed_queue_try_dequeue(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ); + p_msg = (tBT_SBC_HDR *)fixed_queue_dequeue(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ, 0); if ( p_msg == NULL ) { APPL_TRACE_ERROR("Insufficient data in que "); break; @@ -695,7 +695,7 @@ UINT8 btc_a2dp_sink_enque_buf(BT_HDR *p_pkt) memcpy(p_msg, p_pkt, (sizeof(BT_HDR) + p_pkt->offset + p_pkt->len)); p_msg->num_frames_to_be_processed = (*((UINT8 *)(p_msg + 1) + p_msg->offset)) & 0x0f; APPL_TRACE_VERBOSE("btc_a2dp_sink_enque_buf %d + \n", p_msg->num_frames_to_be_processed); - fixed_queue_enqueue(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ, p_msg); + fixed_queue_enqueue(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ, p_msg, FIXED_QUEUE_MAX_TIMEOUT); btc_a2dp_sink_data_post(); } else { /* let caller deal with a failed allocation */ @@ -721,7 +721,7 @@ static void btc_a2dp_sink_handle_clear_track (void) static void btc_a2dp_sink_flush_q(fixed_queue_t *p_q) { while (! fixed_queue_is_empty(p_q)) { - osi_free(fixed_queue_try_dequeue(p_q)); + osi_free(fixed_queue_dequeue(p_q, 0)); } } diff --git a/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_source.c b/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_source.c index 3591901002..7f1ef366be 100644 --- a/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_source.c +++ b/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_source.c @@ -249,7 +249,7 @@ static bool btc_a2dp_source_ctrl_post(uint32_t sig, void *param) evt->sig = sig; evt->param = param; - return osi_thread_post(a2dp_source_local_param.btc_aa_src_task_hdl, btc_a2dp_source_ctrl_handler, evt, 0, OSI_THREAD_BLOCKING); + return osi_thread_post(a2dp_source_local_param.btc_aa_src_task_hdl, btc_a2dp_source_ctrl_handler, evt, 0, OSI_THREAD_MAX_TIMEOUT); } static void btc_a2dp_source_ctrl_handler(void *arg) @@ -421,7 +421,7 @@ void btc_a2dp_source_on_suspended(tBTA_AV_SUSPEND *p_av) static void btc_a2dp_source_data_post(void) { - osi_thread_post(a2dp_source_local_param.btc_aa_src_task_hdl, btc_a2dp_source_handle_timer, NULL, 1, OSI_THREAD_BLOCKING); + osi_thread_post(a2dp_source_local_param.btc_aa_src_task_hdl, btc_a2dp_source_handle_timer, NULL, 1, OSI_THREAD_MAX_TIMEOUT); } static UINT64 time_now_us() @@ -510,7 +510,7 @@ BT_HDR *btc_a2dp_source_audio_readbuf(void) if (btc_a2dp_source_state != BTC_A2DP_SOURCE_STATE_ON){ return NULL; } - return fixed_queue_try_dequeue(a2dp_source_local_param.btc_aa_src_cb.TxAaQ); + return fixed_queue_dequeue(a2dp_source_local_param.btc_aa_src_cb.TxAaQ, 0); } /******************************************************************************* @@ -1378,7 +1378,7 @@ static void btc_media_aa_prep_sbc_2_send(UINT8 nb_frame) } /* Enqueue the encoded SBC frame in AA Tx Queue */ - fixed_queue_enqueue(a2dp_source_local_param.btc_aa_src_cb.TxAaQ, p_buf); + fixed_queue_enqueue(a2dp_source_local_param.btc_aa_src_cb.TxAaQ, p_buf, FIXED_QUEUE_MAX_TIMEOUT); } else { osi_free(p_buf); } @@ -1407,7 +1407,7 @@ static void btc_a2dp_source_prep_2_send(UINT8 nb_frame) } while (fixed_queue_length(a2dp_source_local_param.btc_aa_src_cb.TxAaQ) > (MAX_OUTPUT_A2DP_SRC_FRAME_QUEUE_SZ - nb_frame)) { - osi_free(fixed_queue_try_dequeue(a2dp_source_local_param.btc_aa_src_cb.TxAaQ)); + osi_free(fixed_queue_dequeue(a2dp_source_local_param.btc_aa_src_cb.TxAaQ, 0)); } // Transcode frame @@ -1589,7 +1589,7 @@ static void btc_a2dp_source_aa_stop_tx(void) static void btc_a2dp_source_flush_q(fixed_queue_t *p_q) { while (! fixed_queue_is_empty(p_q)) { - osi_free(fixed_queue_try_dequeue(p_q)); + osi_free(fixed_queue_dequeue(p_q, 0)); } } diff --git a/components/bt/bluedroid/hci/hci_hal_h4.c b/components/bt/bluedroid/hci/hci_hal_h4.c index 5985e32ae2..67bb83438f 100644 --- a/components/bt/bluedroid/hci/hci_hal_h4.c +++ b/components/bt/bluedroid/hci/hci_hal_h4.c @@ -170,9 +170,9 @@ static void hci_hal_h4_rx_handler(void *arg) fixed_queue_process(hci_hal_env.rx_q); } -bool hci_hal_h4_task_post(osi_thread_blocking_t blocking) +bool hci_hal_h4_task_post(uint32_t timeout) { - return osi_thread_post(hci_h4_thread, hci_hal_h4_rx_handler, NULL, 1, blocking); + return osi_thread_post(hci_h4_thread, hci_hal_h4_rx_handler, NULL, 1, timeout); } #if (C2H_FLOW_CONTROL_INCLUDED == TRUE) @@ -314,7 +314,7 @@ static void event_uart_has_bytes(fixed_queue_t *queue) { BT_HDR *packet; while (!fixed_queue_is_empty(queue)) { - packet = fixed_queue_dequeue(queue); + packet = fixed_queue_dequeue(queue, FIXED_QUEUE_MAX_TIMEOUT); hci_hal_h4_hdl_rx_packet(packet); } } @@ -323,7 +323,7 @@ static void host_send_pkt_available_cb(void) { //Controller rx cache buffer is ready for receiving new host packet //Just Call Host main thread task to process pending packets. - hci_host_task_post(OSI_THREAD_BLOCKING); + hci_host_task_post(OSI_THREAD_MAX_TIMEOUT); } static int host_recv_pkt_cb(uint8_t *data, uint16_t len) @@ -347,8 +347,8 @@ static int host_recv_pkt_cb(uint8_t *data, uint16_t len) pkt->len = len; pkt->layer_specific = 0; memcpy(pkt->data, data, len); - fixed_queue_enqueue(hci_hal_env.rx_q, pkt); - hci_hal_h4_task_post(OSI_THREAD_NON_BLOCKING); + fixed_queue_enqueue(hci_hal_env.rx_q, pkt, FIXED_QUEUE_MAX_TIMEOUT); + hci_hal_h4_task_post(0); BTTRC_DUMP_BUFFER("Recv Pkt", pkt->data, len); diff --git a/components/bt/bluedroid/hci/hci_layer.c b/components/bt/bluedroid/hci/hci_layer.c index e426528d45..6c285339cc 100644 --- a/components/bt/bluedroid/hci/hci_layer.c +++ b/components/bt/bluedroid/hci/hci_layer.c @@ -135,9 +135,9 @@ void hci_shut_down(void) } -bool hci_host_task_post(osi_thread_blocking_t blocking) +bool hci_host_task_post(uint32_t timeout) { - return osi_thread_post(hci_host_thread, hci_host_thread_handler, NULL, 0, blocking); + return osi_thread_post(hci_host_thread, hci_host_thread_handler, NULL, 0, timeout); } static int hci_layer_init_env(void) @@ -252,8 +252,8 @@ static void transmit_command( HCI_TRACE_DEBUG("HCI Enqueue Comamnd opcode=0x%x\n", wait_entry->opcode); BTTRC_DUMP_BUFFER(NULL, command->data + command->offset, command->len); - fixed_queue_enqueue(hci_host_env.command_queue, wait_entry); - hci_host_task_post(OSI_THREAD_BLOCKING); + fixed_queue_enqueue(hci_host_env.command_queue, wait_entry, FIXED_QUEUE_MAX_TIMEOUT); + hci_host_task_post(OSI_THREAD_MAX_TIMEOUT); } @@ -273,8 +273,8 @@ static future_t *transmit_command_futured(BT_HDR *command) // in case the upper layer didn't already command->event = MSG_STACK_TO_HC_HCI_CMD; - fixed_queue_enqueue(hci_host_env.command_queue, wait_entry); - hci_host_task_post(OSI_THREAD_BLOCKING); + fixed_queue_enqueue(hci_host_env.command_queue, wait_entry, FIXED_QUEUE_MAX_TIMEOUT); + hci_host_task_post(OSI_THREAD_MAX_TIMEOUT); return future; } @@ -284,10 +284,10 @@ static void transmit_downward(uint16_t type, void *data) transmit_command((BT_HDR *)data, NULL, NULL, NULL); HCI_TRACE_WARNING("%s legacy transmit of command. Use transmit_command instead.\n", __func__); } else { - fixed_queue_enqueue(hci_host_env.packet_queue, data); + fixed_queue_enqueue(hci_host_env.packet_queue, data, FIXED_QUEUE_MAX_TIMEOUT); } - hci_host_task_post(OSI_THREAD_BLOCKING); + hci_host_task_post(OSI_THREAD_MAX_TIMEOUT); } @@ -297,7 +297,7 @@ static void event_command_ready(fixed_queue_t *queue) waiting_command_t *wait_entry = NULL; command_waiting_response_t *cmd_wait_q = &hci_host_env.cmd_waiting_q; - wait_entry = fixed_queue_dequeue(queue); + wait_entry = fixed_queue_dequeue(queue, FIXED_QUEUE_MAX_TIMEOUT); if(wait_entry->opcode == HCI_HOST_NUM_PACKETS_DONE #if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE) @@ -323,7 +323,7 @@ static void event_command_ready(fixed_queue_t *queue) static void event_packet_ready(fixed_queue_t *queue) { - BT_HDR *packet = (BT_HDR *)fixed_queue_dequeue(queue); + BT_HDR *packet = (BT_HDR *)fixed_queue_dequeue(queue, FIXED_QUEUE_MAX_TIMEOUT); // The queue may be the command queue or the packet queue, we don't care packet_fragmenter->fragment_and_dispatch(packet); @@ -461,7 +461,7 @@ intercepted: /*Tell HCI Host Task to continue TX Pending commands*/ if (hci_host_env.command_credits && !fixed_queue_is_empty(hci_host_env.command_queue)) { - hci_host_task_post(OSI_THREAD_BLOCKING); + hci_host_task_post(OSI_THREAD_MAX_TIMEOUT); } if (wait_entry) { @@ -489,7 +489,7 @@ static void dispatch_reassembled(BT_HDR *packet) { // Events should already have been dispatched before this point //Tell Up-layer received packet. - if (btu_task_post(SIG_BTU_HCI_MSG, packet, OSI_THREAD_BLOCKING) == false) { + if (btu_task_post(SIG_BTU_HCI_MSG, packet, OSI_THREAD_MAX_TIMEOUT) == false) { osi_free(packet); } } diff --git a/components/bt/bluedroid/hci/include/hci/hci_layer.h b/components/bt/bluedroid/hci/include/hci/hci_layer.h index 90f0a8221e..8fa5165e9f 100644 --- a/components/bt/bluedroid/hci/include/hci/hci_layer.h +++ b/components/bt/bluedroid/hci/include/hci/hci_layer.h @@ -97,6 +97,6 @@ const hci_t *hci_layer_get_interface(); int hci_start_up(void); void hci_shut_down(void); -bool hci_host_task_post(osi_thread_blocking_t blocking); +bool hci_host_task_post(uint32_t timeout); #endif /* _HCI_LAYER_H_ */ diff --git a/components/bt/bluedroid/osi/fixed_queue.c b/components/bt/bluedroid/osi/fixed_queue.c index 30290060aa..0b21dd8be1 100644 --- a/components/bt/bluedroid/osi/fixed_queue.c +++ b/components/bt/bluedroid/osi/fixed_queue.c @@ -129,45 +129,12 @@ size_t fixed_queue_capacity(fixed_queue_t *queue) return queue->capacity; } -void fixed_queue_enqueue(fixed_queue_t *queue, void *data) +bool fixed_queue_enqueue(fixed_queue_t *queue, void *data, uint32_t timeout) { assert(queue != NULL); assert(data != NULL); - osi_sem_take(&queue->enqueue_sem, OSI_SEM_MAX_TIMEOUT); - - osi_mutex_lock(&queue->lock, OSI_MUTEX_MAX_TIMEOUT); - - list_append(queue->list, data); - osi_mutex_unlock(&queue->lock); - - osi_sem_give(&queue->dequeue_sem); -} - -void *fixed_queue_dequeue(fixed_queue_t *queue) -{ - void *ret = NULL; - - assert(queue != NULL); - - osi_sem_take(&queue->dequeue_sem, OSI_SEM_MAX_TIMEOUT); - - osi_mutex_lock(&queue->lock, OSI_MUTEX_MAX_TIMEOUT); - ret = list_front(queue->list); - list_remove(queue->list, ret); - osi_mutex_unlock(&queue->lock); - - osi_sem_give(&queue->enqueue_sem); - - return ret; -} - -bool fixed_queue_try_enqueue(fixed_queue_t *queue, void *data) -{ - assert(queue != NULL); - assert(data != NULL); - - if (osi_sem_take(&queue->enqueue_sem, 0) != 0) { + if (osi_sem_take(&queue->enqueue_sem, timeout) != 0) { return false; } @@ -181,15 +148,13 @@ bool fixed_queue_try_enqueue(fixed_queue_t *queue, void *data) return true; } -void *fixed_queue_try_dequeue(fixed_queue_t *queue) +void *fixed_queue_dequeue(fixed_queue_t *queue, uint32_t timeout) { void *ret = NULL; - if (queue == NULL) { - return NULL; - } + assert(queue != NULL); - if (osi_sem_take(queue->dequeue_sem, 0) != 0) { + if (osi_sem_take(queue->dequeue_sem, timeout) != 0) { return NULL; } diff --git a/components/bt/bluedroid/osi/include/osi/fixed_queue.h b/components/bt/bluedroid/osi/include/osi/fixed_queue.h index 5ec0c07498..a25e60393c 100644 --- a/components/bt/bluedroid/osi/include/osi/fixed_queue.h +++ b/components/bt/bluedroid/osi/include/osi/fixed_queue.h @@ -21,11 +21,14 @@ #include #include "osi/list.h" +#include "osi/semaphore.h" #ifndef QUEUE_SIZE_MAX #define QUEUE_SIZE_MAX 254 #endif +#define FIXED_QUEUE_MAX_TIMEOUT OSI_SEM_MAX_TIMEOUT + struct fixed_queue_t; typedef struct fixed_queue_t fixed_queue_t; @@ -56,27 +59,14 @@ size_t fixed_queue_length(fixed_queue_t *queue); // not be NULL. size_t fixed_queue_capacity(fixed_queue_t *queue); -// Enqueues the given |data| into the |queue|. The caller will be blocked -// if nore more space is available in the queue. Neither |queue| nor |data| -// may be NULL. -void fixed_queue_enqueue(fixed_queue_t *queue, void *data); +// Enqueues the given |data| into the |queue|. The caller will be blocked or immediately return or wait for timeout according to the parameter timeout. +// If enqueue failed, it will return false, otherwise return true +bool fixed_queue_enqueue(fixed_queue_t *queue, void *data, uint32_t timeout); // Dequeues the next element from |queue|. If the queue is currently empty, -// this function will block the caller until an item is enqueued. This -// function will never return NULL. |queue| may not be NULL. -void *fixed_queue_dequeue(fixed_queue_t *queue); - -// Tries to enqueue |data| into the |queue|. This function will never block -// the caller. If the queue capacity would be exceeded by adding one more -// element, this function returns false immediately. Otherwise, this function -// returns true. Neither |queue| nor |data| may be NULL. -bool fixed_queue_try_enqueue(fixed_queue_t *queue, void *data); - -// Tries to dequeue an element from |queue|. This function will never block -// the caller. If the queue is empty, this function returns NULL immediately. -// Otherwise, the next element in the queue is returned. |queue| may not be -// NULL. -void *fixed_queue_try_dequeue(fixed_queue_t *queue); +// this function will block the caller until an item is enqueued or immediately return or wait for timeout according to the parameter timeout. +// If dequeue failed, it will return NULL, otherwise return a point. +void *fixed_queue_dequeue(fixed_queue_t *queue, uint32_t timeout); // Returns the first element from |queue|, if present, without dequeuing it. // This function will never block the caller. Returns NULL if there are no diff --git a/components/bt/bluedroid/osi/include/osi/mutex.h b/components/bt/bluedroid/osi/include/osi/mutex.h index 65180a7850..1b9784d62b 100644 --- a/components/bt/bluedroid/osi/include/osi/mutex.h +++ b/components/bt/bluedroid/osi/include/osi/mutex.h @@ -23,9 +23,9 @@ #include "freertos/task.h" #include "freertos/queue.h" #include "freertos/semphr.h" +#include "osi/semaphore.h" - -#define OSI_MUTEX_MAX_TIMEOUT 0xffffffffUL +#define OSI_MUTEX_MAX_TIMEOUT OSI_SEM_MAX_TIMEOUT #define osi_mutex_valid( x ) ( ( ( *x ) == NULL) ? pdFALSE : pdTRUE ) #define osi_mutex_set_invalid( x ) ( ( *x ) = NULL ) diff --git a/components/bt/bluedroid/osi/include/osi/thread.h b/components/bt/bluedroid/osi/include/osi/thread.h index b3f77725f9..7f4c46aedf 100644 --- a/components/bt/bluedroid/osi/include/osi/thread.h +++ b/components/bt/bluedroid/osi/include/osi/thread.h @@ -22,9 +22,12 @@ #include "freertos/task.h" #include "esp_task.h" #include "common/bt_defs.h" +#include "osi/semaphore.h" #define portBASE_TYPE int +#define OSI_THREAD_MAX_TIMEOUT OSI_SEM_MAX_TIMEOUT + struct osi_thread; typedef struct osi_thread osi_thread_t; @@ -37,21 +40,53 @@ typedef enum { OSI_THREAD_CORE_AFFINITY, } osi_thread_core_t; -typedef enum { - OSI_THREAD_NON_BLOCKING = 0, - OSI_THREAD_BLOCKING, -} osi_thread_blocking_t; - +/* + * brief: Create a thread or task + * param name: thread name + * param stack_size: thread stack size + * param priority: thread priority + * param core: the CPU core which this thread run, OSI_THREAD_CORE_AFFINITY means unspecific CPU core + * param work_queue_num: speicify queue number, the queue[0] has highest priority, and the priority is decrease by index + * return : if create successfully, return thread handler; otherwise return NULL. + */ osi_thread_t *osi_thread_create(const char *name, size_t stack_size, int priority, osi_thread_core_t core, uint8_t work_queue_num); +/* + * brief: Destroy a thread or task + * param thread: point of thread handler + */ void osi_thread_free(osi_thread_t *thread); -bool osi_thread_post(osi_thread_t *thread, osi_thread_func_t func, void *context, int queue_idx, osi_thread_blocking_t blocking); +/* + * brief: Post an msg to a thread and told the thread call the function + * param thread: point of thread handler + * param func: callback function that called by target thread + * param context: argument of callback function + * param queue_idx: the queue which the msg send to + * param timeout: post timeout, OSI_THREAD_MAX_TIMEOUT means blocking forever, 0 means never blocking, others means block millisecond + * return : if post successfully, return true, otherwise return false + */ +bool osi_thread_post(osi_thread_t *thread, osi_thread_func_t func, void *context, int queue_idx, uint32_t timeout); +/* + * brief: Set the priority of thread + * param thread: point of thread handler + * param priority: priority + * return : if set successfully, return true, otherwise return false + */ bool osi_thread_set_priority(osi_thread_t *thread, int priority); +/* brief: Get thread name + * param thread: point of thread handler + * return: constant point of thread name + */ const char *osi_thread_name(osi_thread_t *thread); +/* brief: Get the size of the specified queue + * param thread: point of thread handler + * param wq_idx: the queue index of the thread + * return: queue size + */ int osi_thread_queue_wait_size(osi_thread_t *thread, int wq_idx); #endif /* __THREAD_H__ */ diff --git a/components/bt/bluedroid/osi/thread.c b/components/bt/bluedroid/osi/thread.c index ceac21bf89..04583061c3 100644 --- a/components/bt/bluedroid/osi/thread.c +++ b/components/bt/bluedroid/osi/thread.c @@ -63,7 +63,7 @@ static void osi_thread_run(void *arg) } while (!thread->stop && idx < thread->work_queue_num) { - work_item_t *item = fixed_queue_try_dequeue(thread->work_queues[idx]); + work_item_t *item = fixed_queue_dequeue(thread->work_queues[idx], 0); if (item) { item->func(item->context); osi_free(item); @@ -227,7 +227,7 @@ void osi_thread_free(osi_thread_t *thread) osi_free(thread); } -bool osi_thread_post(osi_thread_t *thread, osi_thread_func_t func, void *context, int queue_idx, osi_thread_blocking_t blocking) +bool osi_thread_post(osi_thread_t *thread, osi_thread_func_t func, void *context, int queue_idx, uint32_t timeout) { assert(thread != NULL); assert(func != NULL); @@ -243,13 +243,9 @@ bool osi_thread_post(osi_thread_t *thread, osi_thread_func_t func, void *context item->func = func; item->context = context; - if (blocking == OSI_THREAD_BLOCKING) { - fixed_queue_enqueue(thread->work_queues[queue_idx], item); - } else { - if (fixed_queue_try_enqueue(thread->work_queues[queue_idx], item) == false) { - osi_free(item); - return false; - } + if (fixed_queue_enqueue(thread->work_queues[queue_idx], item, timeout) == false) { + osi_free(item); + return false; } osi_sem_give(&thread->work_sem); diff --git a/components/bt/bluedroid/stack/avct/avct_lcb_act.c b/components/bt/bluedroid/stack/avct/avct_lcb_act.c index 02ac96039b..b83219dceb 100644 --- a/components/bt/bluedroid/stack/avct/avct_lcb_act.c +++ b/components/bt/bluedroid/stack/avct/avct_lcb_act.c @@ -449,7 +449,7 @@ void avct_lcb_cong_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) if (p_lcb->cong == FALSE && !fixed_queue_is_empty(p_lcb->tx_q)) { while (!p_lcb->cong && - (p_buf = (BT_HDR *)fixed_queue_try_dequeue(p_lcb->tx_q)) != NULL) + (p_buf = (BT_HDR *)fixed_queue_dequeue(p_lcb->tx_q, 0)) != NULL) { if (L2CA_DataWrite(p_lcb->ch_lcid, p_buf) == L2CAP_DW_CONGESTED) { @@ -569,7 +569,7 @@ void avct_lcb_send_msg(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) } if (p_lcb->cong == TRUE) { - fixed_queue_enqueue(p_lcb->tx_q, p_buf); + fixed_queue_enqueue(p_lcb->tx_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT); } /* send message to L2CAP */ diff --git a/components/bt/bluedroid/stack/avdt/avdt_ccb_act.c b/components/bt/bluedroid/stack/avdt/avdt_ccb_act.c index 9f7b0ee9d0..7d7e7c6e77 100644 --- a/components/bt/bluedroid/stack/avdt/avdt_ccb_act.c +++ b/components/bt/bluedroid/stack/avdt/avdt_ccb_act.c @@ -68,7 +68,7 @@ static void avdt_ccb_clear_ccb(tAVDT_CCB *p_ccb) } /* clear out response queue */ - while ((p_buf = (BT_HDR *) fixed_queue_try_dequeue(p_ccb->rsp_q)) != NULL) { + while ((p_buf = (BT_HDR *) fixed_queue_dequeue(p_ccb->rsp_q, 0)) != NULL) { osi_free(p_buf); } } @@ -659,7 +659,7 @@ void avdt_ccb_clear_cmds(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) avdt_ccb_cmd_fail(p_ccb, (tAVDT_CCB_EVT *) &err_code); /* set up next message */ - p_ccb->p_curr_cmd = (BT_HDR *) fixed_queue_try_dequeue(p_ccb->cmd_q); + p_ccb->p_curr_cmd = (BT_HDR *) fixed_queue_dequeue(p_ccb->cmd_q, 0); } while (p_ccb->p_curr_cmd != NULL); @@ -812,7 +812,7 @@ void avdt_ccb_snd_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) ** not congested, not sending fragment, not waiting for response */ if ((!p_ccb->cong) && (p_ccb->p_curr_msg == NULL) && (p_ccb->p_curr_cmd == NULL)) { - if ((p_msg = (BT_HDR *) fixed_queue_try_dequeue(p_ccb->cmd_q)) != NULL) { + if ((p_msg = (BT_HDR *) fixed_queue_dequeue(p_ccb->cmd_q, 0)) != NULL) { /* make a copy of buffer in p_curr_cmd */ if ((p_ccb->p_curr_cmd = (BT_HDR *) osi_malloc(AVDT_CMD_BUF_SIZE)) != NULL) { memcpy(p_ccb->p_curr_cmd, p_msg, (sizeof(BT_HDR) + p_msg->offset + p_msg->len)); @@ -846,7 +846,7 @@ void avdt_ccb_snd_msg(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) } /* do we have responses to send? send them */ else if (!fixed_queue_is_empty(p_ccb->rsp_q)) { - while ((p_msg = (BT_HDR *)fixed_queue_try_dequeue(p_ccb->rsp_q)) != NULL) { + while ((p_msg = (BT_HDR *)fixed_queue_dequeue(p_ccb->rsp_q, 0)) != NULL) { if (avdt_msg_send(p_ccb, p_msg) == TRUE) { /* break out if congested */ break; diff --git a/components/bt/bluedroid/stack/avdt/avdt_msg.c b/components/bt/bluedroid/stack/avdt/avdt_msg.c index 91e993233d..4ebca9a063 100644 --- a/components/bt/bluedroid/stack/avdt/avdt_msg.c +++ b/components/bt/bluedroid/stack/avdt/avdt_msg.c @@ -1440,7 +1440,7 @@ void avdt_msg_send_cmd(tAVDT_CCB *p_ccb, void *p_scb, UINT8 sig_id, tAVDT_MSG *p p_ccb->label = (p_ccb->label + 1) % 16; /* queue message and trigger ccb to send it */ - fixed_queue_enqueue(p_ccb->cmd_q, p_buf); + fixed_queue_enqueue(p_ccb->cmd_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT); avdt_ccb_event(p_ccb, AVDT_CCB_SENDMSG_EVT, NULL); } @@ -1487,7 +1487,7 @@ void avdt_msg_send_rsp(tAVDT_CCB *p_ccb, UINT8 sig_id, tAVDT_MSG *p_params) AVDT_BLD_LAYERSPEC(p_buf->layer_specific, AVDT_MSG_TYPE_RSP, p_params->hdr.label); /* queue message and trigger ccb to send it */ - fixed_queue_enqueue(p_ccb->rsp_q, p_buf); + fixed_queue_enqueue(p_ccb->rsp_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT); avdt_ccb_event(p_ccb, AVDT_CCB_SENDMSG_EVT, NULL); } @@ -1547,7 +1547,7 @@ void avdt_msg_send_rej(tAVDT_CCB *p_ccb, UINT8 sig_id, tAVDT_MSG *p_params) AVDT_BLD_LAYERSPEC(p_buf->layer_specific, AVDT_MSG_TYPE_REJ, p_params->hdr.label); /* queue message and trigger ccb to send it */ - fixed_queue_enqueue(p_ccb->rsp_q, p_buf); + fixed_queue_enqueue(p_ccb->rsp_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT); avdt_ccb_event(p_ccb, AVDT_CCB_SENDMSG_EVT, NULL); } @@ -1591,7 +1591,7 @@ void avdt_msg_send_grej(tAVDT_CCB *p_ccb, UINT8 sig_id, tAVDT_MSG *p_params) AVDT_TRACE_DEBUG("avdt_msg_send_grej"); /* queue message and trigger ccb to send it */ - fixed_queue_enqueue(p_ccb->rsp_q, p_buf); + fixed_queue_enqueue(p_ccb->rsp_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT); avdt_ccb_event(p_ccb, AVDT_CCB_SENDMSG_EVT, NULL); } diff --git a/components/bt/bluedroid/stack/avdt/avdt_scb_act.c b/components/bt/bluedroid/stack/avdt/avdt_scb_act.c index b4bbcec066..4cdc5a2e36 100644 --- a/components/bt/bluedroid/stack/avdt/avdt_scb_act.c +++ b/components/bt/bluedroid/stack/avdt/avdt_scb_act.c @@ -1232,7 +1232,7 @@ void avdt_scb_hdl_write_req_frag(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) /* this shouldn't be happening */ AVDT_TRACE_WARNING("*** Dropped media packet; congested"); BT_HDR *p_frag; - while ((p_frag = (BT_HDR*)fixed_queue_try_dequeue(p_scb->frag_q)) != NULL) + while ((p_frag = (BT_HDR*)fixed_queue_dequeue(p_scb->frag_q, 0)) != NULL) osi_free(p_frag); } @@ -1397,7 +1397,7 @@ void avdt_scb_snd_stream_close(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) /* clean fragments queue */ BT_HDR *p_frag; - while ((p_frag = (BT_HDR*)fixed_queue_try_dequeue(p_scb->frag_q)) != NULL) { + while ((p_frag = (BT_HDR*)fixed_queue_dequeue(p_scb->frag_q, 0)) != NULL) { osi_free(p_frag); } p_scb->frag_off = 0; @@ -1824,7 +1824,7 @@ void avdt_scb_free_pkt(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) #if AVDT_MULTIPLEXING == TRUE /* clean fragments queue */ BT_HDR *p_frag; - while ((p_frag = (BT_HDR*)fixed_queue_try_dequeue(p_scb->frag_q)) != NULL) { + while ((p_frag = (BT_HDR*)fixed_queue_dequeue(p_scb->frag_q, 0)) != NULL) { osi_free(p_frag); } #endif @@ -1880,7 +1880,7 @@ void avdt_scb_clr_pkt(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) AVDT_TRACE_DEBUG("Dropped fragments queue"); /* clean fragments queue */ BT_HDR *p_frag; - while ((p_frag = (BT_HDR*)fixed_queue_try_dequeue(p_scb->frag_q)) != NULL) { + while ((p_frag = (BT_HDR*)fixed_queue_dequeue(p_scb->frag_q, 0)) != NULL) { osi_free(p_frag); } p_scb->frag_off = 0; @@ -1933,7 +1933,7 @@ void avdt_scb_chk_snd_pkt(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) L2CA_FlushChannel(avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_scb->p_ccb)][avdt_ad_type_to_tcid(AVDT_CHAN_MEDIA, p_scb)].lcid), L2CAP_FLUSH_CHANS_GET); #endif - while ((p_pkt = (BT_HDR*)fixed_queue_try_dequeue(p_scb->frag_q)) != NULL) { + while ((p_pkt = (BT_HDR*)fixed_queue_dequeue(p_scb->frag_q, 0)) != NULL) { sent = TRUE; AVDT_TRACE_DEBUG("Send fragment len=%d\n", p_pkt->len); /* fragments queue contains fragment to send */ @@ -2096,7 +2096,7 @@ void avdt_scb_queue_frags(tAVDT_SCB *p_scb, UINT8 **pp_data, UINT32 *p_data_len, UINT16_TO_BE_STREAM(p, p_frag->layer_specific ); } /* put fragment into gueue */ - fixed_queue_enqueue(p_scb->frag_q, p_frag); + fixed_queue_enqueue(p_scb->frag_q, p_frag, FIXED_QUEUE_MAX_TIMEOUT); num_frag--; } } diff --git a/components/bt/bluedroid/stack/btm/btm_acl.c b/components/bt/bluedroid/stack/btm/btm_acl.c index 6f7ce092a6..d15fc6c7d6 100644 --- a/components/bt/bluedroid/stack/btm/btm_acl.c +++ b/components/bt/bluedroid/stack/btm/btm_acl.c @@ -2364,7 +2364,7 @@ void btm_acl_resubmit_page (void) BD_ADDR bda; BTM_TRACE_DEBUG ("btm_acl_resubmit_page\n"); /* If there were other page request schedule can start the next one */ - if ((p_buf = (BT_HDR *)fixed_queue_try_dequeue(btm_cb.page_queue)) != NULL) { + if ((p_buf = (BT_HDR *)fixed_queue_dequeue(btm_cb.page_queue, 0)) != NULL) { /* skip 3 (2 bytes opcode and 1 byte len) to get to the bd_addr * for both create_conn and rmt_name */ pp = (UINT8 *)(p_buf + 1) + p_buf->offset + 3; @@ -2395,7 +2395,7 @@ void btm_acl_reset_paging (void) BT_HDR *p; BTM_TRACE_DEBUG ("btm_acl_reset_paging\n"); /* If we sent reset we are definitely not paging any more */ - while ((p = (BT_HDR *)fixed_queue_try_dequeue(btm_cb.page_queue)) != NULL) { + while ((p = (BT_HDR *)fixed_queue_dequeue(btm_cb.page_queue, 0)) != NULL) { osi_free (p); } @@ -2419,7 +2419,7 @@ void btm_acl_paging (BT_HDR *p, BD_ADDR bda) (bda[0] << 16) + (bda[1] << 8) + bda[2], (bda[3] << 16) + (bda[4] << 8) + bda[5]); if (btm_cb.discing) { btm_cb.paging = TRUE; - fixed_queue_enqueue(btm_cb.page_queue, p); + fixed_queue_enqueue(btm_cb.page_queue, p, FIXED_QUEUE_MAX_TIMEOUT); } else { if (!BTM_ACL_IS_CONNECTED (bda)) { BTM_TRACE_DEBUG ("connecting_bda: %06x%06x\n", @@ -2429,7 +2429,7 @@ void btm_acl_paging (BT_HDR *p, BD_ADDR bda) btm_cb.connecting_bda[5]); if (btm_cb.paging && memcmp (bda, btm_cb.connecting_bda, BD_ADDR_LEN) != 0) { - fixed_queue_enqueue(btm_cb.page_queue, p); + fixed_queue_enqueue(btm_cb.page_queue, p, FIXED_QUEUE_MAX_TIMEOUT); } else { p_dev_rec = btm_find_or_alloc_dev (bda); memcpy (btm_cb.connecting_bda, p_dev_rec->bd_addr, BD_ADDR_LEN); diff --git a/components/bt/bluedroid/stack/btm/btm_ble_bgconn.c b/components/bt/bluedroid/stack/btm/btm_ble_bgconn.c index 0766715480..3721471f80 100644 --- a/components/bt/bluedroid/stack/btm/btm_ble_bgconn.c +++ b/components/bt/bluedroid/stack/btm/btm_ble_bgconn.c @@ -785,7 +785,7 @@ void btm_ble_enqueue_direct_conn_req(void *p_param) p->p_param = p_param; - fixed_queue_enqueue(btm_cb.ble_ctr_cb.conn_pending_q, p); + fixed_queue_enqueue(btm_cb.ble_ctr_cb.conn_pending_q, p, FIXED_QUEUE_MAX_TIMEOUT); } /******************************************************************************* ** @@ -801,7 +801,7 @@ BOOLEAN btm_send_pending_direct_conn(void) tBTM_BLE_CONN_REQ *p_req; BOOLEAN rt = FALSE; - p_req = (tBTM_BLE_CONN_REQ*)fixed_queue_try_dequeue(btm_cb.ble_ctr_cb.conn_pending_q); + p_req = (tBTM_BLE_CONN_REQ*)fixed_queue_dequeue(btm_cb.ble_ctr_cb.conn_pending_q, 0); if (p_req != NULL) { rt = l2cble_init_direct_conn((tL2C_LCB *)(p_req->p_param)); diff --git a/components/bt/bluedroid/stack/btm/btm_sco.c b/components/bt/bluedroid/stack/btm/btm_sco.c index 661caba638..25d8c4183c 100644 --- a/components/bt/bluedroid/stack/btm/btm_sco.c +++ b/components/bt/bluedroid/stack/btm/btm_sco.c @@ -86,7 +86,7 @@ void btm_sco_flush_sco_data(UINT16 sco_inx) if (sco_inx < BTM_MAX_SCO_LINKS) { p = &btm_cb.sco_cb.sco_db[sco_inx]; - while ((p_buf = (BT_HDR *)fixed_queue_try_dequeue(p->xmit_data_q)) != NULL) { + while ((p_buf = (BT_HDR *)fixed_queue_dequeue(p->xmit_data_q, 0)) != NULL) { osi_free(p_buf); } } @@ -292,7 +292,7 @@ void btm_sco_check_send_pkts (UINT16 sco_inx) BT_HDR *p_buf; while (p_cb->xmit_window_size != 0) { - if ((p_buf = (BT_HDR *)fixed_queue_try_dequeue(p_ccb->xmit_data_q)) == NULL) { + if ((p_buf = (BT_HDR *)fixed_queue_dequeue(p_ccb->xmit_data_q, 0)) == NULL) { break; } #if BTM_SCO_HCI_DEBUG @@ -441,7 +441,7 @@ tBTM_STATUS BTM_WriteScoData (UINT16 sco_inx, BT_HDR *p_buf) p_buf->len += HCI_SCO_PREAMBLE_SIZE; if (fixed_queue_length(p_ccb->xmit_data_q) < BTM_SCO_XMIT_QUEUE_THRS) { - fixed_queue_enqueue(p_ccb->xmit_data_q, p_buf); + fixed_queue_enqueue(p_ccb->xmit_data_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT); btm_sco_check_send_pkts (sco_inx); } else { BTM_TRACE_WARNING ("SCO xmit Q overflow, pkt dropped"); diff --git a/components/bt/bluedroid/stack/btm/btm_sec.c b/components/bt/bluedroid/stack/btm/btm_sec.c index 1d7b3276c6..fc711d9180 100644 --- a/components/bt/bluedroid/stack/btm/btm_sec.c +++ b/components/bt/bluedroid/stack/btm/btm_sec.c @@ -2791,7 +2791,7 @@ void btm_sec_check_pending_reqs (void) btm_cb.sec_pending_q = fixed_queue_new(QUEUE_SIZE_MAX); - while ((p_e = (tBTM_SEC_QUEUE_ENTRY *)fixed_queue_try_dequeue(bq)) != NULL) { + while ((p_e = (tBTM_SEC_QUEUE_ENTRY *)fixed_queue_dequeue(bq, 0)) != NULL) { /* Check that the ACL is still up before starting security procedures */ if (btm_bda_to_acl(p_e->bd_addr, p_e->transport) != NULL) { if (p_e->psm != 0) { @@ -5784,7 +5784,7 @@ static BOOLEAN btm_sec_queue_mx_request (BD_ADDR bd_addr, UINT16 psm, BOOLEAN BTM_TRACE_EVENT ("%s() PSM: 0x%04x Is_Orig: %u mx_proto_id: %u mx_chan_id: %u\n", __func__, psm, is_orig, mx_proto_id, mx_chan_id); - fixed_queue_enqueue(btm_cb.sec_pending_q, p_e); + fixed_queue_enqueue(btm_cb.sec_pending_q, p_e, FIXED_QUEUE_MAX_TIMEOUT); return (TRUE); } @@ -5883,7 +5883,7 @@ static BOOLEAN btm_sec_queue_encrypt_request (BD_ADDR bd_addr, tBT_TRANSPORT tra *(UINT8 *)p_e->p_ref_data = *(UINT8 *)(p_ref_data); p_e->transport = transport; memcpy(p_e->bd_addr, bd_addr, BD_ADDR_LEN); - fixed_queue_enqueue(btm_cb.sec_pending_q, p_e); + fixed_queue_enqueue(btm_cb.sec_pending_q, p_e, FIXED_QUEUE_MAX_TIMEOUT); return TRUE; } diff --git a/components/bt/bluedroid/stack/btu/btu_hcif.c b/components/bt/bluedroid/stack/btu/btu_hcif.c index 5677491c5c..de7693ee84 100644 --- a/components/bt/bluedroid/stack/btu/btu_hcif.c +++ b/components/bt/bluedroid/stack/btu/btu_hcif.c @@ -1086,7 +1086,7 @@ static void btu_hcif_command_complete_evt(BT_HDR *response, void *context) event->event = BTU_POST_TO_TASK_NO_GOOD_HORRIBLE_HACK; - btu_task_post(SIG_BTU_HCI_MSG, event, OSI_THREAD_BLOCKING); + btu_task_post(SIG_BTU_HCI_MSG, event, OSI_THREAD_MAX_TIMEOUT); } @@ -1291,7 +1291,7 @@ static void btu_hcif_command_status_evt(uint8_t status, BT_HDR *command, void *c event->event = BTU_POST_TO_TASK_NO_GOOD_HORRIBLE_HACK; - btu_task_post(SIG_BTU_HCI_MSG, event, OSI_THREAD_BLOCKING); + btu_task_post(SIG_BTU_HCI_MSG, event, OSI_THREAD_MAX_TIMEOUT); } /******************************************************************************* diff --git a/components/bt/bluedroid/stack/btu/btu_init.c b/components/bt/bluedroid/stack/btu/btu_init.c index bf3a55af8c..455cc9fe6a 100644 --- a/components/bt/bluedroid/stack/btu/btu_init.c +++ b/components/bt/bluedroid/stack/btu/btu_init.c @@ -186,7 +186,7 @@ void BTU_StartUp(void) goto error_exit; } - if (btu_task_post(SIG_BTU_START_UP, NULL, OSI_THREAD_BLOCKING) == false) { + if (btu_task_post(SIG_BTU_START_UP, NULL, OSI_THREAD_MAX_TIMEOUT) == false) { goto error_exit; } diff --git a/components/bt/bluedroid/stack/btu/btu_task.c b/components/bt/bluedroid/stack/btu/btu_task.c index 916811df1e..40448357d5 100644 --- a/components/bt/bluedroid/stack/btu/btu_task.c +++ b/components/bt/bluedroid/stack/btu/btu_task.c @@ -246,7 +246,7 @@ void btu_thread_handler(void *arg) osi_free(evt); } -bool btu_task_post(uint32_t sig, void *param, osi_thread_blocking_t blocking) +bool btu_task_post(uint32_t sig, void *param, uint32_t timeout) { btu_thread_evt_t *evt; @@ -258,7 +258,7 @@ bool btu_task_post(uint32_t sig, void *param, osi_thread_blocking_t blocking) evt->sig = sig; evt->param = param; - return osi_thread_post(btu_thread, btu_thread_handler, evt, 0, blocking); + return osi_thread_post(btu_thread, btu_thread_handler, evt, 0, timeout); } void btu_task_start_up(void) @@ -417,7 +417,7 @@ void btu_general_alarm_cb(void *data) assert(data != NULL); TIMER_LIST_ENT *p_tle = (TIMER_LIST_ENT *)data; - btu_task_post(SIG_BTU_GENERAL_ALARM, p_tle, OSI_THREAD_BLOCKING); + btu_task_post(SIG_BTU_GENERAL_ALARM, p_tle, OSI_THREAD_MAX_TIMEOUT); } void btu_start_timer(TIMER_LIST_ENT *p_tle, UINT16 type, UINT32 timeout_sec) @@ -531,7 +531,7 @@ static void btu_l2cap_alarm_cb(void *data) assert(data != NULL); TIMER_LIST_ENT *p_tle = (TIMER_LIST_ENT *)data; - btu_task_post(SIG_BTU_L2CAP_ALARM, p_tle, OSI_THREAD_BLOCKING); + btu_task_post(SIG_BTU_L2CAP_ALARM, p_tle, OSI_THREAD_MAX_TIMEOUT); } void btu_start_quick_timer(TIMER_LIST_ENT *p_tle, UINT16 type, UINT32 timeout_ticks) @@ -614,7 +614,7 @@ void btu_oneshot_alarm_cb(void *data) btu_stop_timer_oneshot(p_tle); - btu_task_post(SIG_BTU_ONESHOT_ALARM, p_tle, OSI_THREAD_BLOCKING); + btu_task_post(SIG_BTU_ONESHOT_ALARM, p_tle, OSI_THREAD_MAX_TIMEOUT); } /* diff --git a/components/bt/bluedroid/stack/gap/gap_ble.c b/components/bt/bluedroid/stack/gap/gap_ble.c index 08f7c195ce..5aab51fc1b 100644 --- a/components/bt/bluedroid/stack/gap/gap_ble.c +++ b/components/bt/bluedroid/stack/gap/gap_ble.c @@ -145,7 +145,7 @@ void gap_ble_dealloc_clcb(tGAP_CLCB *p_clcb) { tGAP_BLE_REQ *p_q; - while ((p_q = (tGAP_BLE_REQ *)fixed_queue_try_dequeue(p_clcb->pending_req_q)) != NULL) { + while ((p_q = (tGAP_BLE_REQ *)fixed_queue_dequeue(p_clcb->pending_req_q, 0)) != NULL) { /* send callback to all pending requests if being removed*/ if (p_q->p_cback != NULL) { (*p_q->p_cback)(FALSE, p_clcb->bda, 0, NULL); @@ -173,7 +173,7 @@ BOOLEAN gap_ble_enqueue_request (tGAP_CLCB *p_clcb, UINT16 uuid, tGAP_BLE_CMPL_C if (p_q != NULL) { p_q->p_cback = p_cback; p_q->uuid = uuid; - fixed_queue_enqueue(p_clcb->pending_req_q, p_q); + fixed_queue_enqueue(p_clcb->pending_req_q, p_q, FIXED_QUEUE_MAX_TIMEOUT); return TRUE; } @@ -190,7 +190,7 @@ BOOLEAN gap_ble_enqueue_request (tGAP_CLCB *p_clcb, UINT16 uuid, tGAP_BLE_CMPL_C *******************************************************************************/ BOOLEAN gap_ble_dequeue_request (tGAP_CLCB *p_clcb, UINT16 *p_uuid, tGAP_BLE_CMPL_CBACK **p_cback) { - tGAP_BLE_REQ *p_q = (tGAP_BLE_REQ *)fixed_queue_try_dequeue(p_clcb->pending_req_q);; + tGAP_BLE_REQ *p_q = (tGAP_BLE_REQ *)fixed_queue_dequeue(p_clcb->pending_req_q, 0);; if (p_q != NULL) { *p_cback = p_q->p_cback; diff --git a/components/bt/bluedroid/stack/gap/gap_conn.c b/components/bt/bluedroid/stack/gap/gap_conn.c index 671ffa7427..db9065de81 100644 --- a/components/bt/bluedroid/stack/gap/gap_conn.c +++ b/components/bt/bluedroid/stack/gap/gap_conn.c @@ -332,7 +332,7 @@ UINT16 GAP_ConnReadData (UINT16 gap_handle, UINT8 *p_data, UINT16 max_len, UINT1 p_buf->len -= copy_len; break; } - osi_free(fixed_queue_try_dequeue(p_ccb->rx_queue)); + osi_free(fixed_queue_dequeue(p_ccb->rx_queue, 0)); } p_ccb->rx_queue_size -= *p_len; @@ -404,7 +404,7 @@ UINT16 GAP_ConnBTRead (UINT16 gap_handle, BT_HDR **pp_buf) return (GAP_ERR_BAD_HANDLE); } - p_buf = (BT_HDR *)fixed_queue_try_dequeue(p_ccb->rx_queue); + p_buf = (BT_HDR *)fixed_queue_dequeue(p_ccb->rx_queue, 0); if (p_buf) { *pp_buf = p_buf; @@ -451,7 +451,7 @@ UINT16 GAP_ConnBTWrite (UINT16 gap_handle, BT_HDR *p_buf) return (GAP_ERR_BUF_OFFSET); } - fixed_queue_enqueue(p_ccb->tx_queue, p_buf); + fixed_queue_enqueue(p_ccb->tx_queue, p_buf, FIXED_QUEUE_MAX_TIMEOUT); if (p_ccb->is_congested) { return (BT_PASS); @@ -461,7 +461,7 @@ UINT16 GAP_ConnBTWrite (UINT16 gap_handle, BT_HDR *p_buf) #if (GAP_CONN_POST_EVT_INCLUDED == TRUE) gap_send_event (gap_handle); #else - while ((p_buf = (BT_HDR *)fixed_queue_try_dequeue(p_ccb->tx_queue)) != NULL) { + while ((p_buf = (BT_HDR *)fixed_queue_dequeue(p_ccb->tx_queue, 0)) != NULL) { UINT8 status = L2CA_DATA_WRITE (p_ccb->connection_id, p_buf); if (status == L2CAP_DW_CONGESTED) { @@ -532,7 +532,7 @@ UINT16 GAP_ConnWriteData (UINT16 gap_handle, UINT8 *p_data, UINT16 max_len, UINT GAP_TRACE_EVENT ("GAP_WriteData %d bytes", p_buf->len); - fixed_queue_enqueue(p_ccb->tx_queue, p_buf); + fixed_queue_enqueue(p_ccb->tx_queue, p_buf, FIXED_QUEUE_MAX_TIMEOUT); } if (p_ccb->is_congested) { @@ -543,7 +543,7 @@ UINT16 GAP_ConnWriteData (UINT16 gap_handle, UINT8 *p_data, UINT16 max_len, UINT #if (GAP_CONN_POST_EVT_INCLUDED == TRUE) gap_send_event (gap_handle); #else - while ((p_buf = (BT_HDR *)fixed_queue_try_dequeue(p_ccb->tx_queue)) != NULL) + while ((p_buf = (BT_HDR *)fixed_queue_dequeue(p_ccb->tx_queue, 0)) != NULL) { UINT8 status = L2CA_DATA_WRITE (p_ccb->connection_id, p_buf); @@ -989,7 +989,7 @@ static void gap_data_ind (UINT16 l2cap_cid, BT_HDR *p_msg) } if (p_ccb->con_state == GAP_CCB_STATE_CONNECTED) { - fixed_queue_enqueue(p_ccb->rx_queue, p_msg); + fixed_queue_enqueue(p_ccb->rx_queue, p_msg, FIXED_QUEUE_MAX_TIMEOUT); p_ccb->rx_queue_size += p_msg->len; /* @@ -1033,7 +1033,7 @@ static void gap_congestion_ind (UINT16 lcid, BOOLEAN is_congested) p_ccb->p_callback (p_ccb->gap_handle, event); if (!is_congested) { - while ((p_buf = (BT_HDR *)fixed_queue_try_dequeue(p_ccb->tx_queue)) != NULL) { + while ((p_buf = (BT_HDR *)fixed_queue_dequeue(p_ccb->tx_queue, 0)) != NULL) { status = L2CA_DATA_WRITE (p_ccb->connection_id, p_buf); if (status == L2CAP_DW_CONGESTED) { @@ -1154,13 +1154,13 @@ static void gap_release_ccb (tGAP_CCB *p_ccb) p_ccb->rx_queue_size = 0; while (!fixed_queue_is_empty(p_ccb->rx_queue)) { - osi_free(fixed_queue_try_dequeue(p_ccb->rx_queue)); + osi_free(fixed_queue_dequeue(p_ccb->rx_queue, 0)); } fixed_queue_free(p_ccb->rx_queue, NULL); p_ccb->rx_queue = NULL; while (!fixed_queue_is_empty(p_ccb->tx_queue)) { - osi_free(fixed_queue_try_dequeue(p_ccb->tx_queue)); + osi_free(fixed_queue_dequeue(p_ccb->tx_queue, 0)); } fixed_queue_free(p_ccb->tx_queue, NULL); p_ccb->tx_queue = NULL; diff --git a/components/bt/bluedroid/stack/gatt/gatt_auth.c b/components/bt/bluedroid/stack/gatt/gatt_auth.c index e233ea0cad..b72cb6faee 100644 --- a/components/bt/bluedroid/stack/gatt/gatt_auth.c +++ b/components/bt/bluedroid/stack/gatt/gatt_auth.c @@ -176,7 +176,7 @@ void gatt_enc_cmpl_cback(BD_ADDR bd_addr, tBT_TRANSPORT transport, void *p_ref_d return; } tGATT_PENDING_ENC_CLCB *p_buf = - (tGATT_PENDING_ENC_CLCB *)fixed_queue_try_dequeue(p_tcb->pending_enc_clcb); + (tGATT_PENDING_ENC_CLCB *)fixed_queue_dequeue(p_tcb->pending_enc_clcb, 0); if (p_buf != NULL) { if (result == BTM_SUCCESS) { if (gatt_get_sec_act(p_tcb) == GATT_SEC_ENCRYPT_MITM ) { @@ -194,7 +194,7 @@ void gatt_enc_cmpl_cback(BD_ADDR bd_addr, tBT_TRANSPORT transport, void *p_ref_d /* start all other pending operation in queue */ for (size_t count = fixed_queue_length(p_tcb->pending_enc_clcb); count > 0; count--) { - p_buf = (tGATT_PENDING_ENC_CLCB *)fixed_queue_try_dequeue(p_tcb->pending_enc_clcb); + p_buf = (tGATT_PENDING_ENC_CLCB *)fixed_queue_dequeue(p_tcb->pending_enc_clcb, 0); if (p_buf != NULL) { gatt_security_check_start(p_buf->p_clcb); osi_free(p_buf); @@ -238,7 +238,7 @@ void gatt_notify_enc_cmpl(BD_ADDR bd_addr) size_t count = fixed_queue_length(p_tcb->pending_enc_clcb); for (; count > 0; count--) { tGATT_PENDING_ENC_CLCB *p_buf = - (tGATT_PENDING_ENC_CLCB *)fixed_queue_try_dequeue(p_tcb->pending_enc_clcb); + (tGATT_PENDING_ENC_CLCB *)fixed_queue_dequeue(p_tcb->pending_enc_clcb, 0); if (p_buf != NULL) { gatt_security_check_start(p_buf->p_clcb); osi_free(p_buf); diff --git a/components/bt/bluedroid/stack/gatt/gatt_db.c b/components/bt/bluedroid/stack/gatt/gatt_db.c index 8a05117482..a860e19e09 100644 --- a/components/bt/bluedroid/stack/gatt/gatt_db.c +++ b/components/bt/bluedroid/stack/gatt/gatt_db.c @@ -1383,7 +1383,7 @@ static BOOLEAN allocate_svc_db_buf(tGATT_SVC_DB *p_db) p_db->p_free_mem = (UINT8 *) p_buf; p_db->mem_free = GATT_DB_BUF_SIZE; - fixed_queue_enqueue(p_db->svc_buffer, p_buf); + fixed_queue_enqueue(p_db->svc_buffer, p_buf, FIXED_QUEUE_MAX_TIMEOUT); return TRUE; diff --git a/components/bt/bluedroid/stack/gatt/gatt_sr.c b/components/bt/bluedroid/stack/gatt/gatt_sr.c index eccc9ec979..6bad33b5c8 100644 --- a/components/bt/bluedroid/stack/gatt/gatt_sr.c +++ b/components/bt/bluedroid/stack/gatt/gatt_sr.c @@ -140,7 +140,7 @@ void gatt_dequeue_sr_cmd (tGATT_TCB *p_tcb) if (p_tcb->sr_cmd.multi_rsp_q) { while (!fixed_queue_is_empty(p_tcb->sr_cmd.multi_rsp_q)) { - osi_free(fixed_queue_try_dequeue(p_tcb->sr_cmd.multi_rsp_q)); + osi_free(fixed_queue_dequeue(p_tcb->sr_cmd.multi_rsp_q, 0)); } fixed_queue_free(p_tcb->sr_cmd.multi_rsp_q, NULL); } @@ -178,7 +178,7 @@ static BOOLEAN process_read_multi_rsp (tGATT_SR_CMD *p_cmd, tGATT_STATUS status, } memcpy((void *)p_buf, (const void *)p_msg, sizeof(tGATTS_RSP)); - fixed_queue_enqueue(p_cmd->multi_rsp_q, p_buf); + fixed_queue_enqueue(p_cmd->multi_rsp_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT); p_cmd->status = status; if (status == GATT_SUCCESS) { @@ -418,7 +418,7 @@ void gatt_process_exec_write_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, U //dequeue prepare write data while(fixed_queue_try_peek_first(prepare_record->queue)) { - queue_data = fixed_queue_dequeue(prepare_record->queue); + queue_data = fixed_queue_dequeue(prepare_record->queue, FIXED_QUEUE_MAX_TIMEOUT); if (is_prepare_write_valid){ if((queue_data->p_attr->p_value != NULL) && (queue_data->p_attr->p_value->attr_val.attr_val != NULL)){ if(is_first) { @@ -1291,7 +1291,7 @@ void gatt_attr_process_prepare_write (tGATT_TCB *p_tcb, UINT8 i_rcb, UINT16 hand if (prepare_record->queue == NULL) { prepare_record->queue = fixed_queue_new(QUEUE_SIZE_MAX); } - fixed_queue_enqueue(prepare_record->queue, queue_data); + fixed_queue_enqueue(prepare_record->queue, queue_data, FIXED_QUEUE_MAX_TIMEOUT); } } diff --git a/components/bt/bluedroid/stack/gatt/gatt_utils.c b/components/bt/bluedroid/stack/gatt/gatt_utils.c index 2455f746f3..8f9303f0bb 100644 --- a/components/bt/bluedroid/stack/gatt/gatt_utils.c +++ b/components/bt/bluedroid/stack/gatt/gatt_utils.c @@ -97,7 +97,7 @@ void gatt_free_pending_ind(tGATT_TCB *p_tcb) /* release all queued indications */ while (!fixed_queue_is_empty(p_tcb->pending_ind_q)) { - osi_free(fixed_queue_try_dequeue(p_tcb->pending_ind_q)); + osi_free(fixed_queue_dequeue(p_tcb->pending_ind_q, 0)); } fixed_queue_free(p_tcb->pending_ind_q, NULL); p_tcb->pending_ind_q = NULL; @@ -121,7 +121,7 @@ void gatt_free_pending_enc_queue(tGATT_TCB *p_tcb) /* release all queued indications */ while (!fixed_queue_is_empty(p_tcb->pending_enc_clcb)) { - osi_free(fixed_queue_try_dequeue(p_tcb->pending_enc_clcb)); + osi_free(fixed_queue_dequeue(p_tcb->pending_enc_clcb, 0)); } fixed_queue_free(p_tcb->pending_enc_clcb, NULL); p_tcb->pending_enc_clcb = NULL; @@ -143,7 +143,7 @@ void gatt_free_pending_prepare_write_queue(tGATT_TCB *p_tcb) if (p_tcb->prepare_write_record.queue) { /* release all queued prepare write packets */ while (!fixed_queue_is_empty(p_tcb->prepare_write_record.queue)) { - osi_free(fixed_queue_dequeue(p_tcb->prepare_write_record.queue)); + osi_free(fixed_queue_dequeue(p_tcb->prepare_write_record.queue, FIXED_QUEUE_MAX_TIMEOUT)); } fixed_queue_free(p_tcb->prepare_write_record.queue, NULL); p_tcb->prepare_write_record.queue = NULL; @@ -265,7 +265,7 @@ tGATT_VALUE *gatt_add_pending_ind(tGATT_TCB *p_tcb, tGATT_VALUE *p_ind) if ((p_buf = (tGATT_VALUE *)osi_malloc((UINT16)sizeof(tGATT_VALUE))) != NULL) { GATT_TRACE_DEBUG ("enqueue a pending indication"); memcpy(p_buf, p_ind, sizeof(tGATT_VALUE)); - fixed_queue_enqueue(p_tcb->pending_ind_q, p_buf); + fixed_queue_enqueue(p_tcb->pending_ind_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT); } return p_buf; } @@ -288,7 +288,7 @@ tGATTS_PENDING_NEW_SRV_START *gatt_add_pending_new_srv_start(tGATTS_HNDL_RANGE * if ((p_buf = (tGATTS_PENDING_NEW_SRV_START *)osi_malloc((UINT16)sizeof(tGATTS_PENDING_NEW_SRV_START))) != NULL) { GATT_TRACE_DEBUG ("enqueue a new pending new srv start"); p_buf->p_new_srv_start = p_new_srv_start; - fixed_queue_enqueue(gatt_cb.pending_new_srv_start_q, p_buf); + fixed_queue_enqueue(gatt_cb.pending_new_srv_start_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT); } return p_buf; } @@ -310,7 +310,7 @@ tGATTS_SRV_CHG *gatt_add_srv_chg_clt(tGATTS_SRV_CHG *p_srv_chg) if ((p_buf = (tGATTS_SRV_CHG *)osi_malloc((UINT16)sizeof(tGATTS_SRV_CHG))) != NULL) { GATT_TRACE_DEBUG ("enqueue a srv chg client"); memcpy(p_buf, p_srv_chg, sizeof(tGATTS_SRV_CHG)); - fixed_queue_enqueue(gatt_cb.srv_chg_clt_q, p_buf); + fixed_queue_enqueue(gatt_cb.srv_chg_clt_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT); } return p_buf; @@ -469,7 +469,7 @@ void gatt_free_hdl_buffer(tGATT_HDL_LIST_ELEM *p) if (p) { while (!fixed_queue_is_empty(p->svc_db.svc_buffer)) { - osi_free(fixed_queue_try_dequeue(p->svc_db.svc_buffer)); + osi_free(fixed_queue_dequeue(p->svc_db.svc_buffer, 0)); } fixed_queue_free(p->svc_db.svc_buffer, NULL); memset(p, 0, sizeof(tGATT_HDL_LIST_ELEM)); @@ -495,7 +495,7 @@ void gatt_free_srvc_db_buffer_app_id(tBT_UUID *p_app_id) if (memcmp(p_app_id, &p_elem->asgn_range.app_uuid128, sizeof(tBT_UUID)) == 0) { gatt_free_attr_value_buffer(p_elem); while (!fixed_queue_is_empty(p_elem->svc_db.svc_buffer)) { - osi_free(fixed_queue_try_dequeue(p_elem->svc_db.svc_buffer)); + osi_free(fixed_queue_dequeue(p_elem->svc_db.svc_buffer, 0)); } fixed_queue_free(p_elem->svc_db.svc_buffer, NULL); p_elem->svc_db.svc_buffer = NULL; @@ -2733,7 +2733,7 @@ tGATT_PENDING_ENC_CLCB *gatt_add_pending_enc_channel_clcb(tGATT_TCB *p_tcb, tGAT if ((p_buf = (tGATT_PENDING_ENC_CLCB *)osi_malloc((UINT16)sizeof(tGATT_PENDING_ENC_CLCB))) != NULL) { GATT_TRACE_DEBUG ("enqueue a new pending encryption channel clcb"); p_buf->p_clcb = p_clcb; - fixed_queue_enqueue(p_tcb->pending_enc_clcb, p_buf); + fixed_queue_enqueue(p_tcb->pending_enc_clcb, p_buf, FIXED_QUEUE_MAX_TIMEOUT); } return p_buf; } diff --git a/components/bt/bluedroid/stack/include/stack/btu.h b/components/bt/bluedroid/stack/include/stack/btu.h index a038ded1e1..b3269e2ca6 100644 --- a/components/bt/bluedroid/stack/include/stack/btu.h +++ b/components/bt/bluedroid/stack/include/stack/btu.h @@ -288,7 +288,7 @@ void btu_task_shut_down(void); UINT16 BTU_BleAclPktSize(void); -bool btu_task_post(uint32_t sig, void *param, osi_thread_blocking_t blocking); +bool btu_task_post(uint32_t sig, void *param, uint32_t timeout); /* #ifdef __cplusplus diff --git a/components/bt/bluedroid/stack/l2cap/l2c_api.c b/components/bt/bluedroid/stack/l2cap/l2c_api.c index 9a50d4ba72..4074cef841 100644 --- a/components/bt/bluedroid/stack/l2cap/l2c_api.c +++ b/components/bt/bluedroid/stack/l2cap/l2c_api.c @@ -2221,7 +2221,7 @@ UINT16 L2CA_FlushChannel (UINT16 lcid, UINT16 num_to_flush) /* If needed, flush buffers in the CCB xmit hold queue */ while ( (num_to_flush != 0) && (!fixed_queue_is_empty(p_ccb->xmit_hold_q))) { - BT_HDR *p_buf = (BT_HDR *)fixed_queue_try_dequeue(p_ccb->xmit_hold_q); + BT_HDR *p_buf = (BT_HDR *)fixed_queue_dequeue(p_ccb->xmit_hold_q, 0); if (p_buf) { osi_free (p_buf); } diff --git a/components/bt/bluedroid/stack/l2cap/l2c_ble.c b/components/bt/bluedroid/stack/l2cap/l2c_ble.c index b4e347335f..300acefe8a 100644 --- a/components/bt/bluedroid/stack/l2cap/l2c_ble.c +++ b/components/bt/bluedroid/stack/l2cap/l2c_ble.c @@ -1361,7 +1361,7 @@ void l2cble_sec_comp(BD_ADDR p_bda, tBT_TRANSPORT transport, void *p_ref_data, if (!fixed_queue_is_empty(p_lcb->le_sec_pending_q)) { - p_buf = (tL2CAP_SEC_DATA*) fixed_queue_dequeue(p_lcb->le_sec_pending_q); + p_buf = (tL2CAP_SEC_DATA*) fixed_queue_dequeue(p_lcb->le_sec_pending_q, FIXED_QUEUE_MAX_TIMEOUT); if (!p_buf) { L2CAP_TRACE_WARNING ("%s Security complete for request not initiated from L2CAP", @@ -1406,7 +1406,7 @@ void l2cble_sec_comp(BD_ADDR p_bda, tBT_TRANSPORT transport, void *p_ref_data, while (!fixed_queue_is_empty(p_lcb->le_sec_pending_q)) { - p_buf = (tL2CAP_SEC_DATA*) fixed_queue_dequeue(p_lcb->le_sec_pending_q); + p_buf = (tL2CAP_SEC_DATA*) fixed_queue_dequeue(p_lcb->le_sec_pending_q, FIXED_QUEUE_MAX_TIMEOUT); if (status != BTM_SUCCESS) { (*(p_buf->p_callback))(p_bda, BT_TRANSPORT_LE, p_buf->p_ref_data, status); @@ -1462,7 +1462,7 @@ BOOLEAN l2ble_sec_access_req(BD_ADDR bd_addr, UINT16 psm, BOOLEAN is_originator, p_buf->is_originator = is_originator; p_buf->p_callback = p_callback; p_buf->p_ref_data = p_ref_data; - fixed_queue_enqueue(p_lcb->le_sec_pending_q, p_buf); + fixed_queue_enqueue(p_lcb->le_sec_pending_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT); status = btm_ble_start_sec_check(bd_addr, psm, is_originator, &l2cble_sec_comp, p_ref_data); return status; diff --git a/components/bt/bluedroid/stack/l2cap/l2c_csm.c b/components/bt/bluedroid/stack/l2cap/l2c_csm.c index ddfe53dba3..ff7fbb0ec0 100644 --- a/components/bt/bluedroid/stack/l2cap/l2c_csm.c +++ b/components/bt/bluedroid/stack/l2cap/l2c_csm.c @@ -1243,7 +1243,7 @@ void l2c_enqueue_peer_data (tL2C_CCB *p_ccb, BT_HDR *p_buf) UINT16_TO_STREAM (p, p_ccb->remote_cid); } - fixed_queue_enqueue(p_ccb->xmit_hold_q, p_buf); + fixed_queue_enqueue(p_ccb->xmit_hold_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT); l2cu_check_channel_congestion (p_ccb); diff --git a/components/bt/bluedroid/stack/l2cap/l2c_fcr.c b/components/bt/bluedroid/stack/l2cap/l2c_fcr.c index 3a7085c803..ecf0bd6c68 100644 --- a/components/bt/bluedroid/stack/l2cap/l2c_fcr.c +++ b/components/bt/bluedroid/stack/l2cap/l2c_fcr.c @@ -752,7 +752,7 @@ void l2c_fcr_proc_pdu (tL2C_CCB *p_ccb, BT_HDR *p_buf) fixed_queue_t *temp_q = p_ccb->fcrb.srej_rcv_hold_q; p_ccb->fcrb.srej_rcv_hold_q = fixed_queue_new(QUEUE_SIZE_MAX); - while ((p_buf = (BT_HDR *)fixed_queue_try_dequeue(temp_q)) != NULL) { + while ((p_buf = (BT_HDR *)fixed_queue_dequeue(temp_q, 0)) != NULL) { if (p_ccb->in_use && (p_ccb->chnl_state == CST_OPEN)) { /* Get the control word */ p = ((UINT8 *)(p_buf + 1)) + p_buf->offset - L2CAP_FCR_OVERHEAD; @@ -921,7 +921,7 @@ static BOOLEAN process_reqseq (tL2C_CCB *p_ccb, UINT16 ctrl_word) #endif for (xx = 0; xx < num_bufs_acked; xx++) { - BT_HDR *p_tmp = (BT_HDR *)fixed_queue_try_dequeue(p_fcrb->waiting_for_ack_q); + BT_HDR *p_tmp = (BT_HDR *)fixed_queue_dequeue(p_fcrb->waiting_for_ack_q, 0); ls = p_tmp->layer_specific & L2CAP_FCR_SAR_BITS; if ( (ls == L2CAP_FCR_UNSEG_SDU) || (ls == L2CAP_FCR_END_SDU) ) { @@ -1118,7 +1118,7 @@ static void process_i_frame (tL2C_CCB *p_ccb, BT_HDR *p_buf, UINT16 ctrl_word, B num_lost, tx_seq, p_fcrb->next_seq_expected, p_fcrb->rej_sent); p_buf->layer_specific = tx_seq; - fixed_queue_enqueue(p_fcrb->srej_rcv_hold_q, p_buf); + fixed_queue_enqueue(p_fcrb->srej_rcv_hold_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT); } else { L2CAP_TRACE_WARNING ("process_i_frame() CID: 0x%04x frame dropped in Srej Sent next_srej:%u hold_q.count:%u win_sz:%u", p_ccb->local_cid, next_srej, fixed_queue_length(p_fcrb->srej_rcv_hold_q), p_ccb->our_cfg.fcr.tx_win_sz); @@ -1147,7 +1147,7 @@ static void process_i_frame (tL2C_CCB *p_ccb, BT_HDR *p_buf, UINT16 ctrl_word, B p_ccb->local_cid, tx_seq, fixed_queue_length(p_fcrb->srej_rcv_hold_q)); } p_buf->layer_specific = tx_seq; - fixed_queue_enqueue(p_fcrb->srej_rcv_hold_q, p_buf); + fixed_queue_enqueue(p_fcrb->srej_rcv_hold_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT); p_fcrb->srej_sent = TRUE; l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_SREJ, 0); } @@ -1471,7 +1471,7 @@ static BOOLEAN retransmit_i_frames (tL2C_CCB *p_ccb, UINT8 tx_seq) /* Also flush our retransmission queue */ while (!fixed_queue_is_empty(p_ccb->fcrb.retrans_q)) { - osi_free(fixed_queue_try_dequeue(p_ccb->fcrb.retrans_q)); + osi_free(fixed_queue_dequeue(p_ccb->fcrb.retrans_q, 0)); } if (list_ack != NULL) { @@ -1490,7 +1490,7 @@ static BOOLEAN retransmit_i_frames (tL2C_CCB *p_ccb, UINT8 tx_seq) { p_buf2->layer_specific = p_buf->layer_specific; - fixed_queue_enqueue(p_ccb->fcrb.retrans_q, p_buf2); + fixed_queue_enqueue(p_ccb->fcrb.retrans_q, p_buf2, FIXED_QUEUE_MAX_TIMEOUT); } if ( (tx_seq != L2C_FCR_RETX_ALL_PKTS) || (p_buf2 == NULL) ) { @@ -1534,7 +1534,7 @@ BT_HDR *l2c_fcr_get_next_xmit_sdu_seg (tL2C_CCB *p_ccb, UINT16 max_packet_length /* If there is anything in the retransmit queue, that goes first */ - p_buf = (BT_HDR *)fixed_queue_try_dequeue(p_ccb->fcrb.retrans_q); + p_buf = (BT_HDR *)fixed_queue_dequeue(p_ccb->fcrb.retrans_q, 0); if (p_buf != NULL) { /* Update Rx Seq and FCS if we acked some packets while this one was queued */ prepare_I_frame (p_ccb, p_buf, TRUE); @@ -1586,7 +1586,7 @@ BT_HDR *l2c_fcr_get_next_xmit_sdu_seg (tL2C_CCB *p_ccb, UINT16 max_packet_length return (NULL); } } else { /* Use the original buffer if no segmentation, or the last segment */ - p_xmit = (BT_HDR *)fixed_queue_try_dequeue(p_ccb->xmit_hold_q); + p_xmit = (BT_HDR *)fixed_queue_dequeue(p_ccb->xmit_hold_q, 0); if (p_xmit->event != 0) { last_seg = TRUE; @@ -1647,7 +1647,7 @@ BT_HDR *l2c_fcr_get_next_xmit_sdu_seg (tL2C_CCB *p_ccb, UINT16 max_packet_length } /* Pretend we sent it and it got lost */ - fixed_queue_enqueue(p_ccb->fcrb.waiting_for_ack_q, p_xmit); + fixed_queue_enqueue(p_ccb->fcrb.waiting_for_ack_q, p_xmit, FIXED_QUEUE_MAX_TIMEOUT); return (NULL); } else { #if (L2CAP_ERTM_STATS == TRUE) @@ -1661,7 +1661,7 @@ BT_HDR *l2c_fcr_get_next_xmit_sdu_seg (tL2C_CCB *p_ccb, UINT16 max_packet_length } p_wack->layer_specific = p_xmit->layer_specific; - fixed_queue_enqueue(p_ccb->fcrb.waiting_for_ack_q, p_wack); + fixed_queue_enqueue(p_ccb->fcrb.waiting_for_ack_q, p_wack, FIXED_QUEUE_MAX_TIMEOUT); } #if (L2CAP_ERTM_STATS == TRUE) diff --git a/components/bt/bluedroid/stack/l2cap/l2c_ucd.c b/components/bt/bluedroid/stack/l2cap/l2c_ucd.c index 8618042c5b..2b130c3490 100644 --- a/components/bt/bluedroid/stack/l2cap/l2c_ucd.c +++ b/components/bt/bluedroid/stack/l2cap/l2c_ucd.c @@ -596,13 +596,13 @@ void l2c_ucd_delete_sec_pending_q(tL2C_LCB *p_lcb) { /* clean up any security pending UCD */ while (p_lcb->ucd_out_sec_pending_q.p_first) { - osi_free(fixed_queue_try_dequeue(p_lcb->ucd_out_sec_pending_q)); + osi_free(fixed_queue_dequeue(p_lcb->ucd_out_sec_pending_q, 0)); } fixed_queue_free(p_lcb->ucd_out_sec_pending_q, NULL); p_lcb->ucd_out_sec_pending_q = NULL; while (! fixed_queue_is_empty(p_lcb->ucd_in_sec_pending_q)) { - osi_free(fixed_queue_try_dequeue(p_lcb->ucd_in_sec_pending_q)); + osi_free(fixed_queue_dequeue(p_lcb->ucd_in_sec_pending_q, 0)); } fixed_queue_free(p_lcb->ucd_in_sec_pending_q); p_lcb->ucd_in_sec_pending_q = NULL; @@ -683,7 +683,7 @@ BOOLEAN l2c_ucd_check_pending_info_req(tL2C_CCB *p_ccb) *******************************************************************************/ void l2c_ucd_enqueue_pending_out_sec_q(tL2C_CCB *p_ccb, void *p_data) { - fixed_queue_enqueue(p_ccb->p_lcb->ucd_out_sec_pending_q, p_data); + fixed_queue_enqueue(p_ccb->p_lcb->ucd_out_sec_pending_q, p_data, FIXED_QUEUE_MAX_TIMEOUT); l2cu_check_channel_congestion (p_ccb); } @@ -727,7 +727,7 @@ BOOLEAN l2c_ucd_check_pending_out_sec_q(tL2C_CCB *p_ccb) *******************************************************************************/ void l2c_ucd_send_pending_out_sec_q(tL2C_CCB *p_ccb) { - BT_HDR *p_buf = (BT_HDR*)fixed_queue_try_dequeue(p_ccb->p_lcb->ucd_out_sec_pending_q); + BT_HDR *p_buf = (BT_HDR*)fixed_queue_dequeue(p_ccb->p_lcb->ucd_out_sec_pending_q, 0); if (p_buf != NULL) { l2c_enqueue_peer_data (p_ccb, (BT_HDR *)p_buf); @@ -747,7 +747,7 @@ void l2c_ucd_send_pending_out_sec_q(tL2C_CCB *p_ccb) *******************************************************************************/ void l2c_ucd_discard_pending_out_sec_q(tL2C_CCB *p_ccb) { - BT_HDR *p_buf = (BT_HDR*)fixed_queue_try_dequeue(p_ccb->p_lcb->ucd_out_sec_pending_q); + BT_HDR *p_buf = (BT_HDR*)fixed_queue_dequeue(p_ccb->p_lcb->ucd_out_sec_pending_q, 0); /* we may need to report to application */ @@ -767,7 +767,7 @@ void l2c_ucd_discard_pending_out_sec_q(tL2C_CCB *p_ccb) *******************************************************************************/ BOOLEAN l2c_ucd_check_pending_in_sec_q(tL2C_CCB *p_ccb) { - BT_HDR *p_buf = (BT_HDR*)fixed_queue_try_dequeue(p_ccb->p_lcb->ucd_in_sec_pending_q); + BT_HDR *p_buf = (BT_HDR*)fixed_queue_dequeue(p_ccb->p_lcb->ucd_in_sec_pending_q, 0); if (p_buf != NULL) { UINT16 psm; @@ -795,7 +795,7 @@ BOOLEAN l2c_ucd_check_pending_in_sec_q(tL2C_CCB *p_ccb) *******************************************************************************/ void l2c_ucd_send_pending_in_sec_q(tL2C_CCB *p_ccb) { - BT_HDR *p_buf = (BT_HDR*)fixed_queue_try_dequeue(p_ccb->p_lcb->ucd_in_sec_pending_q) + BT_HDR *p_buf = (BT_HDR*)fixed_queue_dequeue(p_ccb->p_lcb->ucd_in_sec_pending_q, 0) if (p_buf != NULL) { p_ccb->p_rcb->ucd.cb_info.pL2CA_UCD_Data_Cb(p_ccb->p_lcb->remote_bd_addr, (BT_HDR *)p_buf); @@ -814,7 +814,7 @@ void l2c_ucd_send_pending_in_sec_q(tL2C_CCB *p_ccb) *******************************************************************************/ void l2c_ucd_discard_pending_in_sec_q(tL2C_CCB *p_ccb) { - BT_HDR *p_buf = (BT_HDR*)fixed_queue_try_dequeue(p_ccb->p_lcb->ucd_in_sec_pending_q); + BT_HDR *p_buf = (BT_HDR*)fixed_queue_dequeue(p_ccb->p_lcb->ucd_in_sec_pending_q, 0); if (p_buf) { osi_free (p_buf); @@ -898,7 +898,7 @@ BOOLEAN l2c_ucd_process_event(tL2C_CCB *p_ccb, UINT16 event, void *p_data) break; case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */ - fixed_queue_enqueue(p_ccb->p_lcb->ucd_in_sec_pending_q, p_data); + fixed_queue_enqueue(p_ccb->p_lcb->ucd_in_sec_pending_q, p_data, FIXED_QUEUE_MAX_TIMEOUT); break; case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */ @@ -958,7 +958,7 @@ BOOLEAN l2c_ucd_process_event(tL2C_CCB *p_ccb, UINT16 event, void *p_data) break; case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */ - fixed_queue_enqueue(p_ccb->p_lcb->ucd_in_sec_pending_q, p_data); + fixed_queue_enqueue(p_ccb->p_lcb->ucd_in_sec_pending_q, p_data, FIXED_QUEUE_MAX_TIMEOUT); break; case L2CEVT_L2CAP_INFO_RSP: @@ -1006,7 +1006,7 @@ BOOLEAN l2c_ucd_process_event(tL2C_CCB *p_ccb, UINT16 event, void *p_data) break; case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */ - fixed_queue_enqueue(p_ccb->p_lcb->ucd_in_sec_pending_q, p_data); + fixed_queue_enqueue(p_ccb->p_lcb->ucd_in_sec_pending_q, p_data, FIXED_QUEUE_MAX_TIMEOUT); break; case L2CEVT_SEC_RE_SEND_CMD: /* BTM has enough info to proceed */ @@ -1033,7 +1033,7 @@ BOOLEAN l2c_ucd_process_event(tL2C_CCB *p_ccb, UINT16 event, void *p_data) /* stop idle timer of UCD */ btu_stop_timer (&p_ccb->timer_entry); - fixed_queue_enqueue(p_ccb->p_lcb->ucd_in_sec_pending_q, p_data); + fixed_queue_enqueue(p_ccb->p_lcb->ucd_in_sec_pending_q, p_data, FIXED_QUEUE_MAX_TIMEOUT); l2c_ucd_check_pending_in_sec_q (p_ccb); break; diff --git a/components/bt/bluedroid/stack/l2cap/l2c_utils.c b/components/bt/bluedroid/stack/l2cap/l2c_utils.c index 92e1eede37..841e510a9a 100644 --- a/components/bt/bluedroid/stack/l2cap/l2c_utils.c +++ b/components/bt/bluedroid/stack/l2cap/l2c_utils.c @@ -249,7 +249,7 @@ void l2cu_release_lcb (tL2C_LCB *p_lcb) { while (!fixed_queue_is_empty(p_lcb->le_sec_pending_q)) { - tL2CAP_SEC_DATA *p_buf = (tL2CAP_SEC_DATA*) fixed_queue_dequeue(p_lcb->le_sec_pending_q); + tL2CAP_SEC_DATA *p_buf = (tL2CAP_SEC_DATA*) fixed_queue_dequeue(p_lcb->le_sec_pending_q, FIXED_QUEUE_MAX_TIMEOUT); if (p_buf->p_callback) { p_buf->p_callback(p_lcb->remote_bd_addr, p_lcb->transport, p_buf->p_ref_data, BTM_DEV_RESET); } @@ -930,7 +930,7 @@ void l2cu_send_peer_disc_req (tL2C_CCB *p_ccb) layer checks that all buffers are sent before disconnecting. */ if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_BASIC_MODE) { - while ((p_buf2 = (BT_HDR *)fixed_queue_try_dequeue(p_ccb->xmit_hold_q)) != NULL) { + while ((p_buf2 = (BT_HDR *)fixed_queue_dequeue(p_ccb->xmit_hold_q, 0)) != NULL) { l2cu_set_acl_hci_header (p_buf2, p_ccb); l2c_link_check_send_pkts (p_ccb->p_lcb, p_ccb, p_buf2); } @@ -3488,7 +3488,7 @@ BT_HDR *l2cu_get_next_buffer_to_send (tL2C_LCB *p_lcb) } else { if (!fixed_queue_is_empty(p_ccb->xmit_hold_q)) { - p_buf = (BT_HDR *)fixed_queue_try_dequeue(p_ccb->xmit_hold_q); + p_buf = (BT_HDR *)fixed_queue_dequeue(p_ccb->xmit_hold_q, 0); if (NULL == p_buf) { L2CAP_TRACE_ERROR("l2cu_get_buffer_to_send: No data to be sent"); return (NULL); @@ -3525,7 +3525,7 @@ BT_HDR *l2cu_get_next_buffer_to_send (tL2C_LCB *p_lcb) } } else { - p_buf = (BT_HDR *)fixed_queue_try_dequeue(p_ccb->xmit_hold_q); + p_buf = (BT_HDR *)fixed_queue_dequeue(p_ccb->xmit_hold_q, 0); if (NULL == p_buf) { L2CAP_TRACE_ERROR("l2cu_get_buffer_to_send() #2: No data to be sent"); return (NULL); diff --git a/components/bt/bluedroid/stack/rfcomm/port_api.c b/components/bt/bluedroid/stack/rfcomm/port_api.c index 64f09149e6..f6e0ed4d4f 100644 --- a/components/bt/bluedroid/stack/rfcomm/port_api.c +++ b/components/bt/bluedroid/stack/rfcomm/port_api.c @@ -1098,7 +1098,7 @@ int PORT_Purge (UINT16 handle, UINT8 purge_flags) count = fixed_queue_length(p_port->rx.queue); - while ((p_buf = (BT_HDR *)fixed_queue_try_dequeue(p_port->rx.queue)) != NULL) { + while ((p_buf = (BT_HDR *)fixed_queue_dequeue(p_port->rx.queue, 0)) != NULL) { osi_free (p_buf); } @@ -1115,7 +1115,7 @@ int PORT_Purge (UINT16 handle, UINT8 purge_flags) if (purge_flags & PORT_PURGE_TXCLEAR) { osi_mutex_global_lock(); /* to prevent tx.queue_size from being negative */ - while ((p_buf = (BT_HDR *)fixed_queue_try_dequeue(p_port->tx.queue)) != NULL) { + while ((p_buf = (BT_HDR *)fixed_queue_dequeue(p_port->tx.queue, 0)) != NULL) { osi_free (p_buf); } @@ -1218,7 +1218,7 @@ int PORT_ReadData (UINT16 handle, char *p_data, UINT16 max_len, UINT16 *p_len) p_data += p_buf->len; } - osi_free(fixed_queue_try_dequeue(p_port->rx.queue)); + osi_free(fixed_queue_dequeue(p_port->rx.queue, 0)); osi_mutex_global_unlock(); @@ -1274,7 +1274,7 @@ int PORT_Read (UINT16 handle, BT_HDR **pp_buf) osi_mutex_global_lock(); - p_buf = (BT_HDR *)fixed_queue_try_dequeue(p_port->rx.queue); + p_buf = (BT_HDR *)fixed_queue_dequeue(p_port->rx.queue, 0); if (p_buf) { p_port->rx.queue_size -= p_buf->len; @@ -1340,7 +1340,7 @@ static int port_write (tPORT *p_port, BT_HDR *p_buf) p_port->rfc.state, p_port->port_ctrl); - fixed_queue_enqueue(p_port->tx.queue, p_buf); + fixed_queue_enqueue(p_port->tx.queue, p_buf, FIXED_QUEUE_MAX_TIMEOUT); p_port->tx.queue_size += p_buf->len; return (PORT_CMD_PENDING); diff --git a/components/bt/bluedroid/stack/rfcomm/port_rfc.c b/components/bt/bluedroid/stack/rfcomm/port_rfc.c index 46b3e07116..4a47fb46ca 100644 --- a/components/bt/bluedroid/stack/rfcomm/port_rfc.c +++ b/components/bt/bluedroid/stack/rfcomm/port_rfc.c @@ -869,7 +869,7 @@ void PORT_DataInd (tRFC_MCB *p_mcb, UINT8 dlci, BT_HDR *p_buf) osi_mutex_global_lock(); - fixed_queue_enqueue(p_port->rx.queue, p_buf); + fixed_queue_enqueue(p_port->rx.queue, p_buf, FIXED_QUEUE_MAX_TIMEOUT); p_port->rx.queue_size += p_buf->len; osi_mutex_global_unlock(); @@ -976,7 +976,7 @@ UINT32 port_rfc_send_tx_data (tPORT *p_port) /* get data from tx queue and send it */ osi_mutex_global_lock(); - if ((p_buf = (BT_HDR *)fixed_queue_try_dequeue(p_port->tx.queue)) != NULL) { + if ((p_buf = (BT_HDR *)fixed_queue_dequeue(p_port->tx.queue, 0)) != NULL) { p_port->tx.queue_size -= p_buf->len; osi_mutex_global_unlock(); diff --git a/components/bt/bluedroid/stack/rfcomm/port_utils.c b/components/bt/bluedroid/stack/rfcomm/port_utils.c index 0da8b3d76b..0b08c0f1f3 100644 --- a/components/bt/bluedroid/stack/rfcomm/port_utils.c +++ b/components/bt/bluedroid/stack/rfcomm/port_utils.c @@ -210,13 +210,13 @@ void port_release_port (tPORT *p_port) osi_mutex_global_lock(); RFCOMM_TRACE_DEBUG("port_release_port, p_port:%p", p_port); - while ((p_buf = (BT_HDR *)fixed_queue_try_dequeue(p_port->rx.queue)) != NULL) { + while ((p_buf = (BT_HDR *)fixed_queue_dequeue(p_port->rx.queue, 0)) != NULL) { osi_free (p_buf); } p_port->rx.queue_size = 0; - while ((p_buf = (BT_HDR *)fixed_queue_try_dequeue(p_port->tx.queue)) != NULL) { + while ((p_buf = (BT_HDR *)fixed_queue_dequeue(p_port->tx.queue, 0)) != NULL) { osi_free (p_buf); } diff --git a/components/bt/bluedroid/stack/rfcomm/rfc_mx_fsm.c b/components/bt/bluedroid/stack/rfcomm/rfc_mx_fsm.c index 6d1bf3c93a..dd2af3b803 100644 --- a/components/bt/bluedroid/stack/rfcomm/rfc_mx_fsm.c +++ b/components/bt/bluedroid/stack/rfcomm/rfc_mx_fsm.c @@ -488,7 +488,7 @@ void rfc_mx_sm_state_disc_wait_ua (tRFC_MCB *p_mcb, UINT16 event, void *p_data) rfc_save_lcid_mcb (p_mcb, p_mcb->lcid); /* clean up before reuse it */ - while ((p_buf = (BT_HDR *)fixed_queue_try_dequeue(p_mcb->cmd_q)) != NULL) { + while ((p_buf = (BT_HDR *)fixed_queue_dequeue(p_mcb->cmd_q, 0)) != NULL) { osi_free(p_buf); } diff --git a/components/bt/bluedroid/stack/rfcomm/rfc_utils.c b/components/bt/bluedroid/stack/rfcomm/rfc_utils.c index 8b1e043116..0d1fbdcb39 100644 --- a/components/bt/bluedroid/stack/rfcomm/rfc_utils.c +++ b/components/bt/bluedroid/stack/rfcomm/rfc_utils.c @@ -492,12 +492,12 @@ void rfc_check_send_cmd(tRFC_MCB *p_mcb, BT_HDR *p_buf) __func__, p_mcb, p_mcb->lcid, rfc_find_lcid_mcb(p_mcb->lcid)); } - fixed_queue_enqueue(p_mcb->cmd_q, p_buf); + fixed_queue_enqueue(p_mcb->cmd_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT); } /* handle queue if L2CAP not congested */ while (p_mcb->l2cap_congested == FALSE) { - if ((p = (BT_HDR *)fixed_queue_try_dequeue(p_mcb->cmd_q)) == NULL) { + if ((p = (BT_HDR *)fixed_queue_dequeue(p_mcb->cmd_q, 0)) == NULL) { break; } From 8024fcb296309515cd5169f0be8d93460e11c92f Mon Sep 17 00:00:00 2001 From: baohongde Date: Wed, 24 Apr 2019 15:04:24 +0800 Subject: [PATCH 099/486] components/bt: Add more functions for memory debug. --- components/bt/bluedroid/osi/allocator.c | 83 ++++++++++++++++--- .../bt/bluedroid/osi/include/osi/allocator.h | 5 +- 2 files changed, 76 insertions(+), 12 deletions(-) diff --git a/components/bt/bluedroid/osi/allocator.c b/components/bt/bluedroid/osi/allocator.c index 624a11c176..569bb5f021 100644 --- a/components/bt/bluedroid/osi/allocator.c +++ b/components/bt/bluedroid/osi/allocator.c @@ -36,11 +36,17 @@ typedef struct { } osi_mem_dbg_info_t; static uint32_t mem_dbg_count = 0; -static uint32_t mem_dbg_count2 = 0; static osi_mem_dbg_info_t mem_dbg_info[OSI_MEM_DBG_INFO_MAX]; -static uint32_t mem_dbg_total_size = 0; +static uint32_t mem_dbg_current_size = 0; static uint32_t mem_dbg_max_size = 0; +#define OSI_MEM_DBG_MAX_SECTION_NUM 5 +typedef struct { + bool used; + uint32_t max_size; +} osi_mem_dbg_max_size_section_t; +static osi_mem_dbg_max_size_section_t mem_dbg_max_size_section[OSI_MEM_DBG_MAX_SECTION_NUM]; + void osi_mem_dbg_init(void) { int i; @@ -52,9 +58,13 @@ void osi_mem_dbg_init(void) mem_dbg_info[i].line = 0; } mem_dbg_count = 0; - mem_dbg_count2 = 0; - mem_dbg_total_size = 0; + mem_dbg_current_size = 0; mem_dbg_max_size = 0; + + for (i = 0; i < OSI_MEM_DBG_MAX_SECTION_NUM; i++){ + mem_dbg_max_size_section[i].used = false; + mem_dbg_max_size_section[i].max_size = 0; + } } void osi_mem_dbg_record(void *p, int size, const char *func, int line) @@ -81,9 +91,17 @@ void osi_mem_dbg_record(void *p, int size, const char *func, int line) OSI_TRACE_ERROR("%s full %s %d !!\n", __func__, func, line); } - mem_dbg_total_size += size; - if(mem_dbg_max_size < mem_dbg_total_size) { - mem_dbg_max_size = mem_dbg_total_size; + mem_dbg_current_size += size; + if(mem_dbg_max_size < mem_dbg_current_size) { + mem_dbg_max_size = mem_dbg_current_size; + } + + for (i = 0; i < OSI_MEM_DBG_MAX_SECTION_NUM; i++){ + if (mem_dbg_max_size_section[i].used) { + if(mem_dbg_max_size_section[i].max_size < mem_dbg_current_size) { + mem_dbg_max_size_section[i].max_size = mem_dbg_current_size; + } + } } } @@ -98,7 +116,7 @@ void osi_mem_dbg_clean(void *p, const char *func, int line) for (i = 0; i < OSI_MEM_DBG_INFO_MAX; i++) { if (mem_dbg_info[i].p == p) { - mem_dbg_total_size -= mem_dbg_info[i].size; + mem_dbg_current_size -= mem_dbg_info[i].size; mem_dbg_info[i].p = NULL; mem_dbg_info[i].size = 0; mem_dbg_info[i].func = NULL; @@ -123,7 +141,7 @@ void osi_mem_dbg_show(void) } } OSI_TRACE_ERROR("--> count %d\n", mem_dbg_count); - OSI_TRACE_ERROR("--> size %dB\n--> max size %dB\n", mem_dbg_total_size, mem_dbg_max_size); + OSI_TRACE_ERROR("--> size %dB\n--> max size %dB\n", mem_dbg_current_size, mem_dbg_max_size); } uint32_t osi_mem_dbg_get_max_size(void) @@ -131,9 +149,52 @@ uint32_t osi_mem_dbg_get_max_size(void) return mem_dbg_max_size; } -uint32_t osi_mem_dbg_get_total_size(void) +uint32_t osi_mem_dbg_get_current_size(void) { - return mem_dbg_total_size; + return mem_dbg_current_size; +} + +void osi_men_dbg_set_section_start(uint8_t index) +{ + if (index >= OSI_MEM_DBG_MAX_SECTION_NUM) { + OSI_TRACE_ERROR("Then range of index should be between 0 and %d, current index is %d.\n", + OSI_MEM_DBG_MAX_SECTION_NUM - 1, index); + return; + } + + if (mem_dbg_max_size_section[index].used) { + OSI_TRACE_WARNING("This index(%d) has been started, restart it.\n", index); + } + + mem_dbg_max_size_section[index].used = true; + mem_dbg_max_size_section[index].max_size = mem_dbg_current_size; +} + +void osi_men_dbg_set_section_end(uint8_t index) +{ + if (index >= OSI_MEM_DBG_MAX_SECTION_NUM) { + OSI_TRACE_ERROR("Then range of index should be between 0 and %d, current index is %d.\n", + OSI_MEM_DBG_MAX_SECTION_NUM - 1, index); + return; + } + + if (!mem_dbg_max_size_section[index].used) { + OSI_TRACE_ERROR("This index(%d) has not been started.\n", index); + return; + } + + mem_dbg_max_size_section[index].used = false; +} + +uint32_t osi_mem_dbg_get_max_size_section(uint8_t index) +{ + if (index >= OSI_MEM_DBG_MAX_SECTION_NUM){ + OSI_TRACE_ERROR("Then range of index should be between 0 and %d, current index is %d.\n", + OSI_MEM_DBG_MAX_SECTION_NUM - 1, index); + return 0; + } + + return mem_dbg_max_size_section[index].max_size; } #endif diff --git a/components/bt/bluedroid/osi/include/osi/allocator.h b/components/bt/bluedroid/osi/include/osi/allocator.h index f5e87a473a..579f2b2bb6 100644 --- a/components/bt/bluedroid/osi/include/osi/allocator.h +++ b/components/bt/bluedroid/osi/include/osi/allocator.h @@ -36,7 +36,10 @@ void osi_mem_dbg_record(void *p, int size, const char *func, int line); void osi_mem_dbg_clean(void *p, const char *func, int line); void osi_mem_dbg_show(void); uint32_t osi_mem_dbg_get_max_size(void); -uint32_t osi_mem_dbg_get_total_size(void); +uint32_t osi_mem_dbg_get_current_size(void); +void osi_men_dbg_set_section_start(uint8_t index); +void osi_men_dbg_set_section_end(uint8_t index); +uint32_t osi_mem_dbg_get_max_size_section(uint8_t index); #if HEAP_ALLOCATION_FROM_SPIRAM_FIRST #define osi_malloc(size) \ From f2e6ba8701e0b1d3dd59b01c255df1db7458fdaf Mon Sep 17 00:00:00 2001 From: wangmengyang Date: Thu, 20 Jun 2019 16:25:51 +0800 Subject: [PATCH 100/486] component/bt: clear BT/BLE interrupts after controller_disable to overwrite the default non-zero value of intcntl registers --- components/bt/lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/bt/lib b/components/bt/lib index 45df297ff2..471f03c2ca 160000 --- a/components/bt/lib +++ b/components/bt/lib @@ -1 +1 @@ -Subproject commit 45df297ff295c106171d8c3621216e9f48f7d8b4 +Subproject commit 471f03c2ca55225a600d3783dd277a910bfe80ed From 4bd4c7caf3f9ef8402c5a27ab44561537407eb60 Mon Sep 17 00:00:00 2001 From: David Cermak Date: Thu, 20 Jun 2019 14:12:15 +0200 Subject: [PATCH 101/486] mdns: fix ignoring mdns packet with some invalid name entries in question field In case of invalid name entry, only this entry is invalidated and parsing continues as other query entries could contain questions to be responded to --- components/mdns/mdns.c | 9 +++++---- components/mdns/private_include/mdns_private.h | 1 + 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/components/mdns/mdns.c b/components/mdns/mdns.c index e88672f82b..fe4a03ba89 100644 --- a/components/mdns/mdns.c +++ b/components/mdns/mdns.c @@ -174,7 +174,7 @@ static const uint8_t * _mdns_read_fqdn(const uint8_t * packet, const uint8_t * s size_t index = 0; while (start[index]) { if (name->parts == 4) { - return NULL; + name->invalid = true; } uint8_t len = start[index++]; if (len < 0xC0) { @@ -195,7 +195,7 @@ static const uint8_t * _mdns_read_fqdn(const uint8_t * packet, const uint8_t * s strlcat(name->host, buf, sizeof(name->host)); } else if (strcasecmp(buf, MDNS_SUB_STR) == 0) { name->sub = 1; - } else { + } else if (!name->invalid) { char* mdns_name_ptrs[]={name->host, name->service, name->proto, name->domain}; memcpy(mdns_name_ptrs[name->parts++], buf, len+1); } @@ -2315,6 +2315,7 @@ static const uint8_t * _mdns_parse_fqdn(const uint8_t * packet, const uint8_t * name->service[0] = 0; name->proto[0] = 0; name->domain[0] = 0; + name->invalid = false; static char buf[MDNS_NAME_BUF_LEN]; @@ -2322,7 +2323,7 @@ static const uint8_t * _mdns_parse_fqdn(const uint8_t * packet, const uint8_t * if (!next_data) { return 0; } - if (!name->parts) { + if (!name->parts || name->invalid) { return next_data; } if (name->parts == 3) { @@ -2620,7 +2621,7 @@ void mdns_parse_packet(mdns_rx_packet_t * packet) clas &= 0x7FFF; content = content + 4; - if (clas != 0x0001) {//bad class + if (clas != 0x0001 || name->invalid) {//bad class or invalid name for this question entry continue; } diff --git a/components/mdns/private_include/mdns_private.h b/components/mdns/private_include/mdns_private.h index e3becf28ff..ed7e89cb8f 100644 --- a/components/mdns/private_include/mdns_private.h +++ b/components/mdns/private_include/mdns_private.h @@ -184,6 +184,7 @@ typedef struct { char domain[MDNS_NAME_BUF_LEN]; uint8_t parts; uint8_t sub; + bool invalid; } mdns_name_t; typedef struct mdns_parsed_question_s { From 813c9dcf223115293de070e327923ee5cb84389a Mon Sep 17 00:00:00 2001 From: suda-morris <362953310@qq.com> Date: Fri, 10 May 2019 13:52:23 +0800 Subject: [PATCH 102/486] ethernet: add eth2ap example This example illustrates how to do Layer2 packet forwarding bussiness between Wi-Fi and Ethernet. --- examples/ethernet/eth2ap/CMakeLists.txt | 4 + examples/ethernet/eth2ap/Makefile | 8 + examples/ethernet/eth2ap/README.md | 114 ++++++++ examples/ethernet/eth2ap/eth2ap.png | Bin 0 -> 88732 bytes examples/ethernet/eth2ap/main/CMakeLists.txt | 6 + .../ethernet/eth2ap/main/Kconfig.projbuild | 111 +++++++ examples/ethernet/eth2ap/main/component.mk | 4 + .../eth2ap/main/eth2ap_example_main.c | 275 ++++++++++++++++++ examples/ethernet/eth2ap/sdkconfig.defaults | 1 + 9 files changed, 523 insertions(+) create mode 100644 examples/ethernet/eth2ap/CMakeLists.txt create mode 100644 examples/ethernet/eth2ap/Makefile create mode 100644 examples/ethernet/eth2ap/README.md create mode 100644 examples/ethernet/eth2ap/eth2ap.png create mode 100644 examples/ethernet/eth2ap/main/CMakeLists.txt create mode 100644 examples/ethernet/eth2ap/main/Kconfig.projbuild create mode 100644 examples/ethernet/eth2ap/main/component.mk create mode 100644 examples/ethernet/eth2ap/main/eth2ap_example_main.c create mode 100644 examples/ethernet/eth2ap/sdkconfig.defaults diff --git a/examples/ethernet/eth2ap/CMakeLists.txt b/examples/ethernet/eth2ap/CMakeLists.txt new file mode 100644 index 0000000000..5a2b416c7f --- /dev/null +++ b/examples/ethernet/eth2ap/CMakeLists.txt @@ -0,0 +1,4 @@ +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(eth2ap) diff --git a/examples/ethernet/eth2ap/Makefile b/examples/ethernet/eth2ap/Makefile new file mode 100644 index 0000000000..fe024c7f62 --- /dev/null +++ b/examples/ethernet/eth2ap/Makefile @@ -0,0 +1,8 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := eth2ap + +include $(IDF_PATH)/make/project.mk diff --git a/examples/ethernet/eth2ap/README.md b/examples/ethernet/eth2ap/README.md new file mode 100644 index 0000000000..2a4989440d --- /dev/null +++ b/examples/ethernet/eth2ap/README.md @@ -0,0 +1,114 @@ +# eth2ap Example +(See the README.md file in the upper level 'examples' directory for more information about examples. To try a more complex application about Ethernet to WiFi data forwarding, please go to [iot-solution](https://github.com/espressif/esp-iot-solution/tree/master/examples/eth2wifi).) + +## Overview +![eth2ap](eth2ap.png) + +The similarities on MAC layer between Ethernet and Wi-Fi make it easy to forward packets from Ethernet to Wi-Fi and vice versa. This example illustrates how to implement a simple "router" which only supports forwarding packets between Ethernet port and Wi-Fi AP interface. In this case, the Ethernet should play the role of WAN (i.e. it can access outside network) so that a mobile device could get access to the Internet when it gets connected to ESP32 through Wi-Fi. + +**Note:** In this example, ESP32 works like a *bridge* between Ethernet and Wi-Fi, and it won't perform any actions on Layer3 and higher layer, which means there's no need to initialize the TCP/IP stack. + +## How to use this example + +### Hardware Required + +To run this example, it's recommended that you have an official ESP32 Ethernet development board - [ESP32-Ethernet-Kit](https://docs.espressif.com/projects/esp-idf/en/latest/hw-reference/get-started-ethernet-kit.html). This example should also work for 3rd party ESP32 board as long as it's integrated with a supported Ethernet PHY chip. Up until now, ESP-IDF supports three Ethernet PHY: `TLK110`, `LAN8720` and `IP101`, additional PHY drivers should be implemented by users themselves. + +### Configure the project + +Enter `make menuconfig` if you are using GNU Make based build system or enter `idf.py menuconfig` if you are using CMake based build system. Then go into `Example Configuration` menu. + +* Choose PHY device under `Ethernet PHY Device`, by default, the **ESP32-Ethernet-Kit** has an `IP101` on board. +* Set PHY address under `Ethernet PHY address`, it should depend on the PHY configuration of your hardware. You'd better consult the schematic of the board. By default, the PHY address of **ESP32-Ethernet-Kit** is *1*. +* Check whether or not to control the power of PHY chip under `Use PHY Power (enable / disable) pin`, (if set true, you also need to give the GPIO number of that pin under `PHY Power GPIO`). +* Set SMI MDC/MDIO GPIO number according to board schematic, by default they are set as below: + + | Default Example GPIO | RMII Signal | Notes | + | -------------------- | ----------- | ------------- | + | GPIO23 | MDC | Output to PHY | + | GPIO18 | MDIO | Bidirectional | + +* Select one kind of RMII clock mode under `Ethernet RMII Clock Mode` option. Possible configurations of the clock are listed as below. By default, ESP32-Ethernet-Kit use the `GPIO0 input` mode, which gives a good performance when enabling Ethernet and Wi-Fi at the same time. + + | Mode | GPIO Pin | Signal name | Notes | + | -------- | -------- | ------------ | ------------------------------------------------------------ | + | external | GPIO0 | EMAC_TX_CLK | Input of 50MHz PHY clock | + | internal | GPIO0 | CLK_OUT1 | Output of 50MHz APLL clock | + | internal | GPIO16 | EMAC_CLK_OUT | Output of 50MHz APLL clock | + | internal | GPIO17 | EMAC_CLK_180 | Inverted output of 50MHz APLL clock (suitable for long clock trace) | + + * External RMII clock must be connected to `GPIO0`. + * ESP32 can generate the RMII clock(50MHz) using its internal APLL. But if the APLL has already been used for other peripheral (e.g. I²S), you'd better choose the external clock. + +* Set the SSID and password for Wi-Fi ap interface under `Wi-Fi SSID` and `Wi-Fi Password`. +* Set the maximum connection number under `Maximum STA connections`. + +### Build and Flash + +To build and flash the example, enter `make -j4 flash monitor` if you are using GNU Make based build system or enter `idf.py build flash monitor` if you are using CMake based build system. + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects. + +## Example Output + +### Step 1: Initialize Ethernet and Wi-Fi (AP mode) + +```bash +I (508) example: Power On Ethernet PHY +I (518) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE +I (518) emac: emac reset done +I (518) example: Ethernet Started +...... +I (538) wifi: wifi driver task: 3ffc7fbc, prio:23, stack:3584, core=0 +I (538) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE +I (538) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE +I (568) wifi: wifi firmware version: ec61a20 +I (568) wifi: config NVS flash: enabled +I (568) wifi: config nano formating: disabled +I (568) wifi: Init dynamic tx buffer num: 32 +I (568) wifi: Init data frame dynamic rx buffer num: 32 +I (578) wifi: Init management frame dynamic rx buffer num: 32 +I (588) wifi: Init management short buffer num: 32 +I (588) wifi: Init static rx buffer size: 1600 +I (588) wifi: Init static rx buffer num: 10 +I (598) wifi: Init dynamic rx buffer num: 32 +``` + +### Step 2: Ethernet Connects to Router/Switch/PC (with DHCP server enabled) + +```bash +I (4518) example: Ethernet Link Up +``` + +### Step 3: Start Wi-Fi AP + +```bash +I (4618) phy: phy_version: 4100, 2a5dd04, Jan 23 2019, 21:00:07, 0, 0 +I (4618) wifi: mode : softAP (30:ae:a4:c6:87:5b) +I (4628) wifi: Total power save buffer number: 16 +I (4628) wifi: Init max length of beacon: 752/752 +I (4628) wifi: Init max length of beacon: 752/752 +``` + +### Step 4: Wi-Fi station (e.g. mobile phone) connects to ESP32's Wi-Fi + +```bash +I (10168) wifi: new:<1,0>, old:<1,0>, ap:<1,1>, sta:<255,255>, prof:1 +I (10168) wifi: station: c4:0b:cb:ec:9a:84 join, AID=1, bgn, 20 +I (10258) example: AP got a station connected +``` + +Now your mobile phone should get access to the Internet. + +## Troubleshooting + +* Got error message `emac: emac rx buf err` when running the example. + * This example just forwards the packets on the Layer2 between Wi-Fi and Ethernet, it won't do any Layer3 business. So make sure you have disabled the `CONFIG_ETH_EMAC_L2_TO_L3_RX_BUF_MODE`. By default, this option is false in the `sdkconfig.defaults` file. + +* Got error message `example: WiFi send packet failed: -1` when running the example. + * Ethernet process packets faster than Wi-Fi on ESP32, so have a try to enlarge the value of `FLOW_CONTROL_WIFI_SEND_DELAY_MS`. + +* Wi-Fi station doesn't receive any IP via DHCP. + * All Layer 3 (TCP/IP functions) on the ESP32 are disabled, including the SoftAP DHCP server. This means that devices must be able to access another DHCP server (for example on a Wi-Fi router connected via ethernet) or should use statically assigned IP addresses. diff --git a/examples/ethernet/eth2ap/eth2ap.png b/examples/ethernet/eth2ap/eth2ap.png new file mode 100644 index 0000000000000000000000000000000000000000..39e59aa14fccc872f69dc5d7a752ef6aeded9f6b GIT binary patch literal 88732 zcmeAS@N?(olHy`uVBq!ia0y~yV4BLnz^KE)#=yWZsm7z7fx+uVRY*ihZiRbMVnK#) zeoAT%14GN6Gjul+YlpPyfqr7nF!q2>3d zt#_J#@t@)izxwTIy+Fo{u5DSTAK7dCmuGQQnDI}(Y-u-}UO)VS4Jx|!iM9aS7J#DuxevSCq3$By$BzNxr z&NuTdSO0}w*Ot|)_a&QL6?xHkvm&T2E5yw7{YCry>L2P=`P0{5n|%NLcNXuz@6+Fy zesSUabn5&2nOmMmSKVwch+Fw{uaDfFs7G&ovUh|jE|_okvN-_@S%?C#>oiwHTe@R)#3nf_wG zTPKzUR_4D~vR8PvH*5aHIX`8hUGnF2uQ!t(dky{QFhfh3C=PI(rX;?u!{4 z5BSCEodpe=jLl1C-1)KLVwHkzM44Vj z(l-I$Yw@2w<$t?weW<8>l{?g$hhN)B$@$I7{GMM-^|ubb*cHz$Bl54|dDqIy*`{Bm zjDKG_xAXi8pX&$Kz51B*MCI?-@}ey&RVB+7JhVObIpNR6V8_>-Qo`EFT|agB^FJTg z{l= z{L_raJ?DiV-Q%-gwsgzZU;YzyOO`$qJ;eSX;*I16n;926|AmQL?2udcz4?2C)WeOd zzN{$!7=QQ4Mb{?H`MF#^wsl-{KCM#>joy_~U-8gF%X5QfRLxnJ6dNg3zE!V_47NTI z=2e?9U8MM1%NFndw&wyr@Wim6E^26)%$yVxG{Hp3d)j^p(H)=K*ZZmmPS-HBNUf1G47cBE7|2#`ACp^=R$~)! zC^h9md%~w`wxbXH9y2TvQJJi|ErsFt^_ts13?ADsEuO=gFh}X*uk{^*>t#3AxLugV z%C>A_#LjHasTJR{f~Sc8EY9lREzWkgI{s+2^u`X0J~>N1PeGXl>{9nG{rzq6Lf`(< zx5p(>9nw*K^=DjrPuQ``??|kuogY&%{uKLE)&7zLp+;#>g2di`4($`KC)zUg(BzWmoGv}PR?a8y1R%Xbs zW^86y&DhV`+*Nka^-ILJ>jKj**Pf3M)jced_2Pv{c->Wo`LY}5U3p)=%SHCk{rT!s z&y?gc2OoZXNA2L2?3#k(>KWp9%sy2VFj~CkTXf}K`kHAEZ_lsyc*=a+rKW5Df2+&4 z51iP-pW(V)_ku>6@quWz4J$-=)_8k|F*7)+$@9FJEN}bX|H0EGp4Z%>d$nVow$)!a zaK6qv)Xy-z+RXX#goGz_8;Tq}XZ`3~QX9~^N`yU&>GjGZ z`TY&i-R$|RZeC;WoY(a~^inZ(I!u<8DcFQ&8Re$01MwPM-++Q+Nb`S8h`u~u%AIc%Q85SY3zkbmn6 zE%7eNhsPhjUMlg~ROy7v(p|FpO#c+uc-L`6JC^+dZad$9@wK2G>>PNtX6~&=T4~&2hH@?p0U2RAi(L5!4jjQ zOxc^#5z-!u_posZ&vW19d9w4-_fOrKnw&PGzP!%)68SNmssGOutSCzrij6it!~5!` zmHe__%8v^wb>z4A6!=8ud}KK0{Q1^&{do6O$yeF(*XK_9VOu-nw@6OoyU1jf;GHY4 zP7hItnx!bKdVK!R-27*Sm;LWuIL|fF>H3%UFS>_gbEgF?oO~z8oVm^Z?}l3TwoRWB z4>Bz(_>{_SbKIlY(4GHadNCLC!MhcT*{X#q{&H;l&8*h0T$jI&=b=O0k^P+| zZVt5!D_+|iS(F^Z>K|9cW|bl7B>5pg`(Hk@+4k1=Z!dj6EE{H+<*AY}{lE!BVZp@j z$2aV|6R7frW6z8~j%uIg6jm8e@R)zB#!~&sgXh25m2SLXc`+$~=4Ua=RL(Y%irdwRJ12?8TlbP?oyE<#`s=7Yb3(Q5EjUDESnBDYw_}!z7 z^FxQ=LYdnG={MetWi0pzybG*9(O;cgiXj$XQe| zP3t*y;^P(Pq>VxUeni*lZcA!kDLGZ>bBJ@(93%!G~tq`dagEJ2G`m29K_+zj5*c zcBW&z30Etg-pc;+wfbnms@4t8b5&;;P5acp)hYJ(xyM`|_p3xt&3Q8Q%EyU2f@)4o zNnbH-LSnVjMlmx-YnOey|M{%i)IRB;x=fKlP1(K|g$vGSU&z^cRDbq)QKu`J7gJdN z{5Wn=%GU1Sf6bEPzDH3hckG3LX(#37e&0y_`EY4Rjr+!+l3fejy(S0rU9z6X}p)K#DVZ8-Gv$F`~0jUHAyiW?Mb6!*km z{_ohZyynP*8#lw4<`#>|C#a?fy*)6$JX}Cz`^-M2ew7dVR<-z78vC>vE*F!JaGp3- zXt%b<%|Bgyyb2`_7jgr+WW#EfTwp3_=J_v>RWI}^{Ic`eh^fC;{kG_wG2Ku)t=Fol zIQ%Tn;xkIYjom&n9HB2ByydD@pZ~dVhVLGWy0nJmeG`|m^z`t0NV#5Rbp2s2qI!wL zQ*B%M)wPoMPfpw>-W()#<|wyg(&TE638m%V*GX`{SJ`<~Db%8EFVppA&98^_J&*fq zJFsXURMWgB;d0z6sWCHY)m!Hl!Av(DH>(}zZapoW{-{2|;^tL{nE{>u4Qv}K`pyTX z7i})w`KZ1sa9)$@>G}old#3ftosPUId5OWUT5i^oIj@(jOw9@^P?%P7bB40b!FY*j z_Y@Z7M|Rs-dsmk&DL9~&;INKiQc%I9nuTWyF7NT+Tv=9fZMyEU52@1>Zgp-_c8F$X zuIxD};U!qxS60_h=RfnC|I@Uyj8B6%T@K!-xA2zyM00_OQCCm0*S$E$X5v~~0S zCO9bUzVPJezpaVS=1B>@p0770{L`sb`z%VL&1Wk$Nf@)K>Tgeo*4}qjNJrsWZ-txF z<0D*awk$H*C7NP*TE%mb@`jkd(-j;py+6pdR`cApmy^FTDDf~&%H=(i zBy~eP{pfYw7rUxH9Mt6XS$6r?iq;S9OVy@z`TG9}K35dkx#1L7 z`O4(_4*Q)!CYR3(iM^4ol~1%*=Ck;Jntk7+d%kCmOS#!(DW>ViJ^KA6-kEX7&qMv5 zsUEGoi#>NMJy3O3=5YR$!fW5vo;R;LL;q%*#GSs|DZ-_e#rw@D&?Zz2jU_V>B;_o=eoO8O(E zb~GvEire=;cSYO`j*Ba;Upv)vRbFbsG6(x?F5w+JrrnjlCc3gV=}tMPX%!PU!xOh} z)75uvYuIiPt?pXX_oYEDVDh1o$%cygMlv>)Uls2hclscmUF299xij3wulm91T`_g< z`;J{XykM!_vnd-RuY_LSlKb*1?@_ztYJTB{uCLnR6Mr4vP&p^o-n_NJ#UZUjH1WPj z*2ky`rc$XaG9i!yz-I9syV^G;-d}&gp!b4_#qF~F&ONoF zW{Qi|C(lmGzH{r&iv@zt;X7YHyRu%zjiaaYR({_~QRi1Ph1M)-TC*fUWwGPA#fc0} zl7Hs$Zr;rIVcLmhi#d<5|5dv#dDgk`bywY=NoPX8h|J$IWxr|2=fwC$lP7JdU%{gA z{Wiag)+N{ES1S9@pD0|t-}1$QsL6XfUhT7uab0BgwC<(Tx~3_cjraa~@XO%Zyd0UI z>&1mGx!v&%X-#5uYN=jy{@3MmDVstrE#4geh4szunHpK%J6B!1oRZ#KBhk9zqz zia#Xg-=|d$63Qjbv#x!M-WcOAtfg!lv2%GWBV(y+)z!M($BQ^N2ox~SusN2-w6Ave z`2e9qvkss9vD>7N$3P%>QK;@Nku=F^_0*LTrwPvHJ`eqQm*R?D=7`~3pne4fJQ@Yj2HUft5{ zd-;2~rdD2I4Xj*qH!0uwk*TD!bWUz&)~1mPXUUsuGPsnt)NeQ3JXtWuUuQ&}5?>=Y`3ygoXp zMz5K^nUCq-w%IQ?eh{DZCu_nzF7cQ<@*VXZ_inoGNI!RW_XA_mC96NXoss!;^!GmH zm8<@=&x)A1ZA+ux!Jvk!BTrskJ^If|Zuu4VEzcOu`C9j!^nDZHe5>R``UU%+@t^jW zD<3*MQOKIT_p0XXFORv7YCfAHvh8O8)6=`pUi_R}^I6l-aLJ8lc5e?~t}2`uUY4~z zt9|}yw}gF9%wrDqfBU;d#dQ0N=j@+Ld{oYO*kp1oud2VfaL>}9gRAZxSfaMUAm;!WE`CLg==gf_#nj2459k1hlm$c`GpyHdWLK|N!G34TQNC?=YzAuSO zf89r>S$s<`C#$ZU)bm9pw%6dtluc(}^cse~(~2#yUz)0QtTpntytU`&`mER~AD^)s z@03$x6M1qqUZqvPtF%Q^!|G-iLs>xHN|&OoxksK?F)vBaR8nNCOJh|o+w!aAuCemz zm9Jg|9PhOA{p8zR7?ClJyTa$s&o1HCb7wh1U49sKX+64mxMRgBZkxi$AF@C8#Y|TB zU{=`vzvX7zuKGtN#!u|te^k!1e0os7Xu6fAkKxAGC4ZzBDktyIseQ0(t{KCOw_)t} zXHA>)W>>^y{e7ll{~Obf9(uG}XLI}0a{|df`$9OPN{pBN)cv+qAR&^1>d@;ne%@?DN*I{~+-}r6MP{eAzPhEoK|%empoqEheb%l?>+- z>2H5J#dg0I6(-Laje_sb63 zY{R#;`=+mptJ}Nvs_>CZf%b3X+mnTU9n0Ch@67S$%^QC$SmAnZ&OBd!X}7(nq(#mi zXr87rpRHN8bFVOp&wx9gg>w8;u z@uDi733lg&!bD1DotgRhyso%7ANRax+d?vZo*cB~_o~7Iw@0H?IPh=~`wktwnurvk60v?m4lDKi#8PLa&>hnq4os0-UN5NFx;X0hj0<`; zzvJCzomsplK!Ic8#H#);+k%5M=PO1TP3rI0f0L?U9=VKf<%7>2&Nr+U9jpyk+!XfF z#C+cEEq_8953b@iQ+ju1qJ76XrjN_+1Q#iv@s&_s5w-Vr#f~jnlV3g$_2K(kC3NVJ zN9cdSbIl=J&q;mW7Sv;W*LKpg??3fpXZs5Jo!o!LannpezH2+U+6tDsKTixwyxz}x zps&{AZRu(w+aHs@p0tZ|R@KwzWZ7xH=K9oWQHwjL_ANXc(k{N5$@JhpgMZxCa*LKd z?F*_b3Vowc`})Tx!ADgyXB2mopIW&#43a@oCMRlx(BNn&iN+B4of zy2Ez8>x1MSr>BmB3!1m8%$K_^_ao9_M|r~0lg{g3K7AV|<8td$K>Pg8U#q2GRTDfmW1KW~xpIQ-$bpn~G=e*9SP0pXJ#dD ze~zcY^K*|(?wrb_z+WI3e`eb5lMjCf8+gx_TD<6P=qr!rkLr)qWDZQXyD%>x(LDdc zw^y243Vhq*)wjNHv;F<%`9+_UPpk{IZ>)*$VlY`9x+?7UA+;Zecvf|C3YOmr+W4ws zT5$7MZHG*YoNcDd7oByh{Z`35aZ0j$vhjYF73%$wm$}(^lp@3ZMOhe0~87E{h<$}W1`TAJu_cl#a@cJ(mjpf@(XHkZH6 z`*dUCuYwZwo_f1anr&9;$Fr8r{P~kqmn|1@3ae?$N6)rwWCzn5ynyB>JBJb#t_2Gi!?x9bBKzdr0um%g5({mrEJ z{|2>v!DUO`E6tfrcW~=*-@d#5uJN}mGxcY4GhW;FUN4&cy4#J}$7bDho+87TSh`^| zXBO+^f2{v_6x9A|ZQ3+_p+ZYh^XdB+A04_^bgO0Fbeq(z)!`rSmH5xU_u+29{d>9_ z^H_K;9(L^e-SH{h=C#4r{@eK$%_%oj8~4hwINn<4x#i%jzQ9tR8xwBJylR>yJ?Hd= zDQA*zb|k&n`(){4!#lP-ubPwQscp43HD@SYbicK~%|qv|s)&r^v5SWkjyXR3Tk-T3 z^Q4s%rz%EV-tjwP#=SbbtdezCGC#S+)cjfXqHSuw71O%*GyD_%E5F{H6ME#^ti$&% z#!tA~A2&zm6ZeTPK3<%+&)M&}{JEP)^kLUVp;cy^HL5hfd=d#vytr@Q{#gbN+`GI# zh`sfC%lmo8+u6IU1b26(d8@z5vaxqrv@SAzW8tqqaxTKt*eqr%xO1JKEdDQX|5hg9 zW1fG1?b~B>mjC*TQ>Bt`>BJQtL*+x+9~Emww}A@`Q@b6*KC6Vn@g#1&e0csZYu|)=uIQ+I0M9N}K^>|AfFpP40(Nm~M7Y z_wkwI#`ZGGN4r*Vv4KI#O^F@S^$)`D`QKfjo`3kndgn^5(?8YjXovi_x^c&CZ}y#* z&}WLJGHV;ZpLpInrJ?vtepA_8RmpozY5c`W@`byszkYvQ|3pt(*ZuD9#h-=FUC#I+ z9;v6dL98Vu$Th(Kv?nj?xoDy416D#y?Q~@#5}zbgUS_Yb%YS-nrsc<#H+D;{&?u|# zP96JBBx8|9d(?Rb{vGwQU zU5|ONS$)lT$F*JLYG+~6fpbfa-`CxyIN59S-Rn1O92}Qk34SBK>f40;zB8xf8*K!X zKK#kPdS{A9_@c)BTVJ|uKl91*@U!)`&5J$=?YMdNm)Oq?*Jg=d%fvrjRH_%z$n6Oi zV}4N^wqweZ2djAvZXIY-T7EcQGB|v}=jgf#yeBPJutn-Ut8`ua;&s;F(>vw2tPMQ9 z{?qvv!G~1eZU~f}@$SWsoARXwGgr=zE!uHs@3SW#tqZ@*c+toH(rdM{(5?8auel+A zljCaZw8NS2EPT?wvOZx#g+u=8)1Mu-=x@F*b@I87 z^)Hz{(gzdXZHZ@LaJ9|Rcjrkl@4xzI!~AcT4Kyfm-gRRACzr3CbAAeM_!)UYkyFc6NAgzNPaQ{>x^w4GDwnoQ z-PZm3?aI=;z%x(yd?b%tJNt)0;rv5o=IWVdeP_;|sS`YKDU17d*w?vVTplfF%~`Z) zTiMNP``Dy?9^N|A@A@R;Ohjt3V6lrzq-I9I4cq5gJT~EaC-kHLTU_1F#qsdHWYEh6 zKkC{Vm#%c%;Q3sW*J+ocTW<9>hOg-h{T;Kcr~FJ_ynnAC=S}|izjj^-&GPLz>hj=^ z{^k2RCjSmFDvHiM*&dzpl<_k&Puu6~yK*gJrf%A8V8+SS7q0ZKXPb(-@-5pF(%a{h zoT_SKk!cmwvRBR8)cED^Tcfw%LZT07soOzgC%9zHw_(x@$IbBkws|KEn;qg&rQ8&-D17!`u(XcbZ=QGWYoS zX2s*4yX8AS{hW;y6#57oHgq%PWIFMU3BBfatmQb?db_??U!$_cw4H`sr-xe%7*)Q&#;(& z{#{(fw6;F=;~d*u*SM~89}L`1{e1p$(yD{c6q*9>rk*^y-TfQywkOZa_UCnXncZ}C zSRlk@SvU9e*STRApVZ!*bbr&MOLgtVo3`J3H}m&`rWI<^-woQ=vrIabKRG?|YO&o! zjT-TbY5J#9^*k5eo%q;bcJmhYzR<$~x$Ky{@y+bau&NiQp6+?~c_C9Z>+S@mN zYlNxjiTrlBJ|W`V)d_ExnP@vMx%N11`P=SeRl^k-dWTr%Ay=}^(9REf{KEn8*_#@$>H zA(;Jq=1f@kGou`rPt&KR4`cdmj;aBK?VH^retDiFV?x88iJ7 zEwlp?wx2#&|96_o=Dku6s?IH(&)Hwyc4haDHU+jHQ}=gCz2=^#mJ|Ho&g#o5d)U66 z+oUOQJLBZCh&17x&xh7>2i^Ads29l7|FZm{<`?gttwJ+{*?sRX`Pk$A?%{dAmnT1S zE}y+RWZ~_K{hxO|dZWS=yV%?(_2+{<-ZAISExc5*qfYE_^7|=0@)IX<@8SI{s=SS5 zQP5w(H{u@`s6S5MS(;wiQhIEL-i(k{Glkab*IuiroSS}@OIs%-C_|1p>pItT=NeD8 zOO?_-S>5SkM<&!B>RxWqa>(T5n~ycKIqx{FS6sPDVSB`3vFTcgm0ybPAMKu&gFKXZCM7Dj4zoOm|qp zB88GWo=1&m%JGGl#>qEo&Qyxp(8`k$JCS{PSKg+0*2$?xea7rR!|D&OZ7_OSDJQzr z&Nb#}(D%K*^W{@M2ZT-Ly0i5~;uYVFx__sq$~^gXx89_ZX}{`J?;Ug2M+$1h+Wa&+ zWpX@XC2q`$@%XV!eQBbv+-ujIebY0)Jy1J! z-C*x&U8ed=d)FJEjc|1n`MKCHTvJZjziIwAecgy4hUYy70u#c&xU)WD-gWKC=5x>I8NTy~rTSn!Z4Y#D8D>8CdemI{gywtn7giFsS z<=C;%%Mxv+AG_CiwN6v{cy`tPl|Ov~PrRQuHSM3CGsDcaSMSAmIKEO%6Ykjf^qs*g zqf4TFcXX@fG#_g>T#~gv`{1&&P4@rQKL5^Q`ak7Z!R(De_By9*-W#1izcy~?Q8({X zpVN)qv=?zq*p#Y}ef`NN_hTIw_h@fA-y$95{yFg16Jyam7n35bq}I2bF}yS9iS>rk zJ3r?1K2Ghq+gAO0d!_2zoJ~f(Po=8ccV7x&v2@h?epfPT^2aN}Gk;uqEMVG|7H8IY z;QvSei(8(j#y?t@+x5jV^sBnomRSo~*<+g~9eejn@ZXU|GgMwo4u7G3$F*8!ibCT- zHKnJoyM0pA!;Zh7V6wFH>W+PzdJ+#G*;;()ymYVc2K`y4<|dtHM{nDC3BFA~P&qdz zW37xF%a^ww-bXA-YC2PG_4!8ZiQleqd^%qb%q`1)lcC;j)NC8{k?(PayS(6wquR3W zb)Wb1v4?5Tzt711a&@Mu@BCdGviWT}&PN?lxZ2uq)+1Qd-T2CxL#F3WZ@wIS^~&0? z0y`0BkB^HkrMu44nff!Jr}ygbCljUWHeSyO49qXcvYMgGml+aUdaCy8v+rR(+o$eR z?MpG|za*h}sltDy?+fUo5`&;!lGwXZD{z{c116>vMe@1C4onex5vd%%WFuUi4~n`#Q#r zK@~=6wJo!(V#KRu&sD$mzS_A~D5GrIWp!UR<<%TP)ryn8zHQT$-JrX0uKpeurg@QR zr81JtFBJmPD<2gfUbW%_>pG#HZ4Rq8$Zz{}IZ?mULCNye<)c+6kH@Pv955}6Nn5Lt zAsCfvxiDbqnkzP!GZ%-s@7Vlsi~9ndxAp}ed>_6SI={>Qoc696;y(P6p1!Ux;&*RK zp7O0(owM1`TjF{ye|lARpGSz%JU z;ezFX+ogSb|2s59{wUt{;^k(Y$1NvA?&Y>0vH2?9ocfgI)As8d7rfnCF_-P?rk_8b zowW2Wnyy#>&zwEv*rE*?2l-DfTWlG7$~HOfz=6EeC!RgrmS!cjtI9v*+JWVahWAa? z4&4+h)BCe+UB$$nljq})EskJXy@ku+e@og-(Z4cI-^9NCPPD32U#33-jmw#SSYMs>xrm{LTvrxuTPdu_k8{K@Re7VYYx=QrEP40-dG$x^+Wr#Hw$=keG4A_+`ZcG^0ur0zJH4l z-p-8VcVJfNw<1T`La{Sn zv|6?V=qrmIRpWMGS~v4i^z-A*5B8l=^C>*4vHa)ZSt(xxE=stZ`8#2WNKZxepIc5* zD#BCDKOEJ5SNf?XUPtT2&s^aXHj5|f?q4eOw@Qd*bY^xpfLJ2~t=>cLSDycCzI#_(Ypwr=+Z(KJc~t-HuDw?Kgh!QU z+lt5SYG1jps!JbZ)N){WxJ0-;_rczhBmaXem-?K`X>DvuIm@Ru@pnP->-fDK(}VUM z`||y+on+EA{k9U`{Xt%Qf{t(hteC2NTJX)kOV1X(DmQ!L@<(~sF%9`;M~~m!<=?yL z?#$qjjLl!;e>lh9VXB-mk^gV5{j@0wmwi-^bh&T;pf%ODTk(uZPJu|rQY?wLO(O=z}W>CzWJHScCh@{WMeQp zHG9$hz*+bG1*Mjql`B5PymIE2<#)n@Y<}dtmuH9lPSN zao4QqwCnsc1h!o^x^sWcw-=9=Uy^?=BEh{EzmWW9Xf>I-AcuDYJfa z%=!wAIm@0^9a_q9DNij(WTR{4_xZ)8FW*X(zFk&z!!&V`$H&qw$KRg5>Fxc*rn|Vn zGeyWTc1EY&=VejsS5DTH{IKzRdZIlgg(d4rt`D!x*XB>w_J@9i2kjQSIltk;i5qJT zUgm66@%NZ^)lqp>slAo>rn$=Ei|Bu`<9XC(k5GOk)L;VGifqdb17{PFN){$*y&j8cY2Li=oJ0a=gx{p`0wfv zvx?Z+#>X>w!Dj>753RmYIwNrkt4gj#u!PElJYPXRcB6llsT?;LYSfrk zf7Y2kWN+qJIdlgk4&6H^TzUGoz9v(xPM!@P@IAHLT4=<|5*gZ$eLcWQ6$?L4Zj z;ON_VBBIgm<4obiSbY)KRsS``Zx*+6>YhK}zIWa1BE>@62dC~^Z>nQd`Iy_Z>dYEe z>Ag44_jmHmx+kOV;dbcAE2*lSZsnKLJ{+*?z0dpaJ%gC~m-<|GtM!l8B!Y_C4^ZKf>-b<ZdBAu(!L#Y%Db*)e1hchQpSpP3zW$}xPS&nGyPMj&?>xosTE$F$E&KIY#+U7r z1Achdefv{b-jVh*XiuI^=7;IBN;To;TY?gPF9}l2DPNI4Ip)>1Bw@bi(#Q6_IK4)C zirmZ->NXBrrK|%U$M-$T>o_5p9q~zfZy1y2k1J``@qz-u*G_*T27k-%dnN znv-@WJoDn`xVjA!@BQokcK1P$aNwE$Ub~hC)oEm;8(uT=Hd@cUsiAL2_v6qd7Yom) z?|m$K^W!Fc#(TG{L?#Aqwmx!Z|D^Psl%x9|-hbOYt<_SVU*XYyc~R-82VMO3tL_+> z%kMb1^CrWom>*A{Z7Y*lxKNep!=ZQXUt4tacRe&xy~M=5WJ=w^OfE0CE&P|Pl5^kh ztoZ&lQDNr=hS@FGJTErbADQIPExl;MmldM#vy6h{&I-=#VNg$Tsg0SvoTchDzs=mN ztMB=&JawHXU6Ov}_fW;4&fogE>#_xW?)kj)R~kP*vQVhAcmLt{PkwZI{XWX|TGEa0TtAzxbxz)->F@q*>l5-HT{YhF{=Um|@OD+e-hVp_G7d2sYiM7-EwWBs z=g{l@P7g10JG=M%yl}zaV)cgJ9bwhGw$5EL^}q_=D|Y)FC+}VT>-&A#gk9e}ot`=G zES)jiN9?SuiKIlS=B@mz8ne7N@)^keiRLpooFn%5|GVt4eN#B@ZnH^nWT<7DSuyQL zs?3|8r89pm$_?h(WgX6N;k4+dJ2%#s9NYI}57#Akfymnqf^L7N{t(%@-4`aKKH`sp73&}-Q1sc-)ftvti-bZ#G38AwlAwVa?U&`jFr<3 zKWpomYoy}&fB&v^QJG~M%;SAeotd%Fr24GS+?g|LzwZA#x5RVTTAO>~OW0K+HG9`5 zPD{JZxFg%+)ZAZ+r)PY3bNJePes)p)9@+3Cv->pezMW>aIXrdFeT`ku?petcpIrC6 zqcQyCJIk3&?T=;pEiP0^x##*n-QzWlSFh^Llshps?U-{rJo+XKq%p ztNSKbdbr!z%Wxg&*O_yy_4dSnhxOlS+i$euytYC>2V}% zrFn94nqf%L*E`_&e$wpXjzS;Wr;L6)~=FfTk*7UxOF#C*ok&~UDmvpU@`%}DlVe_1Adbd3I(*NJfKIi&*@t&Y*Q`_j_g@j&Xm;uhRcsby|TC z`<-pm?FB-vsfPKlsp#6W)Y4t-K+CVMQ+xiD^{i8730mFFs9JQ)KW)!B$=4+_ifcos z@6q0UuW#0}l5^*tFMGf4QrN`bMUoZoLn{xpCtJxhor>-jVsc%$mD~5?4fnGuKWz-u zt52JsyEWf6q`>QSr5pP^4!=n|&#yDTn(=E9-}||OrE>-KjLtl%VOjO|@uI_*6fg5T z#Yz^fxE8T}cZ%*Eo$F5w+_H`KO|GfY3IE;Yx{0mj?2L1KXE@|^cb)k2!EsK(|5N<- zk~zNfqdvXs;E+yH-TSNj-&VPEd9rg@q_xEFYNb@=&)Bm(nLBLW`TMbfWl2Y#R6JZ= zwEgc1+rmFgCqBh}I+UpGQSU#?N+`xjSl;u}ii7QQyiaEbmsd^Rqxb5D-;_)8CawD# z?nub`E-_p2@MB1|*Ygd*zSUD*-Hjjr@(8ND_=W$L34e%fX5^}wQmoO=Ox219=KVP< z`13RC9j#T@;yle?H@`Vuo%r}e>dJtx+m$M~USC?kD7<1Fk9m2Ic*6T8hFu%Xj2F%8 zQ*-nGyU6kg_tnmmQ9MUZOxi9f%c8L4TBYxe?P1wzNspxCwmL~{%e*>2EOztjR?+M& zZJp(_xjK_SuDWi1Dv!O^5Z9LWC))GC3=e6pnRhEG4D~n zMVpHp-K;#S4osVto&KRQ<`lEe56|Cr1qSiI7~{`O$|-O$+4pMFd;6Q=Y8$S*R~=c* zT+;F^YPsgc_Q^-Su)kUTYGUSGyTbgWy9^a}%-7lqgiQP1o0uBRJag&}_ZqI}e)gP+ z3=B?>&OV*J+jVyf-!qUu69xQ!ozc}yP3_(L z-|yqgO#&&amrY)JbldBkmgg0_cTT=mxPO=NtOr69s@`;c+2gl326E_8ZLEf`BUV(yDeL}eWLqE<-hQM5?MCaPH+3N`(N+>5|&$& ze|-y{|LdE#TAvkX>$2!$hcBL~$xL?8W4%+m;ryO$Uh+3OF1m+JJ$}^Xr_kG7`=;vp z`AzI`KHhuQZm0J2i@tSh61pk_&iXm^Itio%$+)+wE;KrMG^e{%*m>q^=a%KiC*Ir; z*LHTZs>unZpO5Q~@c%s1aeUJ8+Gg&V3tb<&Es$MdI3d`@eM`pOzgN>9gg50_^qkd| zk7j$_!*%cUzjZ%c*E`pJPw|Km=UH%B>9EgRNx5D#<7)~|21{Puc@?6PJ7=Jl7CZPRPu&<{>3m>=@;P;m7}k5qb=JSt z&j>ELx~A~6TjKvUvzB{aUKE*mu;k(nY5l(kSwsG=oK(ix7NEW3OUgUf`_A=mbE6y; zSG6))b#2gLCo|x@@@X7-&*kQ$Z`?cAoqVloAmx?J!|bmue^P9H}{f&T$#*6hY`Y?||BGKnDa%X7 zDiz1{M;<1#&hMC_??3ro%-7I<2`{B3CiW|A&n{kiWi98{sJpz?y;gg!JgExIte$*N zT9sK>jOEG(#r7@TD%ujCg;i?V%vf?+FXp)JnD=33!OWw6ipjg$qPw^66FHh*6&I)( z!ai|t)~8dNPREtF6hpNd+VgzZ*oog*AkXi<=<)&0HF8rm8dt74QE~eE;k^@kLxOB> z@SI!Ht07YMqviaow!iC)UY~u*a)Xz5?z6_Gy%#P@d-+90?A^FKa>CWreyD3XVaF*Mql~4Xu>mRD@m&?t^ngF z2G4}dO(Lq|{r)$`8MSPB`6&Oo;={CV*OyBqSJ+K(Tfwk7dS=GX+9gXb{63MPed4Y~ z$IXkcCZ8^8yJQgYZ}X9mdrnP#A#ZcZK9RfKqx=@%;#DU< zeVA(TZr_s?cK^KQZua@a_3oX7ck&5&4@pNGXN~xeyBu%kiv-U;a6Mo}dS&g3!X*;- zw;$i-9hR4Ar0w{6G3&Ycw>Fg?=9Z}|^WC&~-tx^qn)EN3>0MiH)w|}H_qrpurvKxu zPtA+k^3H$B4V~D<(rVMxOuxNeI7fKX(SNsErhloZT|C8gL7oHq)u)oPpZ-`jLwka| z>)z?>w-)=f+5B!k{5!yK_GKG2kMCONf9reoo}Aa4W4*Qd=6l2GTjz*aKC;fea>M@9 zmW9i7ZkPlK-k+ekdfC743;fd6b+^@DY?v7KTbZ|JMWUaZ#*uFl=>@k^{~3I<$*8H2 zJb1TCTuo)Uo)6!0*Sh%}-K*reQ$DSIuf?}IEk}87;gR%p`u8~;rTABLFNip@VW!vx zqxZRcGhfFX=*@2bH^tm^PezQ-h8I%r#1G|OpU$)DTht2Uqb&1$CbhRt6mMTy;Q8RE zz&3OC|E3x0CS6}=DZjoceO*QHUUdD8_4Z4T-pu zZG39x)U7jqyo=C3W~_c=MZFQ9`-zX7X?gcf&Z-K@ZFp7qbBo)X=*NDaJ8KvD-Rkb# zJ4HD7sjRA)+R9(?Cnq+u$L)JnQtvwRDM#A=D1Ep2;u(TTIl4L9*2Ku{_`2gl%ajMP zzD)|}AAh`7yvKQqq4*vJ!%$Y~%PWow_wq&jk+ak5c)f)0zSmKn#U7IkC+*~qdLuQT zV{y*iH&$gTm-ZWHultnHQD1raegBH;=e>U&%V(4=oLsbhUCoB|Qj^sbil^0A1US!+ z65BV^b8lMxtJ8b3eA-oYRAaV0D$eK0wK=`N?jtwz^S2CB`JcCneV@wRlr;H$zof5G zXZylklk+o9hHGAz`}<0&vS9w^$W!<3P000LpAviJ-^G{%g3At6%1z8WZSBFKP_*;_ z`!1n_KQA>U>Xt1v$YwCJn^Q6^OZsB`gZWQSugSFk%)T<^n1#HIPlEXJkIPO;iv8O9 z_i)aIh{6iLj9*6&onPRvmc{eY=dT6VFKwNW*|7J{BcsBgzb1;(2Vy~scmJ|XW!_RcR#UfSAO~D%A2E)_I8E-A%wvjD& zlbWIn->!nCH}?9+MFfQ(I%uW(#5DBM`L${C)7m!clt&b_NUivJCq?n}pQX1}zB^WA zy7WWkN6DSHPsjiL6tiNnd8J2GU9R4p$!w2)Fy}004yrxK`*~eiZ71*R2iei*wrk|C zaS{Gl5F;ro=wYjOt0W@%`>)XO9?qYQ-(sz#glnGg80a@$7YSsZk(J*)_3gtgyZ+b- z^D%JlonY2>JWF29{OxO79_{6^rQCtvw`wy=|J!CAssDVlo6*6`vSNix>(|XZ+1qruih=eOU?i#y7dm$P4=9@NUX<2b{ewPL|bv|su> z=h~{Zqq?kDKHBPaljf~Ouk*Si9+j|^91%+0V;-lmbkQNT%$_U$vpQb(rqz@=e|ev3 z*KEw7%65D5?|uI(ZI48pnX37{?8OWYfZ!>4Y=cWly zn!Yx4pL!-99{aaQ%cCnH_`vx+^IIyHpE3S>_-U4%Y(moOq}8Y1ObMUf+`v6u;x*IJ z_J|_}!pE0qzg;kaeXaB|-(KA$)mR6nscYKIi(Y3HF!?2)T{K1csOSn&$JtXh9J~GV zrpt5H{-*`G(Pfi(m7j)qPdCcx)#BPx_T(Hx`h4x*D;4~;+vVr}SiSID&(B7LpJVs^*g`RT;iWh1`Y$M6wGWPY#p|${Z-L{E(+n;7_fNgE-mfg@aiW>; zn7aDEw_6k?b{$N4{r}J7S;5MiD#Isc^vw&N%wF&|O5&ea-Qn-ok6&ZcIkf-rc9Sjh zBjqF$G#=Kqc2&$`FBfBTNIK|W@-B}_?84M@Qas;YeE;JAlaKv`SVGc`uY78k9q)Yq zm+f}*?UFKKCU!Xm-e&tF1u1MNgpK#vt`#rjm2b$2d3Ix;z_UZ!_{7A|to!==Is4v+ z%I8h*UR(0;OwhWp39Kmw`sWWZE6#Ykcu|GKmZxjJ%%&CW>0~Pj%2`HykFm0Le;_I*K zmA@BSmI*(ZsKy#`yuRL8dT!a>j)M~GntjuMd+qamGW|-@RL=5(x|VF_DL3a~`&%2cR$TyZo@s5wj8^iPoi9l4sE%?%(q)%u5py3SwW%f1)I>T z?qZhHckU|{=-}6x8Gq%fp}@<@UyWxOKUcFWJJ-zlR%VB&*Ugei+ijJ2N^d;eRl9p% zYPcK2h3E6<6g_$HLvWGt%pc*q+gp~V-xa%V{6BO{(U-(sfhjEdC)I!aZ~E^$^UwFa zegB_7maY7DK`{S?#w@M>vD~lcUO)5y=SRL5_m5le>M@Ua68=e~+N!0_a@}-Z?UMIq za*e^-C$BYrRFdcCpY~O!JY{E5ZT`8ZM_pgW3p|Os&3t*%)Xb0DU!4&*_Gwv@DOefN zsk|-MzGvIrFS>0$i?rrMNSMWb*lwn`(rfy`Td!j5?%utyHN~fu;n$k^5)AKipESs; z=u0+KWz{{=mXJPVF62B@DEF|^{je)$ZEh!zh&)Q4yYRyv zFQLc5mll;D?T-KVZsPuvO-~BmFT7HAlxI@O-l*$VG4XS<4Y+3#WAE}&YQdCF(Kjq{(tONoxII^+sR$~?@l<I6!rP-~qKcbm>?dF4tA|cQ+fXRoZ|IO0!|!?EdshrN=n#UKFJ2s0Xc1Vu?5^|E zm6y{E-c+|020u6Zwa7Z}RSeJXc@rmSwq%5!^xkZx<8v(^ME@$_P;^O_j_JsKmKY<^Lg3a53SdA4M2YQ_F|L3B4Mpx4ud)=Jh zFKtdrX21Hf^hI;>+k57$B8KU6wByIOlnO0n|7M(eem-0H}0q_wrnWR z71BEYw`8B+f|#9o(W`T>SDo-^S}JfT?3(lawmW zXqxP9sk!WRYilDmzj1Cm&?<0!w;AWuYudY`wF9Cy^G!247|W5!9qs&d-Cp*r<*y>% z35$q^KQ~(|sde0Mrt-odj*FUGs#a{N+vs~P;_|K8>r-vTGq2xc&fO-j=<+-vd&L@i zmAe<(A2*+uO?@-P@_Np;x&_k$G+SQiM45)nv#MQIcB8uB&J#1)S$5wq|GZ^6Ew9lq z{LSgAbCYBCI&masuVDEsu;c01rE9}GyjO@gm58mC(puZT^!DSU7k~L43KHFWE7WMq z#bl4TS*pbbtuGeM64JUoxy4J-CH-R7!b??!e$jl@*VAQF($9J4m(067!7zA9k*15d z%EBNH#am&kTTeBgmt9+%m~;NZwF`gLOC3z2%igTAo%w6sUiMde3d~Jn^jcorc{TsX z9l6;pUW!M)uDR;8bZJsV{nV7|4eP?f-v+{C!*82OmBUmq0UERUc%zd%cV}*!Q$F{3k3om_Il<|J~_lB_-i40&iVF*?Y^{FwUvh^so6=2HO0% z6L^OyS;D+DZ@1o@jWLFc`=f3zdOdObvPm(fU5}jlPJa76-W+K7Y()MOaRln{R zxCV7!;R>75UpyzhH_!UBC}VHbp8j2_*FV|?)=Vl4er}ZaDrWXG)7GZr7;ko9*P z-~9NSUjicC8si)@=P%*2xt!@=nNz)+Q-igaKCiRt zw^I1k^|ybv)%L@2S5!l|E^dRW4_k3(R`w31394$k&!%3#pYvs3IQNum2RE&pW&Ee^ z)t8yt=i)Onjh`>w{<~7=b;~Iutr~8=}#reC}5cmp7x&lN}lwT}xLQ zPGOi5nB(E_{87gujTLiBRigOar;AQ}uwubU{bdTC?UPTnzIojHcJ;S&*Kd5g`b)Xx z?*veJnY;Sr!b?>vZf|P3b*-}VEA!I_J5KV}&EI3P+Osz6-5kH}V*9!2r~6_{S54QI z?91v$?3d-Vbn-D)_jK8ho;yXRn(Wihx$$l4U%^BBpb23~z|T{!^RMmu z!RmU{G*d3=uS4aBor`mBZe0ECIjFcP+mMsFVvEK1%RjB2Z@l8xpSeBZX26P&Ig1nyA zKWyJnECfzuH)XW^=h_8V=jN9>luSMp=G-W>LRhOYjs5gBuBl?Og-_?UIW0|YD(%od zJmW>nqdC5XXU|_#T(`zRZt5eStiroeiII)>vUWcUcsV0uYpnFQ)XUpcL%6Kf!C7nC z*@c&^7;YJrUwV|)xapsn?kr)QXzLKCyV~y~zVBRXBBC{SbNAZ3!nXmDv(|5%bva_c z<-`=7)x1;nuHD|67;lyv?X(ohp}L``mhF>>j-S4}&LUeQ#OZFrO1_S~l5=U%EvJg@ z=c;RHtt%4@iq6hlp`+d7y3}HFNWzhfHTSzTH%k30+ZdOWcX(gUt!*t{iO(TvaFb?8 z)P4!`#1cKs{=Ck8m!cG{&)7`n}C;7 z8Wvvg;7k=!wOXvv_d=-HK%&Ug_rik}8atLqo&BsjTUsmk<~LDLLMXW!kZCm2K&p4` zzF5VU73Y@)WX_D25D~q;%6j{EaGiGRZQjj|tFLamJz-h9m&Qqdm$xextSEUhm0?QI z6yg3`k*RjmmrS1ZW6q(|jO%YMywsL!enVdIje9uvRJF;So|DxczIfr0xw1!|L-E!; zUr@nw{g(cbZ*p3VW!zIig;A)}QdOTQvCFAxY1{TpcH&5EeytTU@Au1^=3*Ub?y2fs z{$|xT;+&QW%ymxZpZfLil0_OpOGTWvtbVNqQfA|P%;zAeW=g1B3#-e{?sD?0=Bwvc zycL$tJC(Q9b7c^hqE(ac!b?@6CD~bCE4IX$1lvc%>4roxr4~0QM)R%aR=gFS&OP-y z*JP04B`mX@mX4sKfbA>2MdEqzU9;Qv`go?ePAe_!e>+_cd^ zGt^7b#eJhD*fp;jGJdT%d;M^p?%Bu{mjgDek=Ke=4q(byeEfp@+SGzOPtxwjb0pfY z`yP;)J!6X2Qh`N_Z`8QXjjiUtvhTy%nC)vr%kJH|djD(l($f27Z*4DVHD0Qc-~CN4 z18?W`K`eAb=lVqV>QmEL}uA^&GR;@$d;?Ue9Q_3Z5n`8x7G zuYSp)c zw?!7|nrK3*M~OZCQK!#TzSQN4(Y5t|y0s~_nEC0o80WcO>Cy3??i?5G)=dw%8JBqJ z(!L*$y3@Y@omc&C;@`3lb+NIrl`of0pS3qvF)Dbnlj@As71u<)w;zxQTYLZ7npT~) zg`YmRHKhhKKka-pS>l^*uyy+;T`^ToMXPIH1+nV$GZ!qD0oscQ3A-%i)uRvy| zV_;zsm!hK8%-4z`QCd?uIXT~oX<0`W!(= z+ShZFuis-XlJiou+WA^HB+5iR=G3eA=U(2<-~aTozx~q#jm#`8EHPj2|Nrptu_O}R){!pRG05<^XgvKa{6}OZr^jkH(6zDtF|1h|NrC1kG^-aUMvC?FuPh_+lEBl zkkVTkb<_0C{r&&D>VMC(tuFev$LF!oTTokmk@pLog_jm>EYQ4qYTe=M*S~kw|96+G zT(Wq#>MjM&sZJew3pclVb!WGFuM8{Wy}d1W^R+c`Nc-B<12E5o=HZ*_y3Mbj^YIxTHgjj^(_ z^4c22aZwMfu4SE=madPQu-oE|SGQ}o2z-SY9cvoGrRY*U5!7B20x5L4J!t^gUFUoP zr)bX-cPWj{8mv&;)g|Hn4wMBkWYdxhcRM&FXdGZi@vcpDa0h+r%_vmXBJGTe9}IAP#VZ zIyo)f5jS<(v_~&qoLJ&Hd7YGAE-!N7oS7;8Evm$|OkPX+cZphyz&4B0Up{k#x#s?U zS>s;bS!_RddN3E~?3A>$Yi&o{4y7h9ydTsdia+SPCFvI2J#YX!fZ4_dp0ulL%+Pgx5$)y)VIaa}cK z(b*fNE4I`aP4*9Q>bMs0e~VLIqv4r3)6ZRtao)Y{$uZce#>x_VWHcA2!6x3~Asx7%(9h_r&Tgn#&b6v-3y-UxtZQAiHT(9~?+rSq1kYZYl(jTO(=~eY z>43_u{z;!Wy*U(J!mr=STDqsbYg=0R!c7acmWF7z3OI3Weiov=)JyYL^WD}{$LtzU zs_QDY2*kz)ub%qpp60FQ*w$0op_)shKppBCrc0wVL%lA>z0nG}_3Na!T_LAp%Zpb} zY=dt|@8+DkEd-|SX3*LxT1zwh%Y?O3>pBFSI4+uO;&Z$qyqjaH@5&%hHdAad*d4Yt zXw{TOdvCg~m{Od_sn{ZLR{43E*tbQ8ol}cfO$k~n;>4kNMru`<*VLer7C!!|MY6$) zEdq;DU+tfMs(SGY4p}KJ>D57MDk=dH5gtxXO<@0YtlqE=tYZov@6>Rxbb`^$S9h+Z z7{!{#oKiOe`EKr0+t7Q$uI?{41l$amx@n<7WW4jkpjDu8gk8HXT?(2rcka`_0VOl| zxIqCOeJG{&>*1v}M#4@U7q6Or-5b(z?TX^UOF>Kbgw4>=(b==e^oWpNDqkmPJi_Jp z%LxIQCQ{OWO|D+Jb28g-rjJDLv9LFg!HNx`?el|PWX+P&0tLOI%kho3m#Bf7hb;n+ zekE@A^aXWjI24b3J)AdrR*M&SG~;#qT{Tls%Sy3D;5z?q-z*`J=8j!jT3QxmZzQU} zze~NjDb;7L)zpiN-HlUD2z;rst=cPyBlpP7d)E%m;+Ih|j=_KVd*lE2;TjTFB z>HH@fkIPLi(9A)Yy==jP2K)a%&--#s%*)I3nQhkV+|F0{?aj@!_rLG#EPnF*xpDF_ zo;!E%&K5DM{{HUl!^7<>*R9Jtx>(Ul8tz-M`?cR?9UUFbSa-a;yW2SF2*k&_)32-uEO~$L?y`M9fB!E2_2s46>&ZLz-OLTT{IVq6M6pHSQPt$B zQ&TUmY^eJB>gYV%>Zp+3oOSZ`ei2s=N%ZOI>7UOSpZ{>y{C>g1L#(GxpPs#U|KH!=FKFMd_nbM0sgHx8%mW8FSuU*_*U4Cs{Z1(3`y|_ImRKP9**PUy z*TiH>cwFUD&*uy7Y{?Y1t^Sts_0`q0mLiKLL$z;Qzwg(nWw-x+-~T`LU=wTaxzwDq zWuMQQ8`uA<+4uXM_3U3i&YIuf@+6?c4>I0(@gT^KD=UKoSBAV5Q+sVb)$8aK&EOwD zpU=1cRnYNz3OHvkVops@H{TVsXJOf8U-N}mvu4`WTFsm}Gw05ZiN3SVHrD(sN;@;7 zF>H12wWSLAd%uQl+O)~)*M^SQ6QJ7X*j7)woxlI?vklK5Jvubm-|po0`*pXMWIfW~ z|L4&5`*o)eHnYFHzkmPP-@h-<|93@EJxTn!KDZ!pv95l-c6-h`w|#%V-Cn!zfBF5| z>C*XoE-tg%^YNJU+I{sGEy0;w(aNE$%fWktn}Jwba+=USD%>hktF)p^!67;55$W4+Ql ze?FUC`RC(t>s{KQw%eVm*K2*PmM;?!gBo&e&g=C5&+Y#|{P6H_&aEw-PoAWtou7C1 z&CSi8o}P)HpPk+F<5Bk<%i?7xZ~uL{eEzH&dBp>L&Z&us4@G@B6kA@b($dnpu{B#< zQBm>FL;m`V54#@mp6z~fb+!1yg$r%I-AE1z2`Tw~(+$>+&N?r(WGz+EL-o z>N?lkGg5J*7HetVNz?CvQyx_GK3<@-Z39ovt!b{DJF}K9t3S5u@8&ng8yD8DDk!=* zc}jlP13THpD&f70x|VO?wMv1NYwOl5TErwSEVKjpjl9NqPPvKtx?={R?m36(L9CcAmdf^F@o*HTS@@SsBrjVHQlDJGb_1!p*(a zKHZ!j2T3hdCmYdlrBhGr%@^_Kz zCJX8GqZ3w&shoa9z83yZu@OWrR@^F zJ@4)=tM|?3cM6!J)`~SBJh0Szy42qPOTDLqlAoiaMMTzGIQaBJWcNCrtNVb&u0EaH07Xn|Yg(HeKcFdema4DejzS#`$WNCfnA9n|P8} zad$0pnmZx)p5Gedu0>Ai2C=_)m>hsL5t!sEp9tpd`RF!HC-TxFR)?W&F@n0}=i-Yuy7qz^mv0W=+?*br8@+UC z*@lEAX>m)7%L?CWI{rq^K}6IW{9)>69~>A3X# z@+E6}Hf=IGKbLp{|^LBa9!*;{aM7O{Jb?Zksw)L(<|UwMtpT_z!4>Onb6Q z|Ge?q&l8dYHP5ApUzchArTubcWJySIj@M_?>oba@gGFEEsd`wL1) zcsMvP_}l$VaW6lv_RL}NAE zT1y3-I@Z1TeBOTls}D~%CLg!jRee1yXOWulU>Cczne9(t;iiF9t3()~yzuUbUgt&sf`r7#~-KZdT z-0HKVx8|u%_qREAcPE_k^sVtr-0XMd-oxiFcSdc!bWJT3)DMZhIJf-X%@@^6CQlA_ zFApwJl$Fzp26yv7L9iaAj8&_zrWYJFZ|!D@cpk| zgFiYVGH<<`xzfF<@f|6^o+ zx8$8xYZ3T%{GHR&tx(GqTQ>NYiEBN41vAF!hWc(EP*f}hMT8Sa;{G>A zA%~_yrC)HZTD|)A7S4-dZ_GlX!a==kP^L}1|Hdrj&{JsWa45c+9ov2i(&>a5^Zbof z2-6IoWqt2J6Wz36PkL<`5t5>bcx~%3B4H}CQn0tu*23f3cs%#+vy5*{-gx&Gymt`J9nc zr%v5kzk1oBJFB!CSKV44;2Ijr8X6MJ8#4QU)bGt--)pbZTDfS}saLCR&DV`zxo%aa zsrkA!KliP=^`~~~&xplx=_atX03jh$oVWhGel;W@uyo_IWz&Bw-7|}8mBwT7Q;lAF zL4m~;b<0lGNNAtE{l9hoN`_MmTBld8Ydght`eWOvZqvUHltQlBZu{Dt@i!pqfBrT5 zX!-Z6X6*~8eVY6Fy?@6u&={M*T%XdTti=+mmZ%lQF6r{B`}tH!^yzes^*daCF7-O4 z9~M}*aaMO~XpGkSWqbBCefq?u7dkCCuCQ~{r|yuj@Q;^UKTnw&zhu#>P1|Buty!?< zk5XtzaA2rsYH8@(^HZ;e#;jViV9}~g-|EEmrv~%J>`A$|O_e8Z)tU!~^zE;=9s1Pe z9TF0|XwM_1t09FiFR7k7eR}1ZHBTNL?PdrG2@4L4^!>TiOKbIlH;8t{s}5h zUa@M=k)xZuPHERKTeE1E_nhUs_H=EUzyGn4eQ<2(IsKm5`RJw+Cn{)^9n}sV~Y zY(q(=T6b{xCW}&4Nl8ha*j-afUl&JA-rBfyrsSG4_YO4QmQT9cr88|=>ZEzwTIO1p zZ+<^h+2+@i;1r$sgYVS87d)7me)Go*ziaF6oS2u+_u*1l+|CjO)$QJ~NRDBD%xHg(!WT`YC-`$tj*0S^Q-OZl;^?uct9gMCo+ZL59 zuKqsn%Zoy0ko3L1>1W?pUpV`aO=ilxFK-HsyDm-s`}_NKyWm&h(^W&drhzAP=lX<( zicU3E-tb8Kn6%frzCa4YU$>fy9J@UFR%aa@QXj-a(%R-Hz z{xg{BedjT$zwdwcthr^;@Bh2GpWA;w`11Sze)~V$%**dz?YAr6@j-q0?VW%B-#r?i z_x|wvy8E;3zwMK4&(mshR`Q%<**?$wPSa)i|4okyv}=D?Z!Z0vx5@mCLiXRXN#g6D z>6i1l1m-<5@2h|GOuG8x>*(soS6=^LsBw2k{WBT4JO2c}pP%RWs7b`k=8M7NDmXc>?PCqk;RrANM7Gv#F%VM!LiMO^?wy?Vy=l?89(~miJ=H%y}y^rSy zh_uG;mODJ>;i?}WmkT|5+`gzx)*~W<#nI8~&tGOvu1gCqw+e_XS^d{8LcDMPe6idB zjl_jjOw3K3AFc~Z33*1WkhA~q;B;7tgZHxV5?)tv8OIVIo#ytQ2j_Oj7)bC;pRaCu znNKG%$Z2T?C#ctckux-O_VO=#%m3)sY?*hY-#_8QhQJ;f+pdQj7jtr76_zxd@nS}V zTVGdt#v6l0?Cyes!hR7~1im=837eL%FdlsP_m zH{i(4kYL=EP{Z8J!NaF$V8K$JAMd2R_+qQ;qgCHrR&yUeAMft+B*E$MA`Tu7MFWMF z4FR9O-;4XlBlV)uOoOM~T>eTHucG3`mbPOL-pU@iv2pTDV|7XY^9+X$w+l!J9GY-Y zd4_Ga+~MTM67~00wm2?4(!aA`>_|{A=pbH3wg8Y5Y=nz36^`dC>ds zM^E3KK4XTV#lK&CW$*7XhXe-lJwG?qs3yf?@7#Y~+o#KCh5NJd6&&_`+uFXHKQBxF z&Xu@|s*DexzNB7oD7U#&d*KJmgJs{G-oEPW-)=88pMj09==j;Y(((HhY;0-^L#qXa zgnHxTyBDpf_;gbJ?0Lb}0hvD|J79S;HC6TL^i?|}A`ey{zkl%V^|^wD--WD`ES%fj z1%&>(znlH&^p@X?Ie8>qJSrFjFB-D69~F=^m5N<4uVcl+9o0Vz;}3}x$=x~lYOC~( zUA64d+utYreYSSZnJD)!7asBiR2!ro`JwP z>`Z*iqYKSvLL*bwol0^%l_a)QSJA|1mO(AgVYg$3DL*u7m`i!2CEY(g;<+-$WPT!D_D788~ z|H-`SI~*Jwhwt9)pD|;iPQ(Pi|NrDNOstxkYbsy&hF89QD!${f{@xesG*Z^b9qRPI zpZ<8)Q;k#WIWDyc3P|+yPw$)Xz~i27)#O9wagKqz3Y!;g$UB>5_jAvLBfry&7C$=V zJX^?OQ@Pdp_cMJ%mds=3Kl^N|dfKOucPUDXohx6yWRvBQ_e_4DiR z+^H=n{(kWEZFzZLg)DP@o4tQNeE9_Oyp1h02XC*UQIP~6-}Vjp_xD+Km#k@CDxj+f z8cVU7t99!1+3uj8`SOP^U+*^j_}wt=O~E_wjwct2fB*bf|Nr3~fBOw@7p|?S%(rYERGju16P| zmuiP?{`>HF#Ktt;pY8pho;0R=o<5QCJef`I)=!O_(q@yVO+5K;Isc}lvr%&ON%H5P z>_0G>ecsax-0nAa)v8y|tC{7jFZ1wa`uybM$^8>2?Q2g3E7|T-%Gi=Dx|99KeN?afj@r^ zNm!g{Ug+HZ>DFuaipOV_*WBLF^Htlp^5IRJdo{lWG*5Ex43s>v%QU#+` z^|JiAbIy5JcHf+1%lO}c*Z+OonbnorkrNFkGBWRZ`Ru`oZ+F^PtT0;tD&H$%Un!Mso|{;AD#ku}AF9QPTbPn@=K%9qrcI$qIdK3%=36T5NNQSa%SRf<(tdQY3U z$D)~Y^74thQY2@t-V`xuV^d?Z%>`C>gMta{%5KLdo9NGMY_=)*e!-ydkxJhFzkG7_ ze+uL3ex`o<^zHNZ{d?UKCg}5@ndzAjz+q>1@W(GU;|rw>!otTEUd*WYabu5LC9*|lnK!0yV&Z-o!o{*5Ut{hIi|LI2mc(xbPdk3TqAsBy~I z=IfPUhAGphU$49sF~7;9;~Avcx< zUfpsI{e0fTN$v~#rJPF680@L7iJZK3+S%x$8+%mS#p7(wbk_fwyXNcbinqN^e0zT= zwZ~UY#`m z_j&pASiL)!&x>xIZO+WLtN3}W9*@kE#_j$LQuh^X_Ws$i`n>HI`3G+Q8S{Gjq>rz$ zaJT=U0*po-PU$5%JYq_G`ALuF64A$3nkF zFHSM1*E)CcbiOdvvsjrWx4pZs<&KiqtXp3n<;)RPpVV{xMq;Gk$38AS%kVD|TUuAW zx)t#AWas(7>Z*eKOFp{r_NF~r!da7JbL{`Qe+M=`c01N5?H^I0ayZFdP*Bp@BLl?R zkoj3GuI{O6&wQEZiyymf%d78bm#^>9_xsoU#&`CQADhoxl>E>*>~>60M8+|ofaTT% z52h`GKiFSzTxt^%knqWvA@`p_Ht}H~3tQ`h=j&BfLgJ0n4O=d3&}ed2;=I&0GkjV0 zVGbUyqvzw@9TGTpJ>Y2ClGxJfF1&>I@wM#4>sS_?JC@DEt{GNN=vLeI$ zu1wy7{|)6|Y|IEeWsMRD7Fh z-VMXf>HI2!_Bu_qyPt0~FSl}+-#!0h^1G)GE^9uqHEU*$PQUcKOxIjvd(w@+8c$89 zp6Y&nT0%^^_fqUiTh{>Rvj-Pezx_V#GG}Dn^~J||Sy*3sMDOXT{XK2+6chILyuBWN za;I;7(mv<9 zJg6W?A!OQ<;r$7-I(~T<2o=uoM*JaN!LD#?nFO4Oh6Qi7#-d(wFjpcWRt1|!p7~9nB z>6EV4cWyf_B)RnX;nR}^7rQO`_*LCPrRT*AMHg3o)wBW~#f1}lI^|E_DwX!|^Gv?o zd^>XH);4v1o3rZbcInTP`#;^eDE{-u=JPlHy;y9Mb>nRQzYp$BPKQ2nbGeDHQn!fd zj}I`g`{%f$s^am=kLnsKGWEF}|85D?{N3`RN%_O)>ks7xdXAZM{BL-0YiGl>11}eI z9&s0rsNVgdL!!o{F{AY1k6)P|zkND>`ZE94)#BfG@F*E5v@AFfP_u`b;{(6ja^B;< zZ_jSHaZ$x*R^N7ihR5nYiIr)5^XFQeyTyC@<-NCVG4INfk8}u%i0(byxSVgXhSr_C zwjV!nBwhKia*2xm!^{5tleebX-rarICw}_wjt-uO`T2X!9sINW#k&i=KXy12Z9kiG zb&}$pcaIMLnORkqKkvk!-^@Qg$F5uB)q8Y;@x&d5QZ>zo3X0yGJ{8LS^Z51opRb1B zFL<+;|3~xV_YwPSdYRezCQP4x{9rS?WyJ@D^ERJ(e*XOV_=%w4x8PnUe=+fly%V#$&Qkf&l`dsC4=)2^wNz0v= zuDI|-;&tnBqn?#|8ePJj*Y%38yDQn)g&|hM z?w=z|d;GefU+gT`=V@tJsZD!noc35=plAAad&PxSH71Qeeq|OGe?MriFr(wdK^B%- zt0%t8v-6o_FxBTumT@yY1Y%{;z3E^rv^)eTWyHy%Xmgqd1IJbXO{`t6nMb-NMs=mvvh}}EGv{*4S z^JPwTf$-`uePt`Pru%>Yv2EIP>E_1|X=b@6kNxc1&B581vZ9WqSviT5=h8xHvl$nY zml@>U*!!cMRamxO|FFA{x|)et{QHD0iWBcx7pd(3d-Z+5uK#^gmb}b~mOdPvFYI8qlo7dC{oQ%ruTR$he&3DkIc}fwexIq&OeuG#Lo7lejR0Tw%Gw z8>;196sXNL%jWfgaJRrF<*wvKl_#!GyB(t(l62~zYv6?^k*@24l-pcn&-G~)KPWA6 zoIfT0`M#ycVuVc2tq@^#Ej$uA-=%JD@ASYNj}?-W!UHT{dzgsCt1_60X|0rH3qSfS zI4I5J{yod7*{RkVYPXO6+2PCkcjuM{maVgH#H`+L_U%TtQ)-eUL(i9HgQZ3ms(7WYdzxuoz+ z{B@9}-9n`>#n!^`_qpf9`CM9`XAgg-(+g*%9mSLZ!*uneeT?G9-WghC$F{3rQJQY zHs?%b$J5oAiFHx!fV;qzgY- zgdWJBSyMU zv9nsZ{(Xg!v9Y9$%X#JgyyXv;ir?5<&CWira*krN^2w;Ja$DQE_4NKqT)5wSVS>lA zGcynGO?IEWJ6U`E{<@a*c{T@Luity*#g2$e#?vkwUuy69dHUv>qs=+@zF2Syr*F_q zm*;)yzy}s6R>F?Lo=0r?C-D|r`*gDImZSL;Ky*ZMenckM)-bLGdeBkl# z+o{uY{yZ12`SkL5&99q=c7NYU%&*OR@>%$}<-e2e68-fjjvv4G{4f8#mAUfr)$_~m z{k8Y2`N$bx`&YlHXxpSo+a`prHop1uL)fIt&n8Tpb#PH@rjM`8G4c8PPyKrHltD0-CbrDvMVK7M|0_xEmJOC67o65 zw5e1}WH}30mT@X)WtC{xBd44o(45`Mk69sIO~-=H>YP;%Te@J!k;${N4@{Wb;Fctg3RpA!gR%bzsV3$H0(`l5-ke+$*p9O$kVHSgWxkqWxI* z{e>To$Ia>8yl6qt%7Q*$)$0?y%l35dyd)E!u&ymmP-}LTjmu@w#g4>b1r%$aZ&AIhU zVE&vR*7N85XcCXB>YVfTcIC^j<{H0FoY*U@W_aQ*cWwp?s6)6bMY8+SG=p+SqwjtG z33ra1^x6NPQE1t@gtNPPm*_k_$N zU(Zi@xhXVd{p|C7cicVCo_MlRHv43p&7|_U>bd#ee#yT+#n0IHM?8M!zq8llYfqZT z&(5m<{&fHUllK1q=H~A+EBqvXwC3mg`cF^y|MxsO^Zy5s++pkT4VAC`uC0kYdOcoz zbJE!;8QJQ^{XFVbEBeas-*T@yC3Ebk|D%7A#gf*AGRp4#2{$$F1=Nurq+}C z=f_7CwR`8ct+n1!vU1b6ovKH!##~>xY15`I`8tiKhm+@TdjInIk6*Jd8{J>Lc}LU= zohyrj=6LF^tNwh+hPP{x+V&&cZpYLv>}y(NAviyO)2&QH%PQWFrHA?~zeI`&&1_yH z+!?lT-4cbVEiE!#ryC%PI3|64tf>fIh?0>I*qggh$mhul-R_EYwr_X?(tM711`0-J zB->tSJD$fmwM*~(s-ok-;$D;ar{&&B!<$v+- zkWNoeKX&Z?>0iCwGfXt+yz?@z(zq*Oduw`o*_qUbO_p~G)8*Zct4`WkW!wMu{^5mx zZ06bh5s}|J<@Ck&q#w8YUhWCs(fz$U?%_dO%QrVRc0V~Pxh8hD>#t3sD!MZdpFaQm z^ND5Enl{{Q}G{jc~{_PIHBt|w>yzw-0f-RKYM?$)K~uv-DQ2}z?vnjI%gw1A5HlkxxG-^-@bOn^ZA+yso%Kl^iHM!eRb7G z%6|67WOd`5D*@MXV-Bvnd)p(v$bRuLmkkvsW5o1kdB?=aNSo(9`}1>t_Onp06(Zr~ zv2CYTt;+iM_o?}tlh#L8mGW;gunyU9?R?hCT zPg|`zpNNVCBxL@qGz~R#|5C|&cBRhZDaYtXL zYLI=)iy0A0O6sRUUuQHg)AA$ z7h2sG^L8qkBysEtux&h;_=A=8@mq0^01u0Mb&Wr=ojESFF5T4 z&FwwCwf^>}5BI-U(^Z~aI(yrN>!}Qe&B>Js=W5>V+N{s`&uW);{lolxnFm+R%P#r8 z^y6PD9;t2o^Z3c|$PEwTLTuk1xqV!|y#HUtuI!zE{l9-WSABj%`unWBq8CSdyNq9N zn)LHcx6QM&vm2ewTc=E!y5{bdR#WL~AAh#zZ!UUkv`M@_E&ABD<{9>|wEzLv&iAHd zzRK~Fxc}(mU+ajCN>f{YPQJM-S6H)n^Nfkp&ISA19r_jGA7}fie#XDQs`dXD&6r&O zpB+S>xBEH8zvuYRBNcNV3CDjp`dt0Tk%~Fz?f>@p_tzgvpI>uocmDpvAo_k>wJf`< zqGqYs>aews{$*~i`2VAN$~ac@)R>A!r@v-SmFT&VZFWkF9Ky zih7ddy+TLT{+gic#R;?4x~HDLuu)TddNyDE^q?t|J(9jX6@Aca_w7;69BGSt(-xNX z{*+(pw<71)M*-iUSkKg_s;d^7h4jsyao~jEN1kq3$2T7?y0{#>GS5vz$ENE;hs2>n zi#ho^RV-ARCR{jiE0~G3-9CX02Y&hYpFREs0x~GhTXih?B!){VP5mE>#5u zCVhGTKTLZ+e&%;sd~xRfUq2tcecazaU(9|#$Ch0Jf0$}Me!9HyV%uX`frj?-gtG?Q z#o~MUtFWqtFdGWq+dOE{H$A>i^Z@SlfJj&kU+OkOFZbj1h$A5l2 zmXterx2gHkWQG46P0foWuH2vdaHI3j>-PH}bPFH<_@&^mV_?O%n}7cleXKTnp0Zfu z?vDHLQcf-yi@ks{Jrx3-twOh{wm$8 z{O#LyuXZ-SeMP_d|Me5f@6}EIe!qJ9e)0JaUa#M`M%L{AuhsfL-E$1h+}zOoMO4}|dRyO=rLQA)skMK#w3e_hG}`q*z{%*ioc3^Y<8Vzs_FL7 zqd^gDGl~MVxz2suDIylVEmP_E(ucuvj+)CB*#v#{)9B*9c{CtQqT8!bZRLTEixK-p zgD>?we1B=yWwp#A-)F}c2ha3xP1*G-aPOYj(sQBWuU>?5t-3Mm@2yWabf=#B)221m zH1wjj)>L0xxkZ_0xV2U>K2D!6w1E5Bz823O8Dal7AO4@eSK01b`HXkE%Cfi9*StKI z(Ie}+)Hl8DUWkip$Qx0Ys`q=pt8q0cB)yMK*72Cww>Me8WaE-Nvx}eqHlERrJe&8; z*X*YGoYz(B0%a;@%v>&0|90LHm(_ji-fIS@Ys@aS3V!g5P4=u+^0B_-7f$eezWCU{ zOtN?5$qb&}GxJU@U0I|vVck5#pqcZEjxF(Vm$!}OF8RuH!Te2xR)Csut(BEWs?%5ik@%UzdMyHc*EV5t|c#-)nPaP%ke8$W?b3A z@<@1*AXn?2Pp4M99B-MtTS<7f}_B zqpqkr^UhROm*eUWL^pf?d-~3rmrUWW^b21G{wcru)cZBRb;H-+PdWeZ@SFdSG`u&33rfv( z^9a#$6gjLT}xQyI@9x;bae>J4=>>FqgdXeis=xKNTSR@ukr_@d?cO?`J5 zK3|+BA=&Tdp4B#O8pFnjo^^*=jd?p7>V zXuA1KYlW4?W)V0C&=gvxPHVwSlpX&NLwGlYL(fY1Mbe*=I1&p1{NI;E-)-RKc{itKEsEC8!LWRJbwJ#H^xb4^K%^s zjR}jDjh7tP&Q}Va@kn08?^{>Qws*Ihx_2{g{Al>*&erywpWkfQTCS?5H80b6nX;hg z#+ioGb!UI#CB%@#9~i>*98c=*wVr z4XdJ#Q(ehZrpY{wh~#{F+zXVP|3qjWf4yo)>1VC0q17CmUmWAl&yX}uH!iy)AtW?; zS1E-Q7{+Wm9-kHa2M54*>2`hOu%$hhbOzxR>5 z`ds4gJNlwdo)B?b6wKM>^R6SLP4$tySie*F{KCo44O9D!Kg<3p_{Mcy-=y(Gi%*cz z=ZJax`%I;`ZO(ff_KDj(ATIRu_WPyx*Z$M4SQt{qV^$yQ%F6pYr|gZ!0ssBaFCToi z|B2;!$-1}zhq5x6Z@=~i1Xlj7&(hLAws6%Vr}dnLITleTFY*@NSn=(R2YdFB!Y{|l z3pOe*`o8N}%p)$dfC=FI;pwo#)s#vyt>eA>0ds%nrvKH{A1@amN$PNd3@fS-t^?<^)u!rlHy;^$N#?kE?-o% zYUb{Vu@idlUw-%gyp?p}yvu+8p1#+gS0@>}H0b_~mYbi&4Ki2phD}y8u5)USI2XUc zLvrmFiS(l{3?EO_=9zjaGs&v$VzQ2*q3q%*UdfX$zIZuXBlu~X+p`xhXP2B76#=Re+CMw1Dh%uZR>GPR)}kYfq>pM~Y2eb|&F#o9>RPUlX=x@miO^ zJ$(4?bj9YY0-2`+PVh{4J@fISckW)tf{do?rrzR_7PG#!Iq$AjQ?v6$5seKp*=HLX zn7$W3b*_wxZVHclV8dK3*F)i1fEZ^BruuD>SD zBEj}_=ge*Mbc%QnDJ!&1SP_ym$;i{=teS<9Ap7@(H!)0wpSY60{?fJi^=WneKkl;6 zzdsoVao#Tsvb_JH)NZnm!n+stJqG{P&sP?H|NeTf${F2?u$+C5kFiy&?2W76HD{q^ z-TeCxlD1jyohNB_Z_8o+&&!R)Lqn=Q%{(%%N$<3+-2$VtY`y2>>UYWW=3MIAw_xY9 zqd_-!MqMa=`g(Wwk<1kf7AZY^zMQT0_)*@x8QC@8wy=G9Y1GU5-$LN;{C(fL3u~4w za{P9Y>CIQ`=OI}Jif?CnyeNJi@^8}j#k1p&TsEC;9e%~o#5DO_Q)`6p%&x0TPWEix zlJtiq``Ov;hpRqqS-$$E&l{zw(;llQrsVX^+{x;8?Aa-er3)uY{$gA9;^pjwjFi5Z z#1%e`(vtmZ3I;t_u9WD-Ssnd4`*~sU=Ld`V?Lp^c#O$nUT=mMxNomo>?a@AYac0xs z$2_VyDrcs0F46f|%qs2e#Vh}*q@TMcBr5Bcl49c5mzR;%G2yj@g2)L26NAT7u#cFn@N(jaSz8B?yN@S0~2 zpNe}__^>!G3{+W~HhIRTmKQB4Z)9u&gXLSNd7M&WFMoF1D&hST52q}pg%bmRu(eE@ zFv~G1O6^?UKAp0H)LCb9LfTg>RKDuEKrd;@GKPsE(>t#j&oD|gtNNv;QSx=BNv2ZJ zOo6KpmA05JIZ-0g6LLA>(v-+GaZ^3F>~E62y}jt+pUib}J5T&9UTzT=estyJt#d5m ze1C3C^6KIb4vX@M%rsOz>M9~{>lB%3`25_wXBDx$$4_}#M3u~#^hl|1>Qs&S(P2?O zvAL?{@9!*Fmc=i(j_=1WmQ6P1?KhTKJeqttBIW&=JC)osOpE0%=cH{;*k@aurk~>J zCU(E(@{Prd0hsr)D{d*kr^T$Wwb$551oyB|G!{fA~joI7Lk*L1;rHGHC$)QyzB_nhH*4Z^p^ml1 z4BO5z?)d3i`0UTq|6jb{FPojW>*x6oAzJk{pJoIYnLT*?fAMk3U8FK7L08boV?s8ebH5s9$z=@rJFVgWW*?5oncFi;FirLDg)(4hvVw&HOAf~q=JvC8J zeBHbUpYLkloc8wIE+1-L~h;r)&;aqmn1TdSn)MepQj4GuI|Sdfh>TtAQ4~ z+cx|^@cf9nYf?f=$EsCqyQ+?znYCSDYWO`Dt;vg*>N;v%0jE?6nIr$0e}CZq0BXx$ z70mLPY1h5-^7D&}k4Jl`a=9pR8g()?E@NqQXmHR-{ZXJB63W_fl&vR7+#vm4jLaM{ z#fdMJFHTbw>XcY#HOca`c;Sx|eAABgC!bz+_D**8quu%YpTtEAhp&%$@@wnqJ3Hde zPyNLmv%f5Irquq*$5P?zW1ii)c{yfx)cK{|&n?TpnTYGpdHkf)%1>tf(?_kvclH$a zo}R8(@a~PLlyqL<*CN)=!ih_kEt@=hx^sbxKy>*#Ma@#lo!VR++`UP!rbynnX=PA+ zEXK&l=;y1|`3$>GpV;*?@}F|h&%fXA7jIwKnJilRL@YP5~86 z`y3}${Azt5F7wrCWz35w=b7B?WA-S2;O%^WiSeDNKy$c*$+~?Lcl;>fSh_e}qx-J& zl>7OwWeza8*FUrVvGIas!=bO%897-${r9lmExGJ#{@-%mpA(bS?LW;>pJP|gciAv` zW_ot})hT_dQft-x0==GI(KOy5Va<5DaI!>tV(Y$noMxBP3{T!-3*NjjR;rYT$M@o8 z^DsA!r(DjU255c)sCC!4P%^2r>B0$#eHvB=rp=vQ_?ai+L4xiZvE3VL&+}c&zuAx< zp~0OizFA4X`_-(&5s@#9FBYg=KAw@FCEM1&p1u0p8zln+0d;rto?bU)krM)3DKE5m zE?zf&{51TYf{syNlj&>E*iAgLwzZFhC+#SBDAc<8da_}j(yS;Y6VsNS?Q>eXH?u2W zF$t7j*Eo5#d?Dw?p3Nri3zqi2WwJIhHsyS znGYU5_D_jPa%bN&jlt^mPY?aP zJjcGg%XoQz@w+#kud=i)YghGMUjAyu+La4-y^;VWzE4Yw=U7Fa{+nDtp z*kIwL^!NiW&vA8yISmss_WX4&_|A9eu>G%{L6-LqPPgpdGjsC^>GJ;>dzv3xEt<`E zvEqlj=F&?ywRzOf2guxUj(8(>Mrry1y(Fvrjr_%LYd4hgJ?K_W{d42*{`GmgUOxNJ z)8Ba9<{4+Y-kjq(Ygj+O6*J6S#d~e?awAaJP*>fsexB^*DJoK~jb!6o*=ezZ{Zjr0HLdl3>{%Wey12Coi;5oY68-5j z-)y%3Z$20Q?TatEM|=0g9Pf}@|F3n!35&k@cI__f+E01wKYsD#(9h4}H@E*+2wry1 z!8^KVS)0J=!mu3{)e9Etx#aF(`_?VHaaKxapHEASlB$kL!`*80jYXk$4<}}R`Sl=} zr&RaHmhD<6IlVo+Jzbq+d4q+SRg?@ber$YmcYnL2@pHqB3mi*~ES)xTY;3DbN_oU9H$EM#GE7Z4I`uMc3UbDYT1;`aEVY{Twyg?)_=D}Jy% zd?ozRY2}>M*C)@k`oGrryZ8f7N1&TUY^@Tgdvwy6hrM4wY&ZMn6Q(cJgKmB-PP<)c zc>P}cm!&(eT)kS_{%!ZmWwZZlSsmMBQ}^ushwt)opC2bzFwL>7p2sPyp1A+U#Fxft zhQ-%pmfbpYBlgR*Js|cqwb6I6S_8@lt{7q)w6ZRiAzsgm!zs}uB_F&=~;H9GFtd$?$J#> z5lKQqou2+G`uiK+G^wVnJ(+lUR_T%*e#Z+ZSvBs96IpXAwenohwe|7+Usa?*<3~QL z+Cvs~d#q>vHr$vUFp%Q;PW z`Gyns7=3+ne|`VWqt4IYH?6KOegX6Qz5m1Gf-1Ww9xnaxx!d9V4W5-Q@{Qa0los+m zOA~W(b1QmrLA;`B=cVQO`XO4#k(Nr02?Z9i^)27LUUJENbl@gh0I92rcid8zx7wzepHA^ci zYn4Uao@ukq*9Qmhluu`mR#H-m*r?Thpo2l^(xns8TRTIuw?CAZJGzJSbK(5A=cZ(S z?u^>h^ZR}+=br=jcQ4&Ed;Y`l$)~n`E8~;ZO}@HI_ld}w^qXB*Q=W-Tnzwb*6xE$B z$JB&p&+rR~Fj*Mb+2VTap}2^jgNI1tLc@v*_6Kj}yZY-n;8On*NflR zvvH%LaoU-LH#auUm@y+E%S_7nv!Ty?L+;;4`z$Q1ghge&OMGO6)%=nk9%?;uBob=8;DB%V%XN2g(}ps9T#G85Ko5dD7P2uh0Ef{IR^yxkpFm{rUPeE+hNo zg-asJVkcjIJSDwjXQhCwY+vRJnffN^~vvS2&n>F@xJBW2Lu%_mdU8(tT&oSjt#zm^`I`dt2VU zL%rJ5cN9ERS^jF(&eFB>W_208T6Jn?;Zvc~(5EuGRz4puDvQY59=f>bu!yXs%f*S# zr`C(gTDnAL8a_SlH8t?mr%y(&US;hlel8|{e(J;LZ{1Q-R5HDTIr_MgZojKddAe?4 z!QZey&CMIONgh7rYCO{{TdmOZWX<=t{IZN6)bDPV+*N7#e_P1EACLP@Ot%+2`eOO5 zrf$ZeuhA#t_jZb|Hrr71)N0z=2;ZzUJ;RThF2_^^MQ6KYxad5**y(aa<>6a#mxKtN zM#YIcs((F{mguVwV3D(H+)?r4(NpoR?%n!&a*R8Ex7mklrR#i1_~pd^?8dI7Tx03o zN6-CeKl13Ace`n#UmzHGa%ba^VI%BQNf}&!aj&3Y^ zl>FGk+xy{x#=kNa8|J5;p7!wN%g*iF&E@3eDypg;J$u&H-Ob(C*QXP;g+p9iJSR8z z1cx z)6&{_?v1&xJ^sE7(pufUVwIkvmD-#eHzI2GFbBwA4P2}tsD0s*nDW$@i*zR>*j33a zJ|6MOtKWQrHqV92=5GGiC3f~PCUr7x6`bX>WmZ@BYH_Johx)c2E?wFqX)fH|$vxBJ zrqt_~(|qRG@ygaFAGsBEDssl$-{=IgE(e?BIfcX!XJv(n)^iho;OUmyGI(bCO#a(A43 zDeTU{{Yl-%X40EII!%k4BKD_Rzx4j>dG^e~+v*I?&faeQ^NzT@mOP@a`!ZtL$K&$5 z)VWGOZ~pUW_07k}`*#+7wX*&$mvVSo?3%kU_?_$Asv)r|3LH)bz@Yk6;s%K}1zusB(Rw#UZ?4w&> zPsi-6nt1x_^PN>&=dBKZy|dz@jkw;N#rysVO@Dp9_~j?db+P-;th`(rv#X}>t+mEFLuD z*%9&G*EScwuR5j0_fY`xmwrpk?`V`FnPC6+eDFdh)Du_jY-^e~M>jTFYNO zxOl>;l9$)N2j`@nOZ@!oD$DMd8S#av^HTWEzPi@W z%dMsI{N}HEeCFk2P@f1i)?-;aXLGt=!LJb6>F4J^ymNE%9NXe1kjTqNk$Q0!hkq6? z-%0PRDTfw6 z?)3Cfwa}5e-(YY^DfuHWkFtVE*Mbcadup0LeBnK2Zordno`0v-x$raR;hWk#)!P%^ z+-)lS#B=zhZNtsaZ)O-KtBHw;dCfF2H9dOd2#d0^G7k^W47+5pb#aY5ezt8sadPs> z;N{BU8UkFc8QIy3SFZHDzpu9P=hNwx-|v=7S`;WqNl9g7WGq;=Y}u4k_x4sBr<@RI zYHHe1`}uk+uoyl~B@+FN;TZ+S-cF7Ik@3Zfq}S_w3v#qxq}pM2AIOU|`~r zhJQS-{+b0X6yx;#ctrL1q}kJbbDT7O^SyOir<{7_z&?()`cu{Qf9ua|pL_1on~&&vy6 zHq6NC6zG1!!D=K@lq^z|%(AP>L-f}R%`X?P2P<3`R2J&EDDq;#(#wX$#WxB{S)3%R zs?sDRdfe2E-So^|ot=+OO5JI(yYh2Tzj@$wF~g)KYErYDT9`E1eyOd<=9p=h%olfW zpU-^T?w#D?52PhpCf<}-v;5qVvv(&SWWF-NLzL%@eZ$GiX`6CyTWu0j+g$tF%ZR`8l3qPLJiJjLQ-oQV5+xw)Wr%IRTpG|w+xAo4x z9i6)#rEGhzr;`4c@15;0z8{T!N9}jNeD?E?clhSx{qmo$UcVm@T;6ryM9Qb@IS(UJ zb!TsT`*V5F%z0ZUZCJtbX!0cv&VEG`qj@WKi0t{>^ntre#lV6|{(nGCvl1s)_rcrt zJm(u`^tH`iKKIVPgM~Y)S&gn9Yb$(wEHN?h;0(iL3EQd?@tGzjCNBQ|{IRjIPl_z9 ztgKF?7#&F}6rULq9-jW>#Kb+nUakJ}{=U4kv-6W8%cPAz~HsQalf6DzkmNk zW%o_x@9&9UGx~e$k6?bv-ICAgPx>qL?5bsd{rmmC;>QO&ez|QQ&u;GrC0f}}Kh8Se zdcXa{)$9BjIlImtcK>eGvT^IoZ`EsUcGUO(ew({xtJ&pSmt21D4ZHPzYtidxp|{?f z7O!91`D~fBiTT}yTbDLITXy?ScJ`93OBf z^ifS$o8LWOosCzrW7;)=rY5J3u0C}u)tPJd#0VJ+ec=73u3^Hq(q(>l^`6eomlA)N zYdLuM6pC*KExUDQiT`~@k0rZKh_-d87L}BK4GgTDd1Z@F-_-EWb?f~*x_p(?>{!{; z`GtavPn-RN2VduPY zr{&hEZHHS`e$!u3Y*;7dvfAo>BtoG`9E4hraga}{CdX>UF)*V ziJ$eRrRu6H>P_t0Rq{>VQ$ zD???g?d;F=*|Oi*Q>&kO`jbc0q|@vEw%@FNaKe7-sVhr=sw_(tmYeI6;v;l0@ghs> zQ31KREzA8G76x{*v>ko?z2wKvcn_tR% ztgWs6^5x4EBh9H^6FpQoTAf&!7^9-1GBPp@vaV>j^++_%nkDu3_jmW;;Loi=KVL4N ze`$AlzTK}E%62~^!-Tgmr-{1H2;9;|O z_8Cu4uYdRXfur^J_xBg?-P^luo0y#49#A7BBRl)yy}e<#Zr{$x%UiN?W#hJOVo}kv zL2Z+*TW2m?#&+xd)=M`E+P_t=-C|;X>Dn!ix9?R3-e9bI{Or(oGhhgDT(a*^+I~af|aUiM>rUn8f8}c@NabKi%@b`n|yik@oX0t zx8fHChRp3N1m{obJG-V*=j1tcHPyK%cDSfT`8wwAuqu+Rb#an3n%g%&QNbW}%C;%} z_OcBVFHA8HiBs25YMP>Y`bYPv_{y(a|GS5jFnOMo+_9@tSVH97jE#>2r585FBszF_ z@bI0JTauP0EGFxp5u)?bd-}$Lg|b$~e1s`|hPY7u%5ZRBBqP?(w>P!b)!+MnwC% zMIUZha>PW=F5~^2kgLAt`cLH7rS;3(KYN|O@8OS=o8R2|?K(a8_&nyE`+qH?%ipDc z{gwNp?dayb(|TQATJvtN_`D`WP5-2-ohtYL1qMxrlR7#)l?+U}F1!e7QVQni@laCH zn!00(&YoJPnJKxwOP8uXKGyqMXzm-IQfX=F@`;|UP8VI(T$Kc!77FOnYL%9A8&Bee1&%zjRw9iK4IU~^&d$vTo7q2~RG)ugb-2FWpAXLW>i^f?tNk8( zukyLr&;en>Px&+jg60 zL6Ga>)KZTt1-YD3vy@zdoL!Whuef(!xNyZy zVPcub{D7uu+gM6V4V@M;EezoI_0_a0Vg^lMh;rTCTi}%Ts=Ybo_R4uiqZ(fw3S}bBqOdowI87JCE3Kxy9>N%?U2ODzkpwgD)SY{N&d^JkuEkYKi=+ z;1xeN=gFa&ljqn*9{+h+eZ~HQX~ybVQvlAk1J~Pc`pA60^DB8CCPk;sccD9LAWR8FO`g-U8uh&1Y?5qFu z+jM=|=i}~`qJKWEUT<0d&`9-Z=$l*R8@heoSw}4C3CX)(+>pEb+ve2gcGvQ6dcN9J zvuM(^P1*kyx4pOgusLYvjCsyJL245>u5>w;E+9AE(J6}Ww}fI%SblN1KFi49x_Iqc;tvkfmL$eox45dl%N=2t|K_AHVe!JHogO-_ z(H9hF++xza#iY3`E%~BY#{{h>HD(zapL4_~&6;^kqm;4u`Aws;I})HpRh=&-?%39B zkT4hADk#z+Q`N1K& z)Lm0wbJ3%1t+Gg zIiYT4C6>-FZL?RtbJ~EE$`OB!}{_cp1AL7pI`oY^NO0UU$4)xC|%d> z^6p-J$6xmTpukY~(6fc#WG#-|V{Tuu&id%VML}h6><(O;`}^jeTK(DE-YV9ARoml~ zbW-!qfpxX*5rI2CwgySgpXKXxt7wvO_=KG`s(J6^ygF;<`TR4vnwyZ&uy5bKO?g3T zlNG0W?Y`n$wrtn@sV5T;w`rypJvh+FBVpj6Il1=NmzP2_|2$~suXw-r`xK*}-)`rB z2Bpt$xAQM=PWS)yfm$hKyL$RCQlQ}rcjvR7n_nh|3XXC7n&WW0rbJHHYdeih& z!kB4KY~&^r=J@#%du)^?m+Co0Ya~oOEE8iZwlH8uVn#w+fP$;ubmi&gYrK@jyf21I zEL^MVtZ>2AeckyBic0_P&;C2Xui>sZnI32YYF$ z?~|ffyQ=rKdzdGAdhn>JpX``pQ7j{_oAS=aTI|eZ!|YonkBo#o{QJ`<&Q-3ft6Ot_ zM_(#y^obKE7M3hmUw!HBhRqufmTpu(xKQ!Qx^u@4?^jmcci`g8ztz(={r{Y^%xC-4 zQ(v=+J}>h0@jq&NK;B*4zl~S6(L>3t^n}5&p3Q3Yu2P(hZEtSuiJiI2E;Rh^)AMr! zPib`NQqB7rTDGcU!t*%cM7dO0UedIsLEMHRSr-M*)G7-kZXOB;@+%cI$r@t2RvD z$8EOx?Z4<9!fHN0*k{U^=fzClb#nf)U8SX^ff^#QDVvJVr7ZVZwR&~=#FGo1 z+sh`NJa6}VO}7u|4DTQ6fn{pUNdeUopB&`Laz&OFnqR_^!rJQdX|GtRy?EByIKQg5#H*{R*n zLGu#N{bZk?I$9jFw`88S`1!)$zeLx??mMvcwD^vqk2306T2lj0TsvlcCEmjO@mpz7 zLHF&ew#mJHj@91Ey1d&nGk2Wb$?aNUu~T6345x>)rk>fnHT~+dwQuh1oGrTg+M^u# zBQe>BzsP2y2G;@)yc-!r~WKEmvd`d_gU+2n<{_DndRL*+oroaVtXdL zHrKVyH;#61IrDVQ-7VcuwXaVO-!buNnunnhyur zYkoXz=aI3PAZJ_k0W`LID|@|W>!xTe&~)GZzWcu;{%l?HX}b-7oz~Rvx1#20Po10e z^TEuw_1aT)eb2@&*|hN?UwK}d{p(<>4Y z*?X|;qLa??=*6p6b+tX?IFh7!&o1xDv!$DKw5N6os~Z+PaQO1I)X?OT>q2c;jVo!# zdLsWkJ-h$YC2y@gOFn6dEneHv*{5P@Icv(50H;GXGgD0GCLVTI6!4npC35O&om<}p zm*8M&U*Fue&d`akq?9gPa|{nZSFrV<&*qt}cazN7R`)w8PEAb6=zW{Sf2nxdrmD|o zMcn2S_vy6rN}ElXvvEq#9MPPc+ow;JHlMh?jF~mte3MeX=uGcTH@B4Xdrw<7Q9}A? z-`i@Bb6!cuubrRBxm4qf+QX;PPXFtgF9>BAmu%1yd%S!@p0Aka!L~PdcXq$(64E-= zwL(jO*`hTaU7M7$R-an2+V0rLSw(&!;Q`T=K9YOa=x9GX(-}4E{k=!$dRLp=-RXIF z?Q2lSX?D2&j=}kQIhbbxfapapv2S44( zICpLBvNcDhU(GHCP2pC*<$u6w^YH!u*}=PK|9`Bp^wK%ihAC(E^lkb2DO4r>iO}3F zQzpC!sd0W(__;e}W3|DybN`O0q;07DEw}B>jq9OW=I>M99+Q2t{+@?Niq5ugeJm`k zN!i)oV`jb)IhCGz>CBlm-9BY~9Is^fuSA;P)SPS|Zf==zL4l1=X2qJ>Z*Fhbul@C9 zVfp)e=Y31}|9-do%lrHD z{`mw+iiv&8f3L5j^XC4o^zU``zyJQbcl^=*>bgHt>mSEIT^@Av+O0Fqzt`6MTrszu z|Gl>P(il5;t=se0ZvNA1I(6>L_s801o7~xZcJA%#p5A?j4%hNsUcgI`u z7?@>Q6aT$qsW^A|e61GuGyS9o2M+3#%{e`%GWf;+n*ud^YAov(b-cRu*JoamXTk*j z$H)7f{m(Z9E;jk_;lqN2t6VWL;==OcC*Rx*E+{WfJUz|y$&=QA4Qlh6+rJ5m&vvd>D!x`po38l9de3^ojcPm*Qo6C z^91{PyUxs+I;T&jW>&HNt2?NpeL~4RTXx#w-Tb^zJ~25R#8=V4cnp#kDS?8XlO2a z6~U?M&R6uSf>)P!`_Agmvx@pMyg$cEy}S41-CXHAdkR~#!}Tv-shB-$*WZfWRYuhZ zmG0QYoYar;zP9S>nfd!Cc$yh^_LV-r^paP`_L1kgu98pO=9e#3txq=m`R+@(xqb7g zf6YgaEKS>7n%=Ik?&glUdk*#LL~m@nvb6HZ(iFj&6Ffr>34P+eu5Eo_``cfC>X#JV z&NIuodslJl?6M72ht0y)-u6nm736c~jG?^ktd+sb%RI}=ule@LT8C*)K7Y;E&N_cn zuv)!U{->LQj_18gzrVk~-+R^W^7r>@{(L-sO3?BB-tThn_y4y8-Pg0X`uno_|Nr*< zd^UT}*K5(SQpNLn{p@y^dOyuC7ycjqbF;^_`MLW+HT3&=^81gy{t;feCWrC;gj3h1 zslD62{lnA6WuUT?Kk4TO$&GvduZj4lzjV{XcfEQ+FW;F-$I3Du4q;cC+;#ElRoC!^ zhby>^kM&+l{gWZP%y+v}czEC2MKdL4o|w=j644`h{P}OQjk(^8adzL{+}&lcZIn79i;r(kxM=c7cv3{l^P?9OvpQU@ zK&fupvK7ucu10o$UL@PSy)JK?%Y3CR+tA5bc;k}AC0R?_BswGbnJ4zd$$vY3@p601 znN%Hbj)crb>>DLLUDTIvtUg~h>GC4ZZobsR)6TAmiS?-QlJR`Wxn!C8#Ay@HU1}!VkKCoVWCUxz*ar|QQZ%u8h6B`$|YiL_}yqu`NLPvMet|uBx-TAb(E62Zi?&7O@wmkph zje_pe)6Z#2SLVg6U%v677--#m>1VC-_jevd9M$_{`={{pH+2)67E$f%0`q2^Srb_t zvEznhaP!F<=jSv$eshxN<$Cu57asooH5Urkl(K8>{wW#4C!?2obX9N8on5m_XP0ft zy>u5h}QL-8-5RWtz8qfz4htO z?1F-#^cx!<_Q+a`NlVXues1=k%)3uEe^#?j{j~hETK%k7ayAtP&1}4{lqT2y{`OXB zvV7eSMNndsulc|Tx|1nYnZ0az8uFEA{y|rIjIFPxmWM zJ-=+f_0mu8=1MP7b$|HGs=LwoilmiOvp+j;(v*m9$HVOlbj1{fUN2am-sYmT_`3Vw z4VyP7pL)`n(Q_%~*A-3?(Y4PXh-ViT%sPImDJLdIHe#F3JGBH428QoI$O6`g=d|)!O~+shiKYE4=rq`>;l z-rA1t(+N#iU6PD8dz_4QJ$LJDMSI)infoXG`TF{VW{h*0qvFPyoojT@C~F+yJ$IM~ezaltAm%3YqhM%1)Et_4t-Q@n#jSt0qd$(t1 z?eRT)cXrI~s?J}hpJ(P~KJd60c1LXY!)LN5R$T?H(esb_Wu&CE=)))ev~z2AK1s2d zwEaxu&(C6$XKZU}@89?EBftG6*_CgeJrA%fSTSX(czVio z_R09Yol|;pjs#g6$;z%-v*usoy(gQMo9e7Ug`TOY>FyIL>8o}Jt696Vl%7gSznik$ zr|!!|chE&uUtV5rx2yg2;oI$eZRM$ROQ+gae|yqvey`%)(YlAvZeFPkKiS^(Js|Mq zv{Uy#sr_63DShG^``dN(?~^~?x3#b9z5a3ir{bWWPp9+O2d~V@&8xK9{^@7XN~;nB zX7+E(yr$`ue*6>=w@%vtB4)nUs`lJM+~k<~R2@ z&pP^zTO(qobHN7l+uPS4y;#U-W+q%vT6iGqk%iLYzZ=SDw0503z2{Gmf6Awe9;Ihy zS{2)U7C)apaV>Lkn*M_0+W7|A&uqkWwz#`4Wk0Ezx#mRl$%%{RwzT|uB>v#^xxdw& z9lj4H`?D(xzFxRg(`~`h;}iBV&)9uoTF(`WMTeX`z5G;_?U?wdEBs;F>){>mmJ=dY z{Cvmc89Q1&ypT{z7WVKsdFbHf<}+`uuF;I~cPippZ6+=tIje2{a{bHCEFV62bNImd zHYE{H4oQArN<9Tl zmmUq7F0=I03&B;Vx>j0+9hxb;_0HbP-nQN5nLDb3ErYjgUF!I=;&<5b{nxL_svI#F zN$0n}wl3bi=vI)~+kbP`MsJ@yYv$Qwx+~|{Z=ChmG%)A>&e#15QW+Q&7(87ZL;8Bw z*vQ%J;`e{hF275hXR5&)=Kf9vb`n|L=xnQ>RXq-t@kpVRI25J72+#@|5_L z``=?7B3T3l93x#)LIuC8p4FLt#pm?5+GVM;yjHKgW3)B*RM7kT@+z6WXKU)>pHKcg zvpD|x=Sj^~g%9?e|6TeyO3r_q{h!Gp|F*3El)mzgrQXz9=4_AiCl38@yLmGH-RzRW z!k^O*onC)>$<(P=eT^y~E6#cSTP^+EoRe!JH*eUs?b$(g`IKj8W}ca2DI6PncXpbA ze8mGsHUD{kR@Y88pSJEX&zvsV85QSQKsWQfc=2Lwy<^@%g)i@AO#W|Y`&d8qzu3uN z`M-D7f7$xu|M$Oz+WY$dTZZo}dfNW+oOyhlRsH0Ee@2@>9n$|6(s!KuZb8KJ=}kXZ z&pb81<>{|^dpD=sfv##zsJyMj^=Z{;=~wXE&@xzA2KY@Zn)d*-3~x1i|ogWqJOJ-qrB?^!e9-qg8Mri&Ww z%A2v~j7U-OCCNx@kN2zc+}zz)tX-|HYC3tzlM*rgytLojbb0u{Zp=%W@#~k>q|3&~ z_?E7DrEaI#X(@P7=Y5>#-$!C=a&rQ;WCE>D6&8ItaxbiLf4s(5?%69=u3WryY3I3h zW^8S{JUl%YZ`d&5=hVc# z>*h}RA~Wk&^^EY?-lh92zioSGvehIwWXiDztDc&K?%r5^n=P*5nSsB}zS+zBWggs2 zZm)Ru!ZEJumxjJ=?Ud>L{hUv`{bip%ect!^k+J{BLm!j>e0^(Q{O*eSoPST0?f$)} zwfp--CEX8n>_nfd-CWDRlF!f89ZR2IcjPg<+2m={+|C|S?a8})wCiAEo10lcM2K5d zmeJ2v);ITWpPc6|wQ`l-(nV>S_iL(Wt`6Vld^yPd@cV^d4|euPUbJ^Qe(K?DE#37w zK3a=|JAHH%j5apBc%l@>oBQCz#`K%@FJ4q|{QB1ZeN*oBh>rn0H-Tl3!~|KlolhG@B}2;JS1|LNF=PtyDk{}kO{vTvWAo15F?(+@5$^L6(3 zf4{7L_3G89K?m7AIXSt$G})Z*vT~Zm6wmK>3okz}Eh!0DeYNJxMfd;n#Gi=qIQ>88 z__;n?;QELAXXR^k?`f_4bN}Ie{i*v!PJ>d{=i=oSwOKw|=jZ%uId5lu@Y6QeC(qT4 zqR%sEXEO%Plrc9quc@uwxM4%WwKb6v5)yw(^ z#@d}lG=aP zBP-KaMOnFf+fr{0eeFfdc1@ZxMdjJ`YZtd}>X+ruU9xN4q)$IT-}rkYPD*ThQkK>M z*Qc#6FFJhG99M=YiFpfl%Uqb9>l2wN`juP!@!PlE^XJQJP30=zs-3q^Yme>Tg2F;) zjj5BT#`}c&nr2`7YP?=)cI8sbHE-S|Cf-eYcHPuqPa$(?Dff=(*Ku>^)>anG(%X2^ zP{#6yK`yuRjm^2@=WR=8sDFF=?8d?474Kd!##OvhSa0`TMc=Nr<9VOlgQt&=SNwY+ zoNPS(yv*aH>3tvH&0hcc=S=?}uO3;RoA+x1_x$;f4(+@==f~^aHUH0EkFlzs=5P0> zVe@jkXJ4=L1@7N4t?7{$+qIresn7j>wYz!vcqr?ui>IfHha|B-JJ)&Ow06g#O75Sp zX6rw^mAWo-PSD}^3rc@}N^QHAn;7`IYxXO7&!=Bc>8yJA@HpScFQ4`AiZt!}s=iW2 zBF;N4&FtdEi{?*#|1Gw=yI(x6E_wdDde!`*Z*Oi&NJ%M)b%Xl{;o{89%sxIod9%)% z7#d2RpYvz!!GrbNrK{_291S^LecXQe+O@u(o}AP5Vt*};-+td!d8*XE%Rd*dKl(pp zrOp4JFK2%X`DeZK)18~=Exz7VulaTI_~W3P_oDpE(qw+IxxJ87(;?#x5y*_E$O-gR+fHMY2NxapPAwwzf1 zJ9j*|xHo0Sgm{OhYVyCm;Zzx^Sh-WOXPxx2&8z2{X18hTX&yduMMyZ8PpxvK^_7#4Rwc;Y+te=J zZ+Ctpvwy*#3xV^ji+a-gW&UN}{H>ucYk#QQ-}a>Td7GoBkN1E4^f7tQze~k`UQQLS zc>MHv7^>pxuI{{Kw%`#mR@%g=pqcl-SZe{Syocx2((Cn?J( zPMml8TWr6B-Rz($U;`a3QS+v7o>fztw8|(dbPg_gBl$CjS=H~f}XM(bRq-WIr z|C<{2r8gjO_mgG6Y*+4y^L@Kl#-?bF?Qgz~WhKIE-=6mH_LjD9dHnvp{lAaBx?=0y zS^gSTT|bzQF-; z;(Q7NK=v@GLp|Lomd$;sKzj-?fT_^7)kr~BbT zU;d9@4!Tzq-S_L0t!R1N{_W$H##$Ns9~S5B3R|A{$vobv&j0z(MfGFT4$ZebR_rhP z5L7a~;y(Z3k@4}0w@)|PoZkNL3s-&BSJiv9ul@J@{w6)A;=S*=`F}gE$5}cQC(CN3 zo1WCnJmcGO{Jwt6la`Vs<)jyHGav0W-~Q>##5A+~w+pw|Ysc4qW&WURA9L+X=9Le( zGity8{l2B_%jD>w^5CUU{}r$7@zGH*%$D0(3EJXt=gM+x6O$>H#m^FwlZ!8NX2-_H z-no0Xu&C(Q-90CzQ$JPBe;0ph$;y?QT&+yDwzfPxJR7!bIdbgSvt_o^uU)$)Auav? zTb*aFZqj@n#gxh4?PXN9Y~5N|TKe=bzx^IX@ps?ckV~dNkN!yR`}gY5dcXSr+MB2D z=W+ZWzU0%dxV;;9%=(_07voS9V{l9@?Zr#!q!gR7{(bwd|NXUU(!6&!zAfolQ*p;O zcBbj&hJE||`sC|7zADdsBA#;j`hDk!3WKb2QMq~RjvRHHFnO+9U?SgavvtXJp_V@@ zXRe7gKJegzR$E(}hKA0uBUdJ68h<^huaWfm+1WL*#wX6TZ`)MyF{woD&Ox16XCEHj z4Wb=}hFcF^R-Z8SuX9d)27C2)Tci8KtKQE#ozBgy=<5N@bTpSqHkA@ z*L?fvY4`iblZdjX>GFjSKl}gubEaL!ZcFRW&t4l%w*S;T=HI*f7JK~qghS6<9z7R4 zwd){L*!R>&&&3@cPjWmr_rHwE!RJ!{IHT^@ezz_4ja5mRc0Yvm>D3muV**l>zwHi^ zoEg!uzFuMCM9>+60zyJf)2E9&EiBmc^&vCA%>mo*cZ}tf>@qK@ym|XJ|J3Y1;lE0x zB_$Kn(%P0TRlRoY+Kkz=lXG%<929nVdgSKj8k(EChlhWce>Z*4#&t^D)sb@zVlsryZ%f1Y3R>3ULS z)}C{7{hV1@ zLs;Z?E4+b`U^didJh*^&}*=U*&an{&(R)T)1RZ0&9ma*@X$%(^-y zRPRvEHo-nWE}q^q9i5#IJC%38d_BuM)RnJvs;bVadG5)+tle4KE7xl-&U57KmR^++ z8RPqUQkvAO*DF@9T~-)q>m4;KBQws&`>|5gY_ZFCt~hvqPMWrD+lD>0lBcI{-MD1w z#IK82-`r7XdVYUF$I)&n!wcrePiN~~I)C`Cc>a2Yn2>iZ?Ms!-zB(no?R0wQ>K3Q^ zT(5@n&kD=GYafgL{bJF(vozMFBFR1&^Kk|fK{qLWOPbr%=ZL%nRbw&R7qdV7_ zXR`7Bx4qN%{;0I|tC;DhxZ~&ibzV}m=KFWgY1?(xv}^U$mOYy>D>5M=qbEjB-rmMC z`B=}#nO?@i!CYOJCR%oU_{jHU<#N6c&&~Nh99!KUv1P@)g$o&riZ_4!`dLNcLi>sn z0bJZg9i4p16)XDx?9nOObou6%LRnkeyKCb1&YE?L&B*Ap$LTYRw&v|Owb{g}Gu`9z z1fM>As*{_YmQbMjd3E>4FJD}i1}O@12cAe>5qr0wxY+&OlS5tm6x*(S2@HtvIz4G= zPtUs4N_X)fcF9)Pook}DcCEQ1l+&|Dpet9fGekl{O4v%hVdp}wDPD@-4sqVN?SJS> zNcYXnTX)p25))R-Sh&_SEGo*%XNE;5r?C3MrM8T@Z#iGQ@Nx0>X4lo7D;@T*y?FWZ@1J>H{rMXmKJ2dDAe zc5KHEi^uQYwe=oox;0D7YMTC11^e!0N54KvS-yDf($>CjH_l(UaACrtM@33Xk3W3l zUbbmhpQLfxhMGv}ZfWTq$%kB2k}S8C3FWL+y}_OG;`MA{In(K)tAE|tQfu7a_h$JD zllFrP=!_`@o449e3WyWUbELuxW#eq2knC zZE3&4*QbAdwN)qbl23WLI;Ur6Ma3>5v4_sSrL3>rw@sQG-!iq)v}h?u=ZV?Y?{*|gVH*eldOigW_ zy4BRq&aR_w*4#aY`Jay2-AzkNyK(#W<0nsA)~(YM6B9e}^waFB`qd#?GP1IUMn=0d z@6`PI^73(zW$LLZ^*`G{b=#dgcPid)z5e52`|b4eC*H!+&9}HcMNf}S{Ht}p{(t$t z9R<_QpP&Eib-v!GtKspz-cL&_|NNTW9rNpxP5SxTV?Vpoca%Ic){C<^|MGMCj*^cu z>F4G?`yw7+@bZeQ-W=<*;_3c{|IVb_U(Gx>-}3D9<^F~5uB6Yg{H>XOuI^O&a=T|= zX8Qm5^C$D)x09ZJbwA`}=jQs%HhWF zPMXuPe7W_xbLU=2&nS4mce8=PhJ&{+v&ZM#H!(4Ba&jFze7E;V%T5*b-m9O^sc~_A znz5kRylAC(AD>;vn^2$m%lfA-mE|*1-L;u#f~Tyf?@1LsGr@&>MJ5Kcg)BO7&#%j8 zl}r5jmJb^ws>1m$ZO@vas(YGauCQxeA*c4QT`M&57i?YIckGy#*7K{zd&*RIe}A)N z?c&+9ezD!Mnzs4tLRp=-ONUPVvzu00Dl8}K{`*sy)h@BiSMT~&M{DcqZjHD-BQPlJ z)U94;5wWSs${U>{x_MzC@ANY6E_8K2uEoXWotZDK zroO-DT3_O&BJa%Ton3P-Wlj6GMqz)A;@P>fS4?!%GtYailA3m5-xg+bbJlNpae=X^)duhOPei;e*4{px&?PTer(kKl%FMv+GMgxo!ND9<*}p z+OxIa@4DyY==}cn_VMrc`TI5h-p*CHyx`&fskguGt$2D$bY0xviKnOUfBb0a`JDwH zW$wrSYs=sFvki13#_8$$pFxMByu1`%^YiKTm|b5)>;M1$Ui0;;c}?|0k@)(`R_^Kh z9<273|9n(@{>Q7^pYJGnCleoIbyj@(xuSoMX3l=~nS1%qKZi2U&Hr7KGwstC$#Zip zPwzBNx2*ofBYv*_;IGofC*Gz%TwUg(BBBs_qA+Zw<{AHGD*6qTH}5T3rlL6YM(~9B zz7Z7$=gyt0sQUTvy?opJc#C)M=Kc7|Dj+B4S5#yqCzn-Jy!qvutmP{#`rpk9Gcw-% zvEcjil`AJcjWu2KX8rQ@`@5I4MsOZDzkGmJgc;@BVW$~9gDKFO0UF}r9 z&92B@Uq>Wr-X$Bg@ib~ znByTO^?YOIYb_(=PaeJ}RjkeDEqpkYhiARI_5O}ox7apqGMZt&e$tvbKA_I-3Ec(h zsrH2*H!fK!Dqw8hv}B1)(I?{>`x6u&P84iu`z827-l-v;MNjXZ%JTG&SIy&@XZ2Z0 z)y=>K5xw(z4?Z=NF6%`a1 zcE(zI zwZk&U!}ZeJA6I{Tb#?XQ{U74@{gGAouRHKrU*`GC$NrxVb+=c(db!u;_Z!D~mCrci zDt}hatNv!;FJIjyKCgOWcw9}JdA$9(`S)vE?f3rWQNLF;lmA{#_xJmMh3jkI=iaOP z?R&54_4&tN&spbxJZJs>0| zFW(&zBOP6zf|8&GW#84*iueK**BxQoAs{Q-=91$clcRD@i?y$>Xo{cK6u)&AtM3G^ zj43RWd%dA-@0~j_6Fgj8+}(|~?CNf6V%oHsOX9A9!|_u_*K*^|oNK>+`O1yXY18Goyc7ijxE#r6}EQal06Nxc17(9=QG%mCOdmuZu;9_ye3=qQXTI)h4HRC zcJ(g%*4^rrmZB`IZ42#hGfZ8~zgN4xv#0CeVeKh@9vL0s>(hz5;~5slIrnaC$@eu1 z`s;fREh>5?W;SVlyyJZb`=hF&Po9Vc?%vS$=MPtpPY-AJhhz8ei-&JzG3Pfdc2fHI z@uRbMboYuA7E#l6)%P!H*t+;`N!fLyP5W9K7p9rz#UA{$u8Qy7{S&9wrODmBe0*`bE&%N|J65F zJl$$OGyeWL+wb@KU6dTvCijL11PF9?cK-PB`t+ohPP3`er zA{N%x%F|D8+_XvR{lnaZgb&&)_XO(X&sqD&9b7(MSsDEC`}gJh_s^fK?tgF9{QLW- z-Y!(Qyuk7B+k;!PuOIs!8n?TwwfOnD51<5dOnUvtPoK|!yz^1L^5;?Y8{6}BOSy$3 z0<7C-wK1&9vhDEtrmAf^W6BeTOG%GA_-8wNtT6Ws+^BMkE6&(814x`qvB zo>-U)Ip`QM5N_%=fn}Q(}*?i80)4;Jz+E zNaBrqg^NgEpB!kXu&}I~#|C-7b46Q9w-}h2J(*Yi?%_Z7Wgj1Gk-Ss?zu9N*FNR&a zs(j{JiOR~#o;iE=XbFSnc2Jf zRz0&5s{dyd*IkH zF?M#=q>Um=7jw^i=_)c!bU|^u*jrid7jIT9O|BHoUHi1B$K>M18oTIg`pXxW+U?#} zejy_I(6X<-CfBDM8|~>>_KPX(tFGhIH|O?L@~(LyyJW?hIr;zo>4vZSyW(c>%7^7U zRnrnvQku4IHDzi{*wf;q=;-LU;%e5NyLXTCgQowQnwt$xOpaW>Jo&|o3>zDp4I4H{ z^z-hDYinzJoV0TN`ugDKbL!2aEmKd4l$4aruqxF$dGh2Oi^4}?HLImeGAkH6028V|MBMX{)ew0JHD91aOlt^m_m)F|F*Cf8W{J58tJ=7 zPTjM6B`vjbR=lrgrX_Q8_6EU@f}*g;Z{GCyyy^&9;-D9-ut6{>C&#y7*6$aWi<{SSrc=S%y!*@=nY7oE4_-cO zZ*3Kgi@h!{?T^ww{PEASXdG=GCC$tzP#S$)Al8w z5eWwWg{aQeSQ5eU%wg}89C~RC8wviuUu(Z zRmCPDC6%0%)V6V>CR3wq(N5)M%bwk-ejie@Mcwf0zRZZoTZ%$f51zehUEZ$m=H`}? zw(RrC<;yERuPwi^GuwQAO;y+P_=?WQ=WUK}T;BKj-f#7a?{~jj)coM+x3BAHm#+g& zq@KOE_xHt1mk!>%$tuoo-{rOXUth(Y;Gsm+00wy&dL-mcr0!6NA3^m+PfKwy@i8Elq(zR(sEwaMyG97>(47XXI!hB#OBSLw_{a|zzps{>)nqPf*!nEbjbB-OVryg zr-cXZ9%kJb;Z@sL*?Jb%9QZ`-G49&Q&4 zeiwN!`Sj`O>4>PPKJI&0TVtv3xaqag%dFVd1fR=NR2vxqlh+ z8eHG*s3rRUiqmx=WoKiaFTNYLZaseMmQ?GZ#2B@+XLUDj*l_5?36_NcJ=2%V3vHBf zZ{-f^6y`HhpW>CSA|zO}lSf-yI%tydkK5|=KQ6c5e>A!Jd-20Z)7M1q?n^ywU72z| zS*G%ngtPPNoBRJ4hOLS4jK9yI?pJdHlx{B0>{z{8YU80s)~($y-^nFae6_lCd6TC0 z+Vp}7Zq=usXYOCncKu$(+*VZwm;#IJxPY zSQvllr8^OakNN&BDKWilf8}V`yfU_S>j?8*vqG8I=G@;nv+o_-x3~2z*S1;BtN3L2 zEL22z{sgCz7%ooDmo*=ss;*PDUr$FUJ=W~ z%fs{G+qYvEE)@Jx`M%#Ox^i)dl%%AfsOZryQSFLfFPHy(wR(Nt(d~;Io7s4`+uFZh z`lnL!6R2??=zRUhMD6Q(#l7-BPsiU^G`DZ>S>wah+$`XEWX9{;Nm(gp=U(>h__A%m z-a|9b{Sw&Jy!OH@BU$HKW}Yu!E?%D7J8zv`)vjM><}Gc1cjo4qSqo2ZdAWJ!((Dsw zBH3P?GdpzT3fICDj60oLPaS1u56@rI>LkKt&h%H=(8%b}kwZf79|T-!yjM|KnV6H) zwX#!H?bx)4=$nftSSM#^cL%LJaO;?ubfc^pleJ5drqe{0somBG6(2@K-d3Ed5uh>U zL`qRgkh@4x_!KVvH99Fr+)@i)eTj^#eamZ>ck9s2;^&rCZ!+cVD`uExU!OQV{=}hH z?wh;I^{va^BwSwR3)*Y(=Jxh}PGPkLAzB|l@Gi^GV1DgpUhw0E+$ymroA+dER?FKv zM22df^^WrJ^L2XruxWX}bg}T_o4aa-v#+mRxOnws-`V8``!{t)8utdAiY*XWJ<&5p z;6@kcyvYu3iHt%!t^d{@&bhZ?+NMP{V%NeAH)YN^>b>iQyv%~~1yfsEcxFY5U%YzR zE%POlQL)fO?I}A}&owkPbJh!167v$c5s{dc)Zn9Z)bn9ivrlV(zxZ6)(-kG}tyZn> zzOgZTLTmT4&4s_CX3c&j8F$w+I@|iLEq7*S)TvERO?6@~c|}I@o!%ujVcs<7vNB86 z&srX*PCWRtv^(Z0*Q~4gy!(&qPCk`+d0F9c+47$2cgyehM(7+na)ia%`S8a-Co(TD zd-(EY=fsJE*REarAEK3?b6!J)>*JR%DHRnn^!NYy6lPP(D`k@L!~S@$_ao5S?bf5N zQ>IOyIC;8vQWjV7von(Y8MEzbt<22KZrr?Ccs;iK?v>@$*VaTnzFUxXcGl6JNaKrF zXP^DscKM>IflK)`Mm4c(EXR)sN=Z7W=5cWw|5>@_%;I(HntHyZY%0Fg*~6!ztK1e^ zJNL=+=Rpx6O;firFgqvbknivlzRIaqGoCtu>Vj^-?OQu_HLfBvUm()DQ>6};m5wMQSb^Ix{NIB?*C zLi;?6GlzG}Cx2J&|MW?Fec_)Uo99@Sw*CDr@9yo*KJ}Dn_*QA(w|jM7ODDX%BsXbs za)FU@iQPTx`Mm13v$M@NmA%ah3YsJlW9^lGUTc}} zbyvl5w|5U(nteK#w(2H6G!zk^nw*o;x_-SmE9++uFMl=L-91;>KapeQTtrb~l|k;ZgLt>%z?ao=VPaNg0|NpY*+*|Se%O8GwIL-6vm6gHyR*^P^k6c)o4pwv@ z(+pnLGky1NYd1GH^RHk2=}zR~=1zWjX{n4&#ROq>zYmZ5?eBFhUjr&`{-^IOdwc7L z&`)>yT9>RUsijX-RhBQGIB%|NQ5IL>$KsD4KOep5+1b06q1c;y4Xc^N>{$s}S$zu^ z8nUsuy?8C1l#$W1Z=a%Z+N-3DltVnR$zOQC&M>Ui5_`7nh`NfP%p?CyPo|B5Mw>QI z&dizP?-yrubZKNu+t0~UWSoC*Qu8|f>BL#>1jw>L3CWoW>FP($`E@uXv^y<4ax1D~ z=~7dsMp>tY64KJqB3!EX;{JIY`82!fjBJXRNKv{-k*Lr{ruzK?_x?XnsjvKQdu>hZ z@s*d;E1zDyE*ZCZR_t#1XRXuYQbO-Dyp)X0xx1}-+O#mc9}iU6`Q`pq{*2Z;a^{GT zzj*zkWT6e>w-1hdysco{+mjB*d&bha%GxYD?k`mqJ z%kw9kS|z1(CbuADhk}sRf(fe)%r>fI_^B*a6^)3zZ5Vxi#=CcnpSnIr-n;6adpO9d zy87j-cMB^VHDhIMbuu%LxUN0mr?4n5QTg*T{e**mthl<0JWqD1Dw@vg`dJ!s-E`xE zWwXw_v8egm6u8=U!`4kI`o_J-c4@8B?g~8dW&wkCr_My+}$ny<01QwvQT5T>>s&#ogr~Mi&)ds)75-u zl}rwqHG8&k>8p@;yWiWr+w<8^&2P?$7Ea-U;$rFZ^Y#QP>FVlE^jg}|)@Ed7RdxHh zN7e;Um;cm`kbiHJFBx6Z)YMirHgEs&MCFn2Bo7ZCHBHN@4<-cs`NLdTTI~L8fy1)} z4k}X=HwY%>rnVn&>sZ;Tx=d{vD1eHm*#&Ln;Vuqdb2euJ#~i^K-Gv1c9!WoGcMsrb zWPDgr{rl0|x1JRh7JNpc|Morn@r(8G)5pCr3tcoOC8X*)Xie_-UK;bRBSPosrAt!L z(bZ3i+*O1mnHpuY%=Md?oCUjOCV0wD^pq`}pjjBSOR2oT>E(;A<};l7{`G!!KMeEt zedSwyy{G5>`kGHS0_ARRpKhw0Eh!gqV%KOk~9vQ0xawy>no1_2rYUGh4Y7^$iAHB8p^@$UwAOD$tzb`go*3*36{fBkAwg3G4{r={L zMCR}Js^9MbH)@{GEjP-%r1IsE$iog6_oZopAap3)`^=Peq#oo-`b~$hmYfm%qMzOw3gdSD`Ce z{4PqI-@YlO@Npdxk_?(z<74$!)j})w13!nO&O~LAlLDQx6HduYJS8*HLsUa1cEYJC zJ9brmd~SaKS+lVGrozi&cE5fI^U2yddA}Cg_V&iPYj1B`e7jOO&*qnicJ=QUuc8kA zen0=giHVCdvi&6{du>$K5-*RLyZ1xU$3t-n%8H6px)#Z-^2&}d-7)Lky*jqA{tK73 z&AHj8_C$QT;g&KH(CqJC<~%L`B}%M86PadSk@WUf+}QbYjZBn#VWni0@D>ifb19FP zz5Vi~%+SKdQ&ahe^qOa2K)N*IyyT3`1Nbj z{Q2jX&#$XmJ--RmP5KjF9IH{kr>d3T{$IzMIXOk#+$YYQRn*mhtIDJYFJ83l4d{tk=%O-BY3fNq?C1a?nBV@{&*yP&(XaVpLf&~CEt(TsTlgtRP)6ijY!rV$^nKUYU2<8cXW!US z%KrCocI4hX=Cj_@AHFiYc51`MR86yQkLITaz3Fgz)^q9N)ZOpqd8Ng1zTWm`$&Nid zZ#}J4)TSEOA9R`(B^DSI=O=jh@RqDx`8MJ4 zy1OZ9svCE#Y<+gliS0V?g)JqPQ@5(-+}}7`boDEVTN~y*>I$;j6}@7u{-V-M(a+*3 zmoFIffBFe}nQqo?Zc|1+sq+I&;h*DSjq51MEDY5eg$0hx;XgmV;*Z(+O}YN}mt>*KlTK)#Q`feiz2!}b(;?*!ZyzN^)u}C0LKZ!G zHfhQp5+p1wZm{JY+slII(%mh?ZJd-}zjsDsONXGaD^JJuHSHfOibFyG%csZvy8_Kd`w zq_)kcM3+wHni$b_>L{d;XiOwdCCm^R8xJ-;jAa4OE`= z&E?-%mNI4H#K5xm_neP^67{(-v**%9R(Ekzp@r;5OS#hh<=5z(J*hp%Ju}ww-o1Gt z*RKTI<=e5gE}b$pd|}!5W06ZiDDY*|@4k*=S=gB`4f}# z_U(s{eF_#TO_Me`Nr~yEU0&8M8FTyGrfENOYIFGC_Li=Bk)fulFPf_>8+7Tp;g&*X z-tN^aXT`gR)|#CTE7`ba_w;4kT6uWZEZ$`||Jk{`Q>(I9uFGGtVN1_0*WM>$(>Cl# zwT!HMdPP4|dGmrESMTW)X9PMp-!|IRr5$H(;3A)n~SLh$O=Fzrtrl{Joup>uQgoVVsy{h;$Us|@fojH2)D7W>-o;QD# z4jo!-Xk?att0eY}Zq9|71~VHDSVly0bIcTW3;7b$)O0w&YNyfqB*tHCYAo$7lV^v! zWJQUs%#5`8@WAIyYmm;`Hv*kcBcg7CRs+rDEH_Aev}EJH`?rKE3VxYvDcMr5m%Dc3 zua)W*iH@2}RYmVu{*;rEX+5DD)KaG8qjN^EE7#C?=O0(c>G^VJ$Tj^X^$8uV+|iM{*35qN zsddYiuNUs_?wT|yYuB#om8;gOTiMOzon3zEs_B^vuhwqSeVu;wRqv9g(>86&pYdgl z6I)n+fYti8ZGCxKtFtoWuemxUX|<|`-nhk4SXAclMnT!grZs=xoCEnYo1Py?-t+aIwM^YB z4!(0-F*bKOICu|T_3rhbAg?#?oy(D<6K2e6YQ7}M-a6;pIln#`ODBJS{&nx_ELKk8 zXf5+l?bDFC5)r{=uyNC&8&M6dOLfh*F;)Ez+q7o&WcTl~i&m#k2yI@SnUk4T`Q7VU zPhQIDzF3)xKb&sYa#Ic;Tbpz9mq7Nlb;}oRnziI5n&Pp9uj2O)K?xD33KtQ_gT*t1#k@RT z$S5sR>g?-PG_va2$@!6mg=ONLIj8pRWq%oKd0@)!_wG5GP6^qKdzT7S7i+(HyQJgo zo5F47&N|Z`J$cd7y;<9+li8?~xo9TS%80fIjf0L_?(dekyi#!15K)?L`9NXXgC{Fo zw5BOew^kCjR@fk*sy`E$vY{^rHTLLc}#mGo5m*Zot}T0NVSi+}ls6vn@YS8v=RX!Ov}TWC@3hmWBT zUWxiu6>-Mbe^j-P+j-2?Wchy6KW0)ZuPktEZu{?0cT?~Et*zMwyzBl|v}XS~vH85c zvqOM_Vd2I#vF1+q4`}`S@+^Cf)nCKs_V#Dz?cH7R{fTE>#pl#_b656<7ci`Q$GBsK z&4kr=KYsg`nwKM!S2wRGN9yUtxz?56UIg2eCD=<^{*1WY8L-k;uq)*T_tJ<6t{Ilj z{h9pdlAig*rk$JR>yl#fk<(K}b@`&Q?@QL~X*#h&LP`12iSuh!?Cd6Yt@0}RRCMx` z?!rBr=Dpb2SyA;lKVQ&$!g})8z=OQhC^fIb!Y&`8t>7O%7f+ zo)fy)_DyXLfA#nE>iX*QCnVH2G1Y$j_T{ke)86gN<>j)UH zmHpk$`4=W|aB*qPjKA&meU+oorAa5W=d|}OWe>gDDRVRE{HuQvQnR98zKcCP^RxKQ zD&BpUbsDpO$eIML1lIwLG1E`IuD+%pzt5)5;rY$!^OTj$CRrvfymIBLkztzi#`Py z?@DE6|MvFzf#hn-Z)Y|iQC*ghmDwDi<*pQ(pg(0%e!`)@!OIt~PxtY86Z7Q@@9}B# z>Q_k`mtMRy>%`vgwjB!>DpqawooSSM>%8~HsPYBIFZH&R9GkXeYsr>lrw=)`1gIRo zc~h8MyI3vj<>IAlTPHTkG#+5o_qXqpxq0||z5T)M)9*cgvH3M8?{~TAF0syn!ce8D zU4=!{9*aL{I^@{b9d+^wd-~-IbIwNR+aLM4`TeI~^ZA5i<&VZrUZ%8Za$;Uu@7%cp z6Fr(HPv(klGti@FI*^C=6~5$rNlukG$}2sb@HXDk3RkH-}}3(Z?nz! z{r`Kqq!TtA^qq9|ch4@9#YV=LR+O&>b?YvzIJe?^n&|1hOlC(L4lZknV{Tzk+z@<& zbD^5`&70PBhv@5HW3JzIDb40S=a)C* z){Td&CS1O9$FbDgTx{AlnJvZ#9krabiqr4Qc|S^S&dBz2Sibh@n_q^BepA+Yf8I|M+hIr*U=-_n)`*uTOO6F($1L>2z82pZ`boj+)24<{fq-&Sj2#ih>Wn zeB=FaNn8K(*X{Q|eDPoR@#1>B2ixWUbMxE%5s3fyTioW$k;ySte;ip7t!F(5*t3^u z!miVtoPA4HY~a}$FIiZ~6XZArIHV;CJm;ayMH&f3)kT+*8@@?+=~2 z)OTi$jE@h0Qc{&bF}L=cH*+^`y`9r-ZLzCHUa5D(mQC)jln%#BI)sWkV%$4VSE4Ja^$jO_okW+pQnx*iCKNzMHw}h(N1W!^)qk=i;r- zow_B&%UgQnh}(jI9}gZsb`A*DPHxzwwBJZF+2IQ zTKwdx^A1Wmj%uaHZ=Mt_FZX9T$dLSaEr(F@oTYg^Juf1oqZKVIM7+79%iiBQ-S^h| z=KlLFhYlrOJ3lA&>zBeLr{uuZOcU6D7JNGMw)tsA~SfjJ0pWL%!Vawv+<8xgbCM+P(Fn_-MzkmNU zrg}~Bddk0_n}g%P>C?>i@%c{8&IOYzH`d;c-BMB_JblgyTWi^g)$ETSK5SonWwxw! z`NT<69ZHim_415rL>`8u{uElzdB9)Ny)sq$|IhsU2ez9Zjg-CEFXX(`Lq+Ir(!bwl zlkV63e#<}q{ohBmR~#E5&yjgo%SlJ92gY@gM)dDI^)%)@rXU~dEO3OOwXq^2fXRc*Nns2VN z>QhnEBS)4fak#a#xfcGMsD9zw*H6c8pD+CSDs*1eGr_pZua#C-vu|4LI&=PhZTI(k ze9+S-Gi6^hE94kDoprxO$X3zE#LR zUQ{bp<6fNHiI}+`J_Z;4?)xjjxqWl!jV?70Ur$%dri7l^tHsN<`MtTH?-Uuyn)`O{ zmyF*UT6#}!KA)FV`qjYB&Q3*j>4lp&eVv_!m6erE%*=vfmoHtmvT@ofwKTUcD(P~I z@;#2v$eVL-n_}MXT={Q&eioLVJ8O^IX`S+xlwF&cx^%v}Ty4w4PRkPI$y>@x0^{So z*KeCVCE|f zk(2G$*OKpFgYVw(@Xltgt(`6WX6x3qF2|2OP%S@r^s21*N;ZCZ%i67sAtLR<(`WgW z2D9(~{U>+_XZ+{0|F3NnzsqzaKvSsGW&4iV2loH_SN`P5)1c@zC5EuI0HMLYVR3pl ze^$C2I~Evw|G=qNR+5o3_k20kUGwj0x{PgU&v*N(e)0JFx%X>68J;>2ut%hbX^PaJ zn$U`BR)GaqCd3CcSp_sN-YndxxKL$z`h*EHx~`lR=9iDpkzKlDXSH(i#{;Ks&AVN8 zr}L*{_NrCcA1D4!%Gkon&8^MD)3agAhNEY$Oxj!RGb29c!IhQWJbZr29@`$ge0n;z z{O$3`S8hdKI*yJ9TUxU{ET%NM+&IOpZ*#Kz{y+8lzu)aYUk%@{s;eiH{<3URQ1OoM z58YR0t#Guz<6j!6_g#K=N^bI;1q!A`^BJyWsU`|~ElO(Y=;T(iIwu}q$*@kYFXE=HAiipLRXAx}f;$p^J`fYuDNaU*0@r%G99XaPMGm<+ZuJE$%GE z-|y}gU}0=^a;&YjjmkAYzyAZz`#ry-<0_x0#?}55ede}2&g#N2t;?^X2Nyei7_ zuq@_IOyRU$1qt&_H*UOm<=XPgYjsoqelU7$?5(p_&hhqj@A!Q2s#y7nf{H7crnI!R z2@41~BqTiG7d`#|--@eQA*;^r|NZ-ypv;oQy0X5`PR?JYRcdMCGos(6r`0uF-Rk-* zZF)zq=Yi|`488$O);+NiGOM3F{oHL+rX}+CghKmuzmL!L_dSw+AieRg?k_<|tq^_Y z>}>O$g^$_H^X@$O_4V}~mV1@opO<7+SA1QbzhPHli?R5-rOOry+J_4K+UPm2{-Nr* zSc~)P>pttpR6onK`*T)*=a&zQ=h&1p?Ti=TvbUT!?_WjLiNdlXWfP;erb$6IckY}# zeQH^KL4&Zmzp=Gp^LirzbA8J`Bl9O+3~%1dH8kCva{Sxe6HDHuq@*cN-C1;f-R_-_ z`>Z>9dX{Wi((!AT)T-551_pP!8V~x}*f3?}WG!5@s44L;i|JKf4k5<$^z=7x)*2XZ zJaF*Xb%{=#$Tm8rB`E`f)*Z$Je z-&ZsBOOJ}kzPO6&>bn0Gk6*s*jL|!OJh*lGb{Xwx^;x$*RX&}%`Oj^6`v*s-%Q*{6 z9ll%s9#m!AJ13dGQo8nU^P64O=1ZTd&e{K+r{C_I!eimXXU_N-+S>Mpt$zCAqB2iE z&!;EB;T8Xs8i;{@%~a@7Etao4?;tC#0*cM?%Y0 zY2u5p&wp!$%=7O(x&8Wnp>W0P=kF&3Pw@Azm%sY8)MVSXi4!OHPd_AIoi6@t+B8X7 z`Nv16$0s~}Avmw%nUu2d2}^}y{l&9a zx9=?Yr{n&7`pW#g#|x(F_8FO{=*dOgwn#|II`}TOT98GVho@JNh54K8jsB%eEn8i8 z_VmB3_}X95ooDxc`+xXmtzY!!hgDRrx#`u^B}>&OO_X%5th9T& zRMh6z4Ws=1rM=VV?LNf*e(!-30qku}4waRb)7RU$`P3+CAAer)?dxeD-LISP-jLxh zw`_7r`2Fp+!GxTreb(>()McEIa89%qYF>M1?{yL7!?{P?TQ}@m_4vxmRFlMs5|X(G zuU(Uy+08yJtjxqTe_Mz3uBgh7Q`0M7zr7w2GwbP}pPT1cm$!ZW8d~@BVRg)o?N8|A9SsZ)96c_WUCq8GH`l|XC$Y}azdqh{ zwfE^OW~CE?1J0ayAz*8(`z1Tt!>i|L_{N`cOTFH@=ftRNES%WV!X_jnxO;~dJneh5Q+;@IRKbqxrv0o6 zDSxKcNqB5a%FOKCJJ;AKUA%_rkA{~1vFmrIZrm5ww@&uin~#s@%!pReFk!M*m-N$n zcOu57ps4KWf}N&qY|)$__?Il)ICa8>f_FQL)&0(uEm^8w^yY?;eC-#p>GD?2?&0d~ z;nyyFEB|z+Sp4VR<@v=wFMhsqHLT#_A;W9tX*Vs*4!zvGdCmGW1~&5!9;uL()xFxo zU%psA(#1fmqoXUO^ec;FW1&g;wV=RYZ^um4%2r7hHrB^4pLSnyZM$+*QhlZIynAsP zR~a>)%=3uNE%^H3sQbIV_A`RL@fxC{tV^D!Jb3o3ZSLH|{M)~on3zqTIrr#|BL?gD zeN+mUtL|w1sd;XW-MLc_Rm1r7KYsE)Z)k7c>7uki_e1I$m8Op+^=yyVtkCsYx<{{n z@#dpGygG%)&5C1U=M)qaI0^KeIHmXC#mV3+CgPXZ%P-itw*Ac?rDN)CFW=>*tk^$y z$BPMPy_OmYOuaRI#}AK3kCZw20tJOusTpatbeu>ybf}qwD`;^LtI&hxYA(WOzFg6G zEbLtJ{cF(eD+eEZkeC<86|aTo|Mx)R|{#UKgQR^7mKT%};l~-(S3R?aZG?C11^&6&V$~ zaLuYooij7l($X}w^we#X+Fs0HN#Z*yEGOH#d%JjK<<69+PlaXV91}b=^5X7AL|Y3l z;at9HRojUd8jtvn8XKFRp4giVYHq$$Jbw2wYxPRmb#g^HvUd!(Zb*4urYurdbn28B zM*v%*ibS#sN6M2lBh%GSFShqzycXE4An{=NmqV9tcW-=ICby?%Uh;AOps+Ca#7dd1 zp+~2Pg-@6^@5q%eIz^Q>LW$~42}kDbj=7lSeq-}>kznIumQF+VZ?lEha4z4WGXK>r zCau+8E7xhNTWj||`Xlq{v(d@Z{_5)udKX>NDdINL)bLuqKx68gJ1U<(ZPw5`sjmE5 zKvndm$`Y003x{~TwT<7*d7Js_m7wjD-T%{zsoXJp ze_3!AKFkqj$=W>Si)y8TOtJbc73W!8lO$v$(+g7OY-?lN#Qk|^!OO(7Y11-t^40A$ zXFgUJ&&*w&ke1VUY@V6X=Pn5;nWfuzw|>0nmy~4kgZ;$E(32NgSOtV--AY0<7Aj^b zDK~ZYa471SbaZ@3XfmD6Y}3V{s;DUSY}xS{v*i}AT|D>SMb2r{#aHI(EnHjQ@M@W! z*6LRsT^%Y1%mtJWHa=Y`Rrl+Qq275{!v(L?3ZI(9*L`H)w|8gdHvVRuho;%78RX3oa#+f&~+-p$#2SK?sAfdGSJhmVW@@37E%<+Va<<>F=AI=9_p zw6XoWv3SF=^X7AoEi;=nt4cyPa?#?|!jIRoNpRerz;RnZg-P_Pt48xv4mPIUW%v7L zuQuNMxAw*@%LmWi_4aR<@9XP3a?Ewo)VW@6X-b|a3scgT2Ec2!yLn;bfxg{kL34%9{5>2cCz<)kqg-gI zW}%Fw)$v~&E%Wy0T5I_lur3w7vUSUZS)mSDZi>s#%P!rywC&`?(pkH1pU~AxPD$yp z%T;FnTiVjT`s250!U7UKUT?iqLO3P{Dzdb%;Nay}(urwSi)dAgXpMT>-rvt}SND&F zmDSMP_SmUQI?T?^XV&K$*qJ$Hrt+RvH|Ju@plC?!&$BSWL|Bm z@i?`5;nuya{{OyM-e)jB;ebVIV-9FE{4~2Jb?`PuY=V$43a7$A%F1EU~d67kR;>>H?^uE0{ zcV745@YS=->gkfZ%kCd$6fRz~=B;JHg-Lta&)nSoo}v8x-pOlk^W4bV)0}%qZW{-? zKu_7D$M1YyBUP{RhK7W1OU}vhDE3xQ@3(cSi{lLo3u0nUE-3w~>hQEXbr&^Y@-WdaSgfHs^H7gQ+m@{}?rgT4vAOIE%j3^^h|!KFILC{rW^i#Mr>VCEnlo@UF5)j~9LT_UZ6>|7NeZ-XSFt7Y!e>w6Eag z3ASuD&?!7wN$^ZuI|T=pNqp*&y>1uY;5Ti?r$FKt^I<%Dl;c5v95OJzloa9 zo?Sb6`t;$*SADCut4Du3!4dpU@zQDWmGK6eo&!4aF|7&}8O--))j}f=FRne!*C(fK(w07~-?{*d4<@Oc5SJtE~ z$$Q7bn(N2St|987!jzh7dFC9ipzO=zrxIMDBq;!Rpn*1UUmvdzwWS=zNuc=SG9*xfzH+VEJJ zGG`k5+K(SUAH8<3Pq)oFP+Nv$!tD)LJq}zt$CP+FZ_S&voZm{9uC?fyc+8_D_hgL4 z+Qkd)W-n?Adi83x#N4aG5|Zwoo|1cOtDd|H{O$8_Tc8^`Q$?wyrK zzkGlB@=b=L{SD8U9Fx3taX;8s1g!kq(Zi*xWyRyWp=*JHsiNX6d9{Ph`{w`q_U)^( zk;GT3D;iFG@xK$&sSqpv{OqgzzW;yco?8F!i~B3-*BAPE zioX4^3Yw|ws@l4xbW3n(aIb2{;n;}+>&1Hw|D2BgIwkzKqJ-qdi^6C3@4N5dn{8s9U;Jd=-rW)xNF|{)*QXe5HaP#~3{!5d?2+R~1!NbdWv%J+jMX)Y*5={emY9~=sAe&x znO$aPmUi-jts*m5i^%u#f7N{(a(zlbT#QR@sG;ssS0Db&nKR#{B?NeTA7}V(U(~ua z*!^Pfxr=wNdIcw|PYpHQQetLkYVMz#EUf&P`Gxz|l;S*EU7T72}tjd_8&cIV^gJ-pH&zgD*QjNaPMH{b8O z!829qVn4KWDEfEt+rNKhZvQ^qTwe0EM9G6?idQ|04_WLh-wEEUdCuB5FYd7S!_!;U z=RY-<-+z8~e*Dv^)8{ED*-Ux!CZ(u|KQXV;JbIhgiL+OHOnbWYIWxOthAJx zhAm&5U5>XH8k(NFamB=zxoXR;w4%>OnJ-_YC6zTU>N5KB`0$qP+l530eO*#G!qR>R z?yySO_nzw2&-^Bq-xEUuugs@**0j?AQ@ zBF}Rt)D$hJ{W-+cbco5a_8U*OckvhV?+H0N0pZsVym(=tnCSSTWHS%%nGM@EoxAi# zDNxWeW!ZECBco#n5||GsmmWEm{o%uhL#Hpx$H!a#`?v4M@8638G$x#W`s3HHNAKV7 zuMAzheog~}Xwm<(x68xCo=3#aTE2O8`^=}xKE1rb5g~_8bt?1p_-)=%G3CmYker;k z1z*1%x#Q^?w8K(R?w-XdW>3#k&sVSim%Q2QXRpNyhgM#G?s@vu(c}J}89Uk*2&ghs6`LN|Cvx7I5$mPcK2hG&>n#!h~%^nl`)WTxdg9k5~CNG!X#)lcJ}%=Q^t%T`KwHY1*Tgj}D44bGo%L z33YDx>6hwdb91NU3wO4evu3B46g6EewB1=L+2YD_`Qp`+r;ZhNX}_{a*wlN$K=kTY z-xq3P7yQo}ZQtTi>}@+y@ZgVCQ$1LieZIXpb}mc5`rEsd`JJra-&i)Z9<^(UE#tUd zmYAB`w)7^WNKw9%>BKvwx5M5UhzRhQ|NozRj7iAxz?mZRrXvjM;qF#j<{mlbQuysm zVO-TyEvs7lU5%c7`D+TEd=OZE{kf#ns}vV|pIBdA)6&)}reXpzi=38n9l7t{+uzS0 z7Z+DiS^4no+urHZ#plhNS5Z~<`03N;-LtuicIvO2XZPdx@5OI(7O!4Ab6uY9W$)KK zd|v`%?;p7{QS?|^_>8%>PL6SkeBahtoQmPNa$;sq?^4tA?H^iXy1cu8+zp@q$^3!! zy8k~eiv0aQ3tCNl{{HRzefHA4dv|i`%gf3?d{E}UaC5S^ceZBe)zmB3!ao1gzyIX1 z|GlLPbhy*aH5p$CTsfFnak_Sb1jm%{XNAw+aBVe>T(kC-W%Waq@3A)hoijBP9~xR% z>`N1aBT-=F_VMcYE`D zuB}T;OP}`Ra(ev7ujcg+XTPq07+?QgvzuFe#fp>5$`^DmK5m|o^JdAWU0iqpTPS)UxmuTe&5`6RiaqC`plV}o7LwT%ii8OCAE>CF>V@s>M zkd&BXMg+@5!9*7qmxVlBj%vkE&)wd(@u#Jp2J?{+;oW3rdF7D4aBO)RB^@(+Ns|^ip zJ3iEIJ%7PN!SbmryusEfmyL!<&ckhGR~@0O!eOL{gq+e-&4&tRqLlc zI^`ubJL~6N{d)dCQ-Ax-buhO*ij&s zEOJ#pO#Xj*_&TQUhlT#09~xJ;{&aC^4+shK^G(*RtyO;UMcL&zyO7}Gl$xx08&`(5 zsV$SZ^!mgZe^p!a>9^c;Ri}O}`T8>R?HecAM{YH4GU0O8XD3g8@BXaDyy+nyU(~I{ z{pV$>*)@xfKD*jqv~$jb2LY2N1)n^3@^L^ClVIRP87~hdExl=5wunqQn3dzBQ((0D z@UgS4^ZqIN=$^fioz(1d`S7uNLIl78G~Y z$2(U1=+2)!-^?#BUYojLzjSUo``)={zm!~A{5E6h;+^x(ZA;o^l_l}&{gN#P{d2e9 zZ@gqLAf25a6wM-9yGj+yA-5USh8iw zx`or8ebZBWJ}n~VZbC`Yw1*!xXH{)^5*`+K?}kfYsIKl^=8V$ECH#GxH!u)v4Hy$JV?LO7%+*369bFSGU9 zHa@22n!sPb5>zA}=PD*-Cm+pK6MJ(n@#r;Ow+e0M&fad9wP8YHVs1V@G7kjOyi7R$*ZO`k+ zmiFsYGuO1$(wh1z4K}KQjnAoQhd7F=yO;`NZXZL2$Ww!8e$^HKxaX(mJ@L2zU_Vc(e|LdT$S?B(* z3Hi78@7lMo7cSpEd8ViBHnV3}%*__aUbwhiT2;%g^UWWnAS0trZ(nsI#m)~uSWE&9 zE3~h!IIR4UpJ& zBA*!6ywN>*R{O!DpOgPQTpa%M+ZDLX~F*w-QyPCOqmtDzW<`vB$vlm`+Xi88xczbZ)No&Aq?n&YU^3^Ygm95>nEMRTo&?)iwLh`GF?a%r)PZPE9%Hp8PDsd;Yrl z3iYOsAD7yE{-NqtI(N$Ss~Za~rms%_I>+M9>6MS)%Gg^SesF;!vaN4&hc;qU<##~XgBwYts0$r>Vd9Fx|H zOb;pgcqw&Nwv?g44adkx+3)v$8%&)W4u`B>ee{F%;MS>7Y6 zZ343EmGlf7cQ}7!Y0vWT>Qy(h>zdW(cCByj!s>!KCp1_&IVY;9xdunZ9zFEOQ|#Gw z38_~{u7|rNB}wS-|0T43?>{rKYSwKWOkaCv+LYzZsJ_#k2U`o!cFOt2t;kLJ_vLhS zbvJI?=BieF^yW>;>}cs4k$XRWu|9ad-sRI@(W(3YJ}8abQD)z?kTLh}+6cKj=ij_B zIDL6?MpH~?-ur~29X-Fp+Aa32d;TI&*4WrNBy8H{D^c#%;l_V=2^M+3^3c~%H(%b- z)+V=WOY;*E(SVrP<468TDNp|V<%@q&#b1rf#iw~*sJ(b8pIlJUc5tHRuad{GDqppu)86TiJ<}dlFJ_(zY4T4PQ6s;h7n8-rdr=)pqR1qtE{zNk6bg zp6@>Qe^21^-S783_OJh^{I%Eg3;)**g*kH`OkgnD{3#>r^^=RMr|+!z$+J?j{lk}U z3KkkY4xs{COYMJuf2(F@CNX#4+&_0J`1D_Ayv|V9R-auge$Lp)%*)A%$UIPH#S*&^-p_CaSlJ6&>o{@h64D9xPQxZ*;N-};`< z-?nDntByPW+P`Fz;ndEcr3X)67q0KM&nr^>CiZQ|&dP*j<<$Rm-f!N-c=(-Lwqixs zxpR!SZdGSw_ zHtgQ5-8At)MYKGev}Zc%Z8iJMz{4(JG|iAqzg60@`W_cmYV zU-)TlxZLBN+RH5xlHLC=5cBuXFS}bS-qvLJ``g>aE0<4PxG!!Q+m|)6b00l9d3nmz zFl7!l2?>rC_v07u*m+dOa_jD0d*$lYi}nU*ew{33Q)ge1|NhB`kGx+dTW$HPEMr;J zqU_#hWNCQ%)hV{m#i!5c>lhlFd)Fq*Ho1U~K9kN)PdR6|V`tUlH#&z89^{OcR-fWk z@3tyQ(|?Mf{B-ZwVB`9KueR5Gmrq(G^4G#Jq{~I=qVBWz<@NXXRDNzx`1#xZPv5zD zVK#e~-nbd9#KAN*)YQbJd_sIkpMr$2oM7wh+0u-&97I+z%!!TNuwkRy1P+g$UI7&* zIlFsT%*_fuebG!Yl5A2)NJ(i?;E<4#O3KLSNKjdDoxkwg7uHQhn*v)kHf-On`Zqws zeYw@etzrVR3pyU`FwoLkt*N84Wa-w)r~6|bU70C4ui}MHzkNm9v_3_f+CPFkEIK)| zH!~~v+Yb53%v~a5V0idsqk1Dmu}zGRGG^tcQ0rPBUj%3T-n`kkukV|M)ve^j#Ma4^MUt5tzntNz`}=S6o}Y(mIoh^gUi>!syc zPIp_@)XY8l)%eBnc1hXyiG_8YH-8HLVz;{Zt!%>d`JS1Pj4vfUL&C*_gM(XIk2>zL z5xsGH=L4nB$4+{7O`a_Lt?V3#pYptQ;e!Kt-s)>~b3bh0ee_u0?ygPo^`o{^7uEYg zCat)46($!4Jb(86_Idl?kA$%v~jwZp~W$%xky*t}oe(xFU_j}I1ysRD>6_t?Ua{kK~m8!c6 zx8~{1ijD@IDSGd-_={J0%NDHZII@K$Y0=@A?^i8ezqet|7L$}9^+}I{H1zcr8Z2*^ zxifEC*cTb=;u(wGpI?0CK4ZSMYjCi5(?W+551x-CI{Mi?Xb!eQ}-lqP4}*6Gw~=CvVP43$bo42~W%ITlms~p5Gzr<|b zjf!0lUp;PGyjeK!9+%zUFU5JE%>OZee3yUGUcJq0qKC?^qjx}sf#UcP$0 zY~jN8wqsuB=Gi$%XDgrH?4FUQsib5y-U-cgdG;m+oD5uPl|FSLEJRbh+|Z>->VZw^Dg{dM0qNU3toG zY#!wFXQ9I(CuTzv0Rv;(hMhMZ&zw24uij_FHpAn0&M>K~Yq|+FZuydPukx)?zFoQ8 z!36uaYnzX>{49KXOG~^@S5Ww=?|i@R{r(K|`0_mV-kqRr^ReXf(USebbN@c(IeTV9 z;X4WUuwO4$@<067eSF6J`Kr3cjP_gYA3r{A^W}i2RTN)gP2lk_pSWyy3GQNZv#9x^ zle{^br|;~>vPVouLwO}USmwy2+_=3XDgA9z?^IhqzP=kbZzQEZ?&w|2GqYJc&q!KE zX3mGtoX@+@?|bs=?0bRo`Q5OUP-4ZxJ3f9ly<-0GSNZ)9&+Y$ro_fV)wd>Z)cWYEF z6`PJ6(TIwEwe!=D)Oof4)K!)*PfSeiUcA^`POfgp?rJ4NfsRh@l&o#D?;I*%SvDnfaLnXSD!}vX0K%g2M0#Q^Wmgy~P&`Z~pR)*TBHw zz=a1Ex@YHdOwzq!U48M=!o|z$n+`rS%#vE2@%Gj7z3CJFOwu}*K22loils{zPk#8( z@X_SQH%hO|ty%k~u&CtVxoe!!(#PLN-#=$zb)ukX(xVp&&T3x|9E@vg-_0Li^YQAl z!rhw7ypW0umVJBo{fv9s`+nWwv#)EuUKRiHExYhziK@D}th>6j)a}h#$ zLBQ+u=@X|;tJ)dP`EkUf$!W@pD|H%Ey<%eSaD=p9xN_rIzWqPd_kaE@xA{DIwT_P8 z=L?6$D_6tq;8k{xm9by-{O$F5Pad|*ISBMbM$CF1F2DCA`};iyg8gG2otnyC z@$+f6jIGuAb#vK2gtHi$-gVBaQ)*+gHZV4JSGwaEn5Z|?lCPqIbII}ykB(ktGrO*v zM|qE_AMdah@vRC|vizBulA>;CvTE^;JxxasyRXP!=`1UBqM>d^pP%l|O4pRMrvl{~ zvo36K*i(DwMxus*+=>Ht?#Og1ESL}_u)a{;!r|1n+_pQ#-{(fmn4OrF)z#T4`TN`3 z+d1{oGZG#r^lv`H#VuSM^CtG>c|RpHHJM;z?O$J+-`v_c_sbn2 z++6xgG33^5%kG6iU+R4!O^5ECC+m0Xz5gw5_wz~l3-=PAGk!)EYtkLoCzlj8J@{ZC z6f|k&id8Ds=IwL##9T6()Y-x1zA|iK5WBFLly8)mon_=x7uO{pe!X7*=^*?2;;*kB z^YHQ(yt^aGr?0>Hp-PS$8sDBi>v)>TKDZ)_nrE>X5r({EUVu$ z9lr)T14U$O=urZ@%6E-xx*ijI4X%Z(Fm($9SMM10r5+V*E zB^vv<sRJ~+f8Oy4v6}wLwifmI7+?Je{=I`yUezbMvo-dz-_xwHA{e`#e(#5+jURg57 z`(&T}d_M2d)aUabtz6w-^uPA`j2RNj+Um2D)%z}+U+~YXlgpOwULzB^e94}^yT5%d z_V#TkteN_$E6K|GmPPS9m9=kmUEGdtNPYeGPU&;IZ|^Km*p|;us=M&6-61pmbxZG7 z=5J-^EN*z$|0pS%^ytZpPxekvpUE(l*s_uDwxt-SLgg-3hx+eE3qLZek)*pUSZKvhNz*t4S&x zDr#zd(@Tszer8?{1Coqd0b!7?syUC6X)6XwruUwF|hZ-26R^|v>NA1$z) z_Dl?PKxbAJgSIv|=+Xy6!-w;#-+}PKTeqYfH(7etpPy%KXf#EkxF~(~#kp_p7EgFK z&CILobH%5b)sePFZb4p3v9@!kOj-W%i?R65vZsP^HJ^;u+f=uj$5pm1UvKN?md4d6 zWqNaGBQw*Jj~*)b_{*mil}+7vlJ!#1<`o*Nm+e_GY11a55G~Prar>4mfBN8= z*71wyL}yO64sth_kxjXA+cG)(@ek+Mw-3hclGKom1;ze;HCSu0rJm^<>wXIKl!H>b9c{#ojV@B zU3}o~VeWE%{Ut`#a{FpJ&Fg+~U0-K=u$kZP#L?e+g)jfLThxEE)3Ym>wK=^nc+ULh zi5nYY1P^S}jrQ!FYz$-H)}*hGTkkOG z`QKy@9v!CYpOxPp{j@n(Io~Jk+2^@c=k}cY{rhL#KC8W~-FI6LKUGn;7M>rxtRyX7 zZtnH=d3Txa*w+}u|M{rQCtuZhJ?_@IX3BJ9?Sv&3QQNZGRE}GA=LHbPTE z-_p28_H=Q4AG+j4(-jn9X?>{_St^fJo!PqzTuT{V8 z`^Y3ObNl?^rN%R&Y&x6rVkHcI*e&y$f7W@jnsM17mu2T?9X`;=e_`L+u5G$jljh9u zaY{G!eYb1&A;ZZPIX5R;j(o6j7kPVp%#(>rhTnaBO!-ShWq*MGcrTmQqc zb?FtA&x37tfAu`pTPraCx^JDMc!`?!B&S6s$2`8hn{#@VyW1sWYoA$By|HD9hLMHQ zU(aYu9laLX`sUu&3CE|MQLx)*xPI@p$*!NhBeo^T8F#4}7C&=&<~RS~CfB2P%Jcaj zC9AOUb3Xo*QE+R|mv@#NH>H%^vRYMK&vVKx&hW|VYII56{UYzBi|Smf5|^n{JiIos zc`6!KoM3o%Zgy{|*`Mp0WceZSxPK4JcR|FFE4H+vL*-YtF^bb)vG z+2+`?2UE_nFh_35U;boaq3fEst%CE-doE@iOEOF}S#qsh>$TI9UZf)2;EK!v?v1GDQ=SJ7yicc$-zqqU1zqI(+=MQh#UtV6V-TeNw|9(4W3F|bOe*0}5 zH;)QSUNJw>a`C&-z5`+mgzfFCN+b&hGE!6pJqXt%zB9W?nIW z^E>VdqMqrmjwlsEwYq|H%G=A^@ zc11xas5`F&Rt>d-|yAl{P3l! zZ13Lwi^<&?*B6?0-H=dODOqls*LQEldOgFI}-^250bcqn#B}#&YwHPM9dX zp-TDA(g^_`g)d6u&ba9>cIQcc*`yR^V>Feu_;|3zGX1>Wy(UwAUZ=@ssvH$mKf2)6 z6o)-3ik@dP?uGa+Kc^M)N7NS3EZNb;6b9vchKL~ogpZl3Ryb)_YD{hnhFoqq>-2+a~< zKB>vZvr5HEMdj1QNBpDpJp~7&j3=+DPBI_(CabiWGvZQsHR-Av!*45KLJp8zQrbQZyVc|xPNk_IkI$87Y zrn=8;qlulua!H5Vd{>yR+>tBx>+37;`PFO%{_`2{TwX3e@r%!-t!{06a;LY+o;Q5` zFsRMeV3KJ6dL`APJ%?*2UFn(W85*IoO+Ok3+WhcDkGqANi2kA7!>CP!1j z@|CPvi)2rEPuC0N4hadpv^d;ehXFlEeAB$~AX(Oq{-L-Qkzq7d^6kPy1#4Q54Q!_wt}}vT^6cEnBW@NM5_` zxqiazA8DB;{vHVd+h*t0ar;KE%$R$ljqk0nM^M(w8ykyF63X6q{QmaV^V%xUYpW)T zM5*1+PCu2Q9dub`ij=NruGPe;lRdxRbH4HA%;h&V$>O);t7d9mUUvEUJ;xVkjc%DR zPZBwAySLqNSAobRC#SB79i71f^4Hm>T1`FHdwWyiU$bXt<4;^tpP%sD+WMsK3#a0h z!uq*fo02m*G~G_N%zRw&W#d(wT_v1itLL?Lcl|v4-Y&K3qDqn~XRAtJh{?=;d%Kg) z{Jp7%e_gHl^e}K+PDRsA)ytM81^TUHR6~1D z3qE^ubMuvJVZqAhE@|9p;F#K^z)?Qq)+?zMz2}y;yqGmLeV?UwUY*>`n~^Qbk`}Rc zGJg7DH#^V1JNEL^!;fr*7PU8G;%piX_61n^`3uax-2Uc6($?yAs}eMXXWwlOElb>H zVIYyJ6Q&-vTd?__^QwD(t6aS|oosT-IzB@&_UEhaATwLLX&wgb)9qd6>%IK*^6IuvJUM#RQDr5sRI$|V>rXzs>f(9V^|r&zbm9?~(k()hJv;&~ ztqfKd5)!(wVzP&~ubSV&M-SZe)yr5i7wwQPjp7QLbL7+VYny6rdR2XY?f>uxCr^EW z2y;76>QQ0;PX}HYdUQ#=lmDrp9j2FluW9F(x8@UO+^FD_t($(gnr}fz=i>bRjy{^Y zX~)(aT@$x^%F3g{JGb9gnB`?y^TDqn`SuhKL$Z@)sF)~Y+E}dibRkZ%s<>@}!^IUe9y+%QM%k{ga=3 z;GIeMwPkB=v&MOUdGY>Flb*$0J*I;Oaq$c{pB;G3omgH`P|dW%mT}KMflZg2Pm3RU z%AHjHyI=?N*N2yqijy~4ZM<=ZrAabkjh|1NwcBLg#V6ZtK05I5(`6ueqRPTYrRS=gmdO6TF=L3NLtEoDs72 z$g9-IFRzo`y7qIIK356pvc)HdqUm3~BoV14 zGb-G^7+zH0E<9<8)6z>5mUyk3pT6?yvISR{EeO%Nm|>!#t}c98Z|1+dGCVI|JEVWP zyeL=LWMx&_`B;Z()`t$cdP~@(Sm8xc`d!gQ z8?SV@O>!w}nm5fi`tzUS*FWBTe{Yy~r=Y6zy!y&w_D7FZuC0qbyJ2B-NB`s-8`4zY z@83PWaPhnJhj(7Sx&7DZd|ut8v#;gT&(GF3N_vwwe3kdffhsN<8KYM(rJ z&eiPJ#wec?o5Z_s_8rYR{`AYj%aSKfzdm&T-?#1B_m@kZ@~ii~ZYPxzEGE{?c{A-v z&eiW%u3ZZ{ytMwQdHfH1KWL*qcDc}^yQiWStXVYYPm`XD(WR58^%ZQDyTA147;P@% zXj9s8r>#xvrTmH~f-o&ld6Xwdv$k{h0SfzE#PQF=| z`hBNx_ti$v<;T;C6HFovX0DaEq_bp|j?YY+e)s9QDGzt8O?gtL7}k~eqhqCyX5^_k zs;4zx-0VN*E4*F(#QS-alCTIIOVq=wXE5GQ)&Fh=`_i8GCGXkyo}oM3zR_utfuyWX z#J&mDzj)IgE?RnK&R2uQZvDwenE5y5{7mMNwwuzf*7Nwd_y42Um+yac_4>U>=j;C} zU)?(0^5;j+XZo{T7X_U@=6k;A-|x+HEPtD?(mMU~cX@im?n3Th@8_GJ-~C?k`^4)W z$)5@vC$*e?bXYCAqjzdW<#F$tPZz62e0R=on#0j%!n^6}iO;v^B_8>psG-s+dRnaL z%ZI#sHL23a`SzVWe*F8#j~{FQd^=L-CwKewJ@dE+M<(w5^Ye564EuVil~prmE-mJ4 zzihbll*L;<9Wi~i#k`wOww;$&Qd8@5?Uq}(YuBt-ud=eTSMLmoJ-Q@wugA3+hS5PA zXZ@XH|KFTp)z6Rd-;av_J1@L2NPy*`-{l3tk><-kl~hXR-rnAQ?(^#CEOoPayJwjw zOLRTpZvEK4xIWg-e$(aFyMKd^UlQhc%%6Ux-N#>{=e+!}j7f&GQhd^+)4FY~?1P`H zm>%spnBlrKDA8Q0`HuJbiSuu_F8g>^BG{r&^|aXME3ZO%-q^Ef znzg0OZjA6;bbc>$#I`im&+>YS7ZPRDO8($tT*vm)6X-JTRLv4UM{STy>oR5tL93!kXIsBrG83= zj=r11+WpcO6h2mavLxNO^h-*b-@F4K6q`H4Jv_RO`OFDCb9l4Y49jf3;%9nEx4sCe z`RXOz`_uY^sqR>>{OM1}79g`>vD z4L4V46)*Qojw>{@{ba;Zd06C0+Qroi80NlFxH-!$eJyMLG}*O|r_Wt4|8V^LfBEWr zHJ#J9+aK;epZn~o`25E&s@o-M;_DwhRteLKKeLnh}-#>r6SX=YuUfsVBw-=w8W!h=?w|!&6N24pNMCOR;hiztUKG=Em&4Hw{2SvX; zJg-fjAAex`{krJ?Uw=J1J^71#)=|ZDh1)$5x_)|d{`)iNh5f(2|CzP^9YJV+(Bw@> zSa^7_v+n<&f6Le0-_h7R+k9i z7YOYA^-Wn@f4)bU*}|TjKM#9;?Z216(r?~zGs%71&R5rWY>AM)VRJEW+2yClHLMMH z@40cOP9%uutcamWpozL_XnW4~ODl8TZ<*!0w{P<*$qeIJskx$5Y|pilDQ_ot^!F~F zDB_>IQc$W?O3j;L^~DCqX@-$!R+w^nZ2VYqVT=1}FXu^*0yL5`RZN`}P2DEVx@_U* zqgWQ zy17bL_g$X%XR+LjtGk$Wqqn+GdSoE6)}t#z#IVpK$Y)EEii&1m-QTuAqiVU;*Va5+ z>8pKXPo7r4j%L!SD@sz{x{~)UpSUqEQBLN;vB#GTQ&U>l+0zUPa~Oq>`z$K>67lU# zU9NePp-DzcK!Yn>gs%-U#-4<;%yQCQDZMowo8V~ZfrQsm$OYkZ87i7 zSBIW{dH9uO;u49jt}e^MM=ZO`-X>j{VRvE^>+ZXKIsS*!&MkZ8BN^bV`^ZD~;d5wz zVdtwS_b<->C9fa5L~KXQ=gX7yw{G29Q~OJV_3o}qtMl!5mArR!?4S2#$8z)LHonF8 z4>&$4f9>ZtooUmjPn@YKLMKnDPMPRyVB76cWoB1Dqd=uQL8JLgkBrkpAEudiKd=^a zuzmj6&Rx)M{Gp}h$KSf5X3HPOH5JY;f7r}q`OCQHp#j@|dG{OL*IyoZ?|J-fna~%D zkClfloM)!6Z03kqwEJ$XpS@qc7~gu?>DCSUGg>cCY;^wikgvj^>T+kxp^BQgIR`oP z`la0!dWo%;fdLSM0Ny$Fb?n(Z`(v94QaK{^oE!)N**|ixMN_v@@w^Z}sNf*wXiPcG$+x zKP}fp{$}wGTerMAF1+gZE#tNGyWG`&Zd8$wh@Nm}&mn=y@Axl8T{2!!9zO5hj!x$H zd(K%`Z!doHVD_7;ua)24R?cPqU2go~UbTj%X43zA<~Mew^4=|Ss`>EFx~3#Y-kpD+ zAn!DFzM_ivufM#Xd3^WpvX7^}KT88$EJ0a`s`_4Ba;yFF!n-S3G&@ zOm_tVB{Q>mJ9b#8cq-|-w%<-ZtjE6L<|4)|n;y-v2yow_32g^%y_lDJ_u_riE7zW% z*(LvXS98U`$FJAFxU{vw?)$gp(d+L$DShu(w|(7{QeB1h+1r1e(tY6-7H9YJrX|be z%^ayIlPAvfwwUDVmX=q$g2#PRN6R4>qf4DBON4oTb+%mUboull?Mad!|6`u#!XMHn zz4Jc)&gb~ViJi7j4nI6~r)^9ld_P8{ULiOJrwvKb|>8y$4 zU)R99{c!)?CvMxUHd?%$aaYgBj9IK(eD&epqwfyA+LEy8N1^4RuZfqmtk<7mFMU+- ztKi1o9drJ@_+(|ppJ&-~GUM=5j>m>S3uoEPNcI1cAl5Ct`DW|YSB}&EM(D)lY!j&3 z`})+y4`n=6@?keSPd{D~{e51KmdwG1M(2r3MA~>I9UnF?zOXFg#B0N88#gbz{QsYm z&=kI3CHwmun|CRmt_%9=7*rA_7rk+grRwI2oQF1HIh#n#$RHugh(2Th!F3dN$eVr<<>49er4kV7SZE*D(2t^~H=y zo?bzp&)&>$JRKu;I)uwy0X(v^-iLQ<$n|RlJD2U+7kqPTwfU|RT?K9R*7qOO8@0pt}EVs_OV0R?Tx!~BIX#m&EC1sr_8%{ zrtGy}SrxU1e@o}4@oai|EcNo@>7_5$WP(ttY*nQDEY+zY#av#YN?xH#o>{6|zCv2Q zLY`Tvf4`P4&kfNM_VDs@S?lMtIB;Q@`ql8Hr1VLXgf3=XEqQlm;<4Vx6ID8Q?2yG%her~Sh?+X^kdh0tHzwxI+4a}G9UU2;pP$cOXEl4WLQFrnTfN_i*LwQ@ulMZ>cJ1l;bc)Mw>fB|Yd_R`Z&tlNbw2Em^WBU$ zYeRnCG&*%|&bsxx{`-f|eH8Na`jqf#fpL*;uAx6eL(6L`D=nWseL7{TtA2P$NI+zy zXKJddsHkX2$dtgix>=`A6`h;6cG0R;O`A3a&$0V!I(6#PuaD2qv-Pg5vT+T`T|ojtmR*i*&V|n!ajNq#O5wXZk#@iD z9)ELlZ?}EjPxgO*j;7aqeLuhW*ZTOPSNib(9;K|D)%;{eN>ldAk$* z_BH4E?f)G3e!u>FzkS@JcKNy|@9Y2feSUtv_}v}LeLtVg{$*AEPDWfW=E0#>?wap+ z%WHnW-9E>@9&`s=!NWtWdU1O=Is!EB%z%dPR)^Buv!T0>tY4-5oag^iCa2uouTwAo zyc)ATD@*VGhPu2vYp;KePtMKNH8nN85>R`1*UtHJt9Shpx_Wi`tN8pcTixY1=W#jR zHEG@TI{(Mb50{^ZAD;R=x_IWR)8Z@FSRDO+zxMRZRYvD(gHN`8PPeFE*Y)}NsvQOY zc%JWD`NStwQdIO}m)-u=@&2V>&)R?9Wmx(?{P1%<4s*y5>iU4YMb00BSM03#m}FvL z;-hc6^-9?FKT58?Z@<2-(p+`$t@-U|zVhGGI(t_;Ki|r@CGYI9`KxEKJ=a>Za^+5~ z0@FSBzcS?>{%C4y`gOkN;p6M-z1Q7Yzw4J!0Q1(X0nxRgANH9YFyFKG-RJ)=Kd!6m zyLjMNf2jGhPft&Wgoj@**VK>QRdO|3yd@i4Z-3DKy7}&j?8A4?I^F1f73Ta{w`cd$ zfQrkx_m`}ztp9g&U2d$yo_jOe9&T8e{eQn%$2IwqQq>z*uU-4+-2VB}Qu)SNtCyL( zi2vOA>;HW2`clQuY3q2;?N14h{!uCj?iqxyNYvO-^YhbH#;0OdmF8c(+t1B^U2v^; z@5Q&cZmzT5wB>(#mDtFw=H@GULS2DdkVOx?BZG0UGEiye0>r~NO4 z{ocQBjGrDI?Y@3D?yla8T_3lvdid?N-u(|VIPS<*3gH4N zRN&a|3+iLDI0~>l+y;&j1&$_#J7(bemc>z^<+d6*hQ6(5Sb0|9wutc`4h9AW22WQ% Jmvv4FO#nN!ct-#L literal 0 HcmV?d00001 diff --git a/examples/ethernet/eth2ap/main/CMakeLists.txt b/examples/ethernet/eth2ap/main/CMakeLists.txt new file mode 100644 index 0000000000..aaf55bea54 --- /dev/null +++ b/examples/ethernet/eth2ap/main/CMakeLists.txt @@ -0,0 +1,6 @@ + +set(COMPONENT_SRCS "eth2ap_example_main.c") + +set(COMPONENT_ADD_INCLUDEDIRS ".") + +register_component() diff --git a/examples/ethernet/eth2ap/main/Kconfig.projbuild b/examples/ethernet/eth2ap/main/Kconfig.projbuild new file mode 100644 index 0000000000..70dce6b386 --- /dev/null +++ b/examples/ethernet/eth2ap/main/Kconfig.projbuild @@ -0,0 +1,111 @@ +menu "Example Configuration" + + choice EXAMPLE_PHY_MODEL + prompt "Ethernet PHY Device" + default EXAMPLE_PHY_IP101 + help + Select the PHY driver to use for the example. + config EXAMPLE_PHY_IP101 + bool "IP101" + help + IP101 is a single port 10/100 MII/RMII/TP/Fiber Fast Ethernet Transceiver. + Goto http://www.icplus.com.tw/pp-IP101G.html for more information about it. + config EXAMPLE_PHY_TLK110 + bool "TLK110" + help + TLK110 is an Industrial 10/100Mbps Ethernet Physical Layer Transceiver. + Goto http://www.ti.com/product/TLK110 for information about it. + config EXAMPLE_PHY_LAN8720 + bool "LAN8720" + help + LAN8720 is a small footprint RMII 10/100 Ethernet Transceiver with HP Auto-MDIX Support. + Goto https://www.microchip.com/LAN8720A for more information about it. + endchoice + + config EXAMPLE_PHY_ADDRESS + int "Ethernet PHY Address" + default 1 + range 0 31 + help + PHY Address of your PHY device. It depends on your schematic design. + + choice EXAMPLE_PHY_CLOCK_MODE + prompt "Ethernet RMII Clock Mode" + default EXAMPLE_PHY_CLOCK_GPIO0_IN + help + Select external (input on GPIO0) or internal (output on GPIO0, GPIO16 or GPIO17) RMII clock. + config EXAMPLE_PHY_CLOCK_GPIO0_IN + bool "GPIO0 Input" + help + Input of 50MHz RMII clock on GPIO0. + config EXAMPLE_PHY_CLOCK_GPIO0_OUT + bool "GPIO0 Output" + help + Output the internal 50MHz RMII clock on GPIO0. + config EXAMPLE_PHY_CLOCK_GPIO16_OUT + bool "GPIO16 Output" + help + Output the internal 50MHz RMII clock on GPIO16. + config EXAMPLE_PHY_CLOCK_GPIO17_OUT + bool "GPIO17 Output (inverted)" + help + Output the internal 50MHz RMII clock on GPIO17 (inverted signal). + endchoice + + config EXAMPLE_PHY_CLOCK_MODE + int + default 0 if EXAMPLE_PHY_CLOCK_GPIO0_IN + default 1 if EXAMPLE_PHY_CLOCK_GPIO0_OUT + default 2 if EXAMPLE_PHY_CLOCK_GPIO16_OUT + default 3 if EXAMPLE_PHY_CLOCK_GPIO17_OUT + + config EXAMPLE_PHY_USE_POWER_PIN + bool "Use PHY Power (enable / disable) pin" + default y + help + Use a GPIO "power pin" to power the PHY on/off during operation. + When using GPIO0 to input RMII clock, the reset process will be interfered by this clock. + So we need another GPIO to control the switch on / off of the RMII clock. + + if EXAMPLE_PHY_USE_POWER_PIN + config EXAMPLE_PHY_POWER_PIN + int "PHY power pin" + default 5 + range 0 33 + help + Set the GPIO number used for powering on/off the PHY. + endif + + config EXAMPLE_PHY_SMI_MDC_PIN + int "Ethernet SMI MDC gpio number" + default 23 + range 0 33 + help + GPIO number used for SMI clock signal. + + config EXAMPLE_PHY_SMI_MDIO_PIN + int "Ethernet SMI MDIO gpio number" + default 18 + range 0 33 + help + GPIO number used for SMI data signal. + + config EXAMPLE_WIFI_SSID + string "Wi-Fi SSID" + default "eth2ap" + help + Set the SSID of Wi-Fi ap interface. + + config EXAMPLE_WIFI_PASSWORD + string "Wi-Fi Password" + default "12345678" + help + Set the password of Wi-Fi ap interface. + + config EXAMPLE_MAX_STA_CONN + int "Maximum STA connections" + default 4 + help + Maximum number of the station that allowed to connect to current Wi-Fi hotspot. + +endmenu diff --git a/examples/ethernet/eth2ap/main/component.mk b/examples/ethernet/eth2ap/main/component.mk new file mode 100644 index 0000000000..a98f634eae --- /dev/null +++ b/examples/ethernet/eth2ap/main/component.mk @@ -0,0 +1,4 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/examples/ethernet/eth2ap/main/eth2ap_example_main.c b/examples/ethernet/eth2ap/main/eth2ap_example_main.c new file mode 100644 index 0000000000..ca64cfc7c1 --- /dev/null +++ b/examples/ethernet/eth2ap/main/eth2ap_example_main.c @@ -0,0 +1,275 @@ +/* eth2ap (Ethernet to Wi-Fi AP packet forwarding) Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "esp_event_loop.h" +#include "esp_event.h" +#include "esp_log.h" +#include "esp_eth.h" +#include "esp_wifi.h" +#include "nvs_flash.h" +#include "esp_private/wifi.h" +#include "driver/gpio.h" +#include "sdkconfig.h" + +// Choose the default phy config according to Kconfig +#if CONFIG_EXAMPLE_PHY_LAN8720 +#include "eth_phy/phy_lan8720.h" +#define DEFAULT_ETHERNET_PHY_CONFIG phy_lan8720_default_ethernet_config +#elif CONFIG_EXAMPLE_PHY_TLK110 +#include "eth_phy/phy_tlk110.h" +#define DEFAULT_ETHERNET_PHY_CONFIG phy_tlk110_default_ethernet_config +#elif CONFIG_EXAMPLE_PHY_IP101 +#include "eth_phy/phy_ip101.h" +#define DEFAULT_ETHERNET_PHY_CONFIG phy_ip101_default_ethernet_config +#endif + +#define FLOW_CONTROL_QUEUE_TIMEOUT_MS (100) +#define FLOW_CONTROL_QUEUE_LENGTH (10) +#define FLOW_CONTROL_WIFI_SEND_TIMEOUT_MS (100) + +static const char *TAG = "example"; + +typedef struct { + void *packet; + uint16_t length; +} flow_control_msg_t; + +static xQueueHandle flow_control_queue = NULL; + +static bool s_sta_is_connected = false; +static bool s_ethernet_is_connected = false; +static uint8_t s_eth_mac[6]; + +#ifdef CONFIG_EXAMPLE_PHY_USE_POWER_PIN +/** + * @brief power control function for phy + * + * @param enable: set true to enable PHY power, set false to disable PHY power + * + * @note This function replaces the default PHY power on/off function. + * If this GPIO is not connected on your device (and PHY is always powered), + * you can use the default PHY-specific power on/off function. + */ +static void phy_device_power_enable_via_gpio(bool enable) +{ + assert(DEFAULT_ETHERNET_PHY_CONFIG.phy_power_enable); + if (!enable) { + /* call the default PHY-specific power off function */ + DEFAULT_ETHERNET_PHY_CONFIG.phy_power_enable(false); + } + gpio_pad_select_gpio(CONFIG_EXAMPLE_PHY_POWER_PIN); + gpio_set_direction(CONFIG_EXAMPLE_PHY_POWER_PIN, GPIO_MODE_OUTPUT); + if (enable) { + gpio_set_level(CONFIG_EXAMPLE_PHY_POWER_PIN, 1); + ESP_LOGI(TAG, "Power On Ethernet PHY"); + } else { + gpio_set_level(CONFIG_EXAMPLE_PHY_POWER_PIN, 0); + ESP_LOGI(TAG, "Power Off Ethernet PHY"); + } + vTaskDelay(1); + if (enable) { + /* call the default PHY-specific power on function */ + DEFAULT_ETHERNET_PHY_CONFIG.phy_power_enable(true); + } +} +#endif + +/** + * @brief gpio specific init + * + * @note RMII data pins are fixed in esp32 as follows: + * TXD0 <=> GPIO19 + * TXD1 <=> GPIO22 + * TX_EN <=> GPIO21 + * RXD0 <=> GPIO25 + * RXD1 <=> GPIO26 + * CLK <=> GPIO0 + * + */ +static void eth_gpio_config_rmii(void) +{ + phy_rmii_configure_data_interface_pins(); + phy_rmii_smi_configure_pins(CONFIG_EXAMPLE_PHY_SMI_MDC_PIN, CONFIG_EXAMPLE_PHY_SMI_MDIO_PIN); +} + +// Forward packets from Wi-Fi to Ethernet +static esp_err_t pkt_wifi2eth(void *buffer, uint16_t len, void *eb) +{ + if (s_ethernet_is_connected) { + if (esp_eth_tx(buffer, len) != ESP_OK) { + ESP_LOGE(TAG, "Ethernet send packet failed"); + } + } + esp_wifi_internal_free_rx_buffer(eb); + return ESP_OK; +} + +// Forward packets from Ethernet to Wi-Fi +// Note that, Ethernet works faster than Wi-Fi on ESP32, +// so we need to add an extra queue to balance their speed difference. +static esp_err_t pkt_eth2wifi(void *buffer, uint16_t len, void *eb) +{ + esp_err_t ret = ESP_OK; + flow_control_msg_t msg = { + .packet = buffer, + .length = len + }; + if (xQueueSend(flow_control_queue, &msg, pdMS_TO_TICKS(FLOW_CONTROL_QUEUE_TIMEOUT_MS)) != pdTRUE) { + ESP_LOGE(TAG, "send flow control message failed or timeout"); + ret = ESP_FAIL; + } + return ret; +} + +// This task will fetch the packet from the queue, and then send out through Wi-Fi. +// Wi-Fi handles packets slower than Ethernet, we might add some delay between each transmitting. +static void eth2wifi_flow_control_task(void *args) +{ + flow_control_msg_t msg; + int res = 0; + uint32_t timeout = 0; + while (1) { + if (xQueueReceive(flow_control_queue, &msg, pdMS_TO_TICKS(FLOW_CONTROL_QUEUE_TIMEOUT_MS)) == pdTRUE) { + timeout = 0; + if (s_sta_is_connected && msg.length > 4) { + do { + vTaskDelay(pdMS_TO_TICKS(timeout)); + timeout += 2; + res = esp_wifi_internal_tx(ESP_IF_WIFI_AP, msg.packet, msg.length - 4); + } while (res == -1 && timeout < FLOW_CONTROL_WIFI_SEND_TIMEOUT_MS); + if (res != ESP_OK) { + ESP_LOGE(TAG, "WiFi send packet failed: %d", res); + } + } + esp_eth_free_rx_buf(msg.packet); + } + } + vTaskDelete(NULL); +} + +// Event handler for Ethernet +static void eth_event_handler(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + switch (event_id) { + case ETHERNET_EVENT_CONNECTED: + ESP_LOGI(TAG, "Ethernet Link Up"); + s_ethernet_is_connected = true; + esp_eth_get_mac(s_eth_mac); + esp_wifi_set_mac(WIFI_IF_AP, s_eth_mac); + ESP_ERROR_CHECK(esp_wifi_start()); + break; + case ETHERNET_EVENT_DISCONNECTED: + ESP_LOGI(TAG, "Ethernet Link Down"); + s_ethernet_is_connected = false; + ESP_ERROR_CHECK(esp_wifi_stop()); + break; + case ETHERNET_EVENT_START: + ESP_LOGI(TAG, "Ethernet Started"); + break; + case ETHERNET_EVENT_STOP: + ESP_LOGI(TAG, "Ethernet Stopped"); + break; + default: + break; + } +} + +// Event handler for Wi-Fi +static void wifi_event_handler(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + switch (event_id) { + case WIFI_EVENT_AP_STACONNECTED: + ESP_LOGI(TAG, "Wi-Fi AP got a station connected"); + s_sta_is_connected = true; + esp_wifi_internal_reg_rxcb(ESP_IF_WIFI_AP, pkt_wifi2eth); + break; + case WIFI_EVENT_AP_STADISCONNECTED: + ESP_LOGI(TAG, "Wi-Fi AP got a station disconnected"); + s_sta_is_connected = false; + esp_wifi_internal_reg_rxcb(ESP_IF_WIFI_AP, NULL); + break; + default: + break; + } +} + +static void initialize_ethernet(void) +{ + eth_config_t config = DEFAULT_ETHERNET_PHY_CONFIG; + config.phy_addr = CONFIG_EXAMPLE_PHY_ADDRESS; + config.gpio_config = eth_gpio_config_rmii; + config.clock_mode = CONFIG_EXAMPLE_PHY_CLOCK_MODE; + config.tcpip_input = pkt_eth2wifi; + config.promiscuous_enable = true; +#ifdef CONFIG_EXAMPLE_PHY_USE_POWER_PIN + /* Replace the default 'power enable' function with an example-specific one that toggles a power GPIO. */ + config.phy_power_enable = phy_device_power_enable_via_gpio; +#endif + ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler, NULL)); + ESP_ERROR_CHECK(esp_eth_init_internal(&config)); + ESP_ERROR_CHECK(esp_eth_enable()); +} + +static void initialize_wifi(void) +{ + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, wifi_event_handler, NULL)); + ESP_ERROR_CHECK(esp_wifi_init_internal(&cfg)); + ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); + wifi_config_t wifi_config = { + .ap = { + .ssid = CONFIG_EXAMPLE_WIFI_SSID, + .ssid_len = strlen(CONFIG_EXAMPLE_WIFI_SSID), + .password = CONFIG_EXAMPLE_WIFI_PASSWORD, + .max_connection = CONFIG_EXAMPLE_MAX_STA_CONN, + .authmode = WIFI_AUTH_WPA_WPA2_PSK + }, + }; + if (strlen(CONFIG_EXAMPLE_WIFI_PASSWORD) == 0) { + wifi_config.ap.authmode = WIFI_AUTH_OPEN; + } + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP)); + ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_config)); +} + +static esp_err_t initialize_flow_control(void) +{ + flow_control_queue = xQueueCreate(FLOW_CONTROL_QUEUE_LENGTH, sizeof(flow_control_msg_t)); + if (!flow_control_queue) { + ESP_LOGE(TAG, "create flow control queue failed"); + return ESP_FAIL; + } + BaseType_t ret = xTaskCreate(eth2wifi_flow_control_task, "flow_ctl", 2048, NULL, (tskIDLE_PRIORITY + 2), NULL); + if (ret != pdTRUE) { + ESP_LOGE(TAG, "create flow control task failed"); + return ESP_FAIL; + } + return ESP_OK; +} + +void app_main() +{ + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK(ret); + ESP_ERROR_CHECK(esp_event_loop_create_default()); + ESP_ERROR_CHECK(initialize_flow_control()); + + initialize_ethernet(); + initialize_wifi(); +} diff --git a/examples/ethernet/eth2ap/sdkconfig.defaults b/examples/ethernet/eth2ap/sdkconfig.defaults new file mode 100644 index 0000000000..710208fe53 --- /dev/null +++ b/examples/ethernet/eth2ap/sdkconfig.defaults @@ -0,0 +1 @@ +CONFIG_ETH_EMAC_L2_TO_L3_RX_BUF_MODE=n From 430d9c6fba0ab328d3dd7b39a16ed6d40e48d7d8 Mon Sep 17 00:00:00 2001 From: redchenjs Date: Tue, 18 Jun 2019 13:52:36 +0800 Subject: [PATCH 103/486] i2s: fix a bug when calculating i2s apll parameters Closes https://github.com/espressif/esp-idf/issues/2634 Closes https://github.com/espressif/esp-idf/issues/3380 Fixes https://github.com/espressif/esp-idf/issues/3407 --- components/driver/i2s.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/components/driver/i2s.c b/components/driver/i2s.c index 02322b341a..2056c43f1f 100644 --- a/components/driver/i2s.c +++ b/components/driver/i2s.c @@ -253,7 +253,7 @@ static esp_err_t i2s_apll_calculate_fi2s(int rate, int bits_per_sample, int *sdm max_rate = i2s_apll_get_fi2s(bits_per_sample, 255, 255, _sdm2, 0); min_rate = i2s_apll_get_fi2s(bits_per_sample, 0, 0, _sdm2, 31); avg = (max_rate + min_rate)/2; - if(abs(avg - rate) < min_diff) { + if (abs(avg - rate) < min_diff) { min_diff = abs(avg - rate); *sdm2 = _sdm2; } @@ -263,11 +263,21 @@ static esp_err_t i2s_apll_calculate_fi2s(int rate, int bits_per_sample, int *sdm max_rate = i2s_apll_get_fi2s(bits_per_sample, 255, 255, *sdm2, _odir); min_rate = i2s_apll_get_fi2s(bits_per_sample, 0, 0, *sdm2, _odir); avg = (max_rate + min_rate)/2; - if(abs(avg - rate) < min_diff) { + if (abs(avg - rate) < min_diff) { min_diff = abs(avg - rate); *odir = _odir; } } + min_diff = APLL_MAX_FREQ; + for (_sdm2 = 4; _sdm2 < 9; _sdm2 ++) { + max_rate = i2s_apll_get_fi2s(bits_per_sample, 255, 255, _sdm2, *odir); + min_rate = i2s_apll_get_fi2s(bits_per_sample, 0, 0, _sdm2, *odir); + avg = (max_rate + min_rate)/2; + if (abs(avg - rate) < min_diff) { + min_diff = abs(avg - rate); + *sdm2 = _sdm2; + } + } min_diff = APLL_MAX_FREQ; for (_sdm1 = 0; _sdm1 < 256; _sdm1 ++) { From 070b86eee5cbeac19368d0212c01f56fd376e7b2 Mon Sep 17 00:00:00 2001 From: Ajita Chavan Date: Wed, 19 Jun 2019 16:06:20 +0530 Subject: [PATCH 104/486] i2s: test case for variation in apll clock rate --- components/driver/i2s.c | 9 +++++ components/driver/include/driver/i2s.h | 10 +++++ components/driver/test/test_i2s.c | 53 ++++++++++++++++++++++++++ 3 files changed, 72 insertions(+) diff --git a/components/driver/i2s.c b/components/driver/i2s.c index 2056c43f1f..56c418d153 100644 --- a/components/driver/i2s.c +++ b/components/driver/i2s.c @@ -89,6 +89,7 @@ typedef struct { bool use_apll; /*!< I2S use APLL clock */ bool tx_desc_auto_clear; /*!< I2S auto clear tx descriptor on underflow */ int fixed_mclk; /*!< I2S fixed MLCK clock */ + double real_rate; #ifdef CONFIG_PM_ENABLE esp_pm_lock_handle_t pm_lock; #endif @@ -178,6 +179,12 @@ esp_err_t i2s_enable_tx_intr(i2s_port_t i2s_num) return ESP_OK; } +float i2s_get_clk(i2s_port_t i2s_num) +{ + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); + return p_i2s_obj[i2s_num]->real_rate; +} + static esp_err_t i2s_isr_register(i2s_port_t i2s_num, int intr_alloc_flags, void (*fn)(void*), void * arg, i2s_isr_handle_t *handle) { return esp_intr_alloc(ETS_I2S0_INTR_SOURCE + i2s_num, intr_alloc_flags, fn, arg, handle); @@ -465,6 +472,7 @@ esp_err_t i2s_set_clk(i2s_port_t i2s_num, uint32_t rate, i2s_bits_per_sample_t b I2S[i2s_num]->sample_rate_conf.rx_bck_div_num = m_scale; I2S[i2s_num]->clkm_conf.clka_en = 1; double fi2s_rate = i2s_apll_get_fi2s(bits, sdm0, sdm1, sdm2, odir); + p_i2s_obj[i2s_num]->real_rate = fi2s_rate/bits/channel/m_scale; ESP_LOGI(I2S_TAG, "APLL: Req RATE: %d, real rate: %0.3f, BITS: %u, CLKM: %u, BCK_M: %u, MCLK: %0.3f, SCLK: %f, diva: %d, divb: %d", rate, fi2s_rate/bits/channel/m_scale, bits, 1, m_scale, fi2s_rate, fi2s_rate/8, 1, 0); } else { @@ -475,6 +483,7 @@ esp_err_t i2s_set_clk(i2s_port_t i2s_num, uint32_t rate, i2s_bits_per_sample_t b I2S[i2s_num]->sample_rate_conf.tx_bck_div_num = bck; I2S[i2s_num]->sample_rate_conf.rx_bck_div_num = bck; double real_rate = (double) (I2S_BASE_CLK / (bck * bits * clkmInteger) / 2); + p_i2s_obj[i2s_num]->real_rate = real_rate; ESP_LOGI(I2S_TAG, "PLL_D2: Req RATE: %d, real rate: %0.3f, BITS: %u, CLKM: %u, BCK: %u, MCLK: %0.3f, SCLK: %f, diva: %d, divb: %d", rate, real_rate, bits, clkmInteger, bck, (double)I2S_BASE_CLK / mclk, real_rate*bits*channel, 64, clkmDecimals); } diff --git a/components/driver/include/driver/i2s.h b/components/driver/include/driver/i2s.h index 59c11978e5..c00be6632f 100644 --- a/components/driver/include/driver/i2s.h +++ b/components/driver/include/driver/i2s.h @@ -501,6 +501,16 @@ esp_err_t i2s_zero_dma_buffer(i2s_port_t i2s_num); */ esp_err_t i2s_set_clk(i2s_port_t i2s_num, uint32_t rate, i2s_bits_per_sample_t bits, i2s_channel_t ch); +/** + * @brief get clock set on particular port number. + * + * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * + * @return + * - actual clock set by i2s driver + */ +float i2s_get_clk(i2s_port_t i2s_num); + /** * @brief Set built-in ADC mode for I2S DMA, this function will initialize ADC pad, * and set ADC parameters. diff --git a/components/driver/test/test_i2s.c b/components/driver/test/test_i2s.c index 3afb24412f..813a906cb5 100644 --- a/components/driver/test/test_i2s.c +++ b/components/driver/test/test_i2s.c @@ -9,6 +9,7 @@ #include "freertos/task.h" #include "driver/i2s.h" #include "unity.h" +#include "math.h" #define SAMPLE_RATE (36000) #define SAMPLE_BITS (16) @@ -18,6 +19,7 @@ #define SLAVE_WS_IO 26 #define DATA_IN_IO 21 #define DATA_OUT_IO 22 +#define PERCENT_DIFF 0.0001 /** * i2s initialize test @@ -267,3 +269,54 @@ TEST_CASE("I2S memory leaking test", "[i2s]") vTaskDelay(100 / portTICK_PERIOD_MS); TEST_ASSERT(initial_size == esp_get_free_heap_size()); } + +/* + * The I2S APLL clock variation test used to test the difference between the different sample rates, different bits per sample + * and the APLL clock generate for it. The TEST_CASE passes PERCENT_DIFF variation from the provided sample rate in APLL generated clock + * The percentage difference calculated as (mod((obtained clock rate - desired clock rate)/(desired clock rate))) * 100. + */ +TEST_CASE("I2S APLL clock variation test", "[i2s]") +{ + i2s_pin_config_t pin_config = { + .bck_io_num = MASTER_BCK_IO, + .ws_io_num = MASTER_WS_IO, + .data_out_num = DATA_OUT_IO, + .data_in_num = -1 + }; + + i2s_config_t i2s_config = { + .mode = I2S_MODE_MASTER | I2S_MODE_TX, + .sample_rate = SAMPLE_RATE, + .bits_per_sample = SAMPLE_BITS, + .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, + .communication_format = I2S_COMM_FORMAT_I2S, + .dma_buf_count = 6, + .dma_buf_len = 60, + .use_apll = true, + .intr_alloc_flags = 0, + }; + + TEST_ESP_OK(i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL)); + TEST_ESP_OK(i2s_set_pin(I2S_NUM_0, &pin_config)); + TEST_ESP_OK(i2s_driver_uninstall(I2S_NUM_0)); + int initial_size = esp_get_free_heap_size(); + + uint32_t sample_rate_arr[8] = { 10675, 11025, 16000, 22050, 32000, 44100, 48000, 96000 }; + int bits_per_sample_arr[3] = { 16, 24, 32 }; + + for (int i = 0; i < (sizeof(sample_rate_arr)/sizeof(sample_rate_arr[0])); i++) { + for (int j = 0; j < (sizeof(bits_per_sample_arr)/sizeof(bits_per_sample_arr[0])); j++) { + i2s_config.sample_rate = sample_rate_arr[i]; + i2s_config.bits_per_sample = bits_per_sample_arr[j]; + + TEST_ESP_OK(i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL)); + TEST_ESP_OK(i2s_set_pin(I2S_NUM_0, &pin_config)); + TEST_ASSERT((fabs((i2s_get_clk(I2S_NUM_0) - sample_rate_arr[i]))/(sample_rate_arr[i]))*100 < PERCENT_DIFF); + TEST_ESP_OK(i2s_driver_uninstall(I2S_NUM_0)); + TEST_ASSERT(initial_size == esp_get_free_heap_size()); + } + } + + vTaskDelay(100 / portTICK_PERIOD_MS); + TEST_ASSERT(initial_size == esp_get_free_heap_size()); +} From e0a652f164c49a726edbe8d9a2559001e967e86d Mon Sep 17 00:00:00 2001 From: Renz Christian Bagaporo Date: Sun, 5 May 2019 15:42:39 +0800 Subject: [PATCH 105/486] spiffs: add ability to specify dependencies when dirs themselves are generated --- components/spiffs/Makefile.projbuild | 2 +- components/spiffs/project_include.cmake | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/components/spiffs/Makefile.projbuild b/components/spiffs/Makefile.projbuild index 13b2223a9a..645856c497 100644 --- a/components/spiffs/Makefile.projbuild +++ b/components/spiffs/Makefile.projbuild @@ -20,7 +20,7 @@ endif define spiffs_create_partition_image -$(1)_bin: $(PARTITION_TABLE_BIN) | check_python_dependencies +$(1)_bin: $(PARTITION_TABLE_BIN) $(DEPENDS) | check_python_dependencies partition_size=`$(GET_PART_INFO) \ --partition-table-file $(PARTITION_TABLE_BIN) \ get_partition_info --partition-name $(1) --info size`; \ diff --git a/components/spiffs/project_include.cmake b/components/spiffs/project_include.cmake index 19fc96ed7a..31f63b5111 100644 --- a/components/spiffs/project_include.cmake +++ b/components/spiffs/project_include.cmake @@ -4,7 +4,8 @@ # have the created image flashed using `idf.py flash` function(spiffs_create_partition_image partition base_dir) set(options FLASH_IN_PROJECT) - cmake_parse_arguments(arg "${options}" "" "" "${ARGN}") + set(multi DEPENDS) + cmake_parse_arguments(arg "${options}" "" "${multi}" "${ARGN}") idf_build_get_property(idf_path IDF_PATH) set(spiffsgen_py ${PYTHON} ${idf_path}/components/spiffs/spiffsgen.py) @@ -33,6 +34,7 @@ function(spiffs_create_partition_image partition base_dir) --meta-len=${CONFIG_SPIFFS_META_LENGTH} ${use_magic} ${use_magic_len} + DEPENDS ${arg_DEPENDS} ) set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" APPEND PROPERTY From c65038fd7414bac3cf3f71a929b090e3b0c15c86 Mon Sep 17 00:00:00 2001 From: Renz Christian Bagaporo Date: Fri, 21 Jun 2019 10:58:00 +0800 Subject: [PATCH 106/486] spiffs,make: change spiffsgen build API --- components/spiffs/Makefile.projbuild | 7 +++-- docs/en/api-reference/storage/spiffs.rst | 38 +++++++++++++++++++----- examples/storage/spiffsgen/Makefile | 3 +- 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/components/spiffs/Makefile.projbuild b/components/spiffs/Makefile.projbuild index 645856c497..4d8f39c081 100644 --- a/components/spiffs/Makefile.projbuild +++ b/components/spiffs/Makefile.projbuild @@ -19,8 +19,7 @@ endif # have the created image flashed using `make flash` define spiffs_create_partition_image - -$(1)_bin: $(PARTITION_TABLE_BIN) $(DEPENDS) | check_python_dependencies +$(1)_bin: $(PARTITION_TABLE_BIN) $(SPIFFS_IMAGE_DEPENDS) | check_python_dependencies partition_size=`$(GET_PART_INFO) \ --partition-table-file $(PARTITION_TABLE_BIN) \ get_partition_info --partition-name $(1) --info size`; \ @@ -35,9 +34,11 @@ all_binaries: $(1)_bin print_flash_cmd: $(1)_bin # Append the created binary to esptool_py args if FLASH_IN_PROJECT is set -ifeq ($(3), FLASH_IN_PROJECT) +ifdef SPIFFS_IMAGE_FLASH_IN_PROJECT +ifeq ($(SPIFFS_IMAGE_FLASH_IN_PROJECT),1) SPIFFSGEN_FLASH_IN_PROJECT += $(1) endif +endif endef ESPTOOL_ALL_FLASH_ARGS += $(foreach partition,$(SPIFFSGEN_FLASH_IN_PROJECT), \ diff --git a/docs/en/api-reference/storage/spiffs.rst b/docs/en/api-reference/storage/spiffs.rst index dcd31a022f..5620e83d77 100644 --- a/docs/en/api-reference/storage/spiffs.rst +++ b/docs/en/api-reference/storage/spiffs.rst @@ -44,26 +44,50 @@ Aside from invoking the ``spiffsgen.py`` standalone by manually running it from Make:: - $(eval $(call spiffs_create_partition_image,,,[FLASH_IN_PROJECT])) + SPIFFS_IMAGE_FLASH_IN_PROJECT := ... + SPIFFS_IMAGE_DEPENDS := ... + $(eval $(call spiffs_create_partition_image,,)) CMake:: - spiffs_create_partition_image( [FLASH_IN_PROJECT]) + spiffs_create_partition_image( [FLASH_IN_PROJECT] [DEPENDS dep dep dep...]) This is more convenient as the build configuration is automatically passed to the tool, ensuring that the generated image is valid for that build. An example of this is while the *image_size* is required for the standalone invocation, only the *partition* name is required when using ``spiffs_create_partition_image`` -- the image size is automatically obtained from the project's partition table. -Due to the differences in structure between Make and Cmake, it is important to note that: +Due to the differences in structure between Make and CMake, it is important to note that: - for Make ``spiffs_create_partition_image`` must be called from the project Makefile - for CMake ``spiffs_create_partition_image`` must be called from one of the component CMakeLists.txt files -For both build systems, the image will be created in the build directory with the filename *partition*.bin. +Optionally, user can opt to have the image automatically flashed together with the app binaries, partition tables, etc. on +``idf.py flash`` or ``make flash`` by specifying ``FLASH_IN_PROJECT``. For example, -Optionally, you can opt to have the image automatically flashed together with the app binaries, partition tables, etc., with -``idf.py flash`` or ``make flash`` by specifying ``FLASH_IN_PROJECT``. For example:: +in Make:: + + SPIFFS_IMAGE_FLASH_IN_PROJECT := 1 + $(eval $(call spiffs_create_partition_image,,)) + +in CMake:: spiffs_create_partition_image(my_spiffs_partition my_folder FLASH_IN_PROJECT) -If FLASH_IN_PROJECT is not specified, the image will still be generated, but you will have to flash it manually using ``esptool.py``, ``parttool.py``, or a custom build system target. +If FLASH_IN_PROJECT/SPIFFS_IMAGE_FLASH_IN_PROJECT is not specified, the image will still be generated, but you will have to flash it manually using ``esptool.py``, ``parttool.py``, or a custom build system target. + +There are cases where the contents of the base directory itself is generated at build time. Users can use DEPENDS/SPIFFS_IMAGE_DEPENDS to specify targets +that should be executed before generating the image. + +in Make:: + + dep: + ... + + SPIFFS_IMAGE_DEPENDS := dep + $(eval $(call spiffs_create_partition_image,,)) + +in CMake:: + + add_custom_target(dep COMMAND ...) + + spiffs_create_partition_image(my_spiffs_partition my_folder DEPENDS dep) +For an example, see :example:`examples/storage/spiffsgen>`. diff --git a/examples/storage/spiffsgen/Makefile b/examples/storage/spiffsgen/Makefile index 07cc03d6dd..300abdd6e8 100644 --- a/examples/storage/spiffsgen/Makefile +++ b/examples/storage/spiffsgen/Makefile @@ -12,4 +12,5 @@ include $(IDF_PATH)/make/project.mk # that fits the partition named 'storage'. FLASH_IN_PROJECT indicates that # the generated image should be flashed when the entire project is flashed to # the target with 'make flash'. -$(eval $(call spiffs_create_partition_image,storage,spiffs_image,FLASH_IN_PROJECT)) \ No newline at end of file +SPIFFS_IMAGE_FLASH_IN_PROJECT := 1 +$(eval $(call spiffs_create_partition_image,storage,spiffs_image)) \ No newline at end of file From 149e07911decbeecc917f225ec6721149015887e Mon Sep 17 00:00:00 2001 From: baohongde Date: Fri, 21 Jun 2019 11:55:45 +0800 Subject: [PATCH 107/486] components/bt: Optimization and bugfix of previous commits --- components/bt/bluedroid/bta/dm/bta_dm_act.c | 2 + components/bt/bluedroid/bta/dm/bta_dm_cfg.c | 2 +- components/bt/bluedroid/bta/dm/bta_dm_main.c | 2 + .../bt/bluedroid/bta/dm/include/bta_dm_int.h | 2 + .../bt/bluedroid/bta/gatt/bta_gattc_co.c | 2 +- .../bt/bluedroid/bta/include/bta/bta_sys.h | 2 +- .../bt/bluedroid/bta/sys/bta_sys_main.c | 4 +- components/bt/bluedroid/btc/core/btc_task.c | 116 +++-- .../bluedroid/btc/profile/std/avrc/btc_avrc.c | 2 - .../btc/profile/std/include/btc_avrc.h | 2 +- .../common/include/common/bt_user_config.h | 17 +- components/bt/bluedroid/device/controller.c | 4 +- components/bt/bluedroid/main/bte_init.c | 440 ++++++++++-------- components/bt/bluedroid/stack/a2dp/a2d_api.c | 14 +- components/bt/bluedroid/stack/avrc/avrc_sdp.c | 14 +- .../bt/bluedroid/stack/btm/btm_ble_gap.c | 8 +- .../bt/bluedroid/stack/btm/btm_devctl.c | 2 + components/bt/bluedroid/stack/btm/btm_main.c | 12 +- .../bt/bluedroid/stack/btm/include/btm_int.h | 8 +- components/bt/bluedroid/stack/btu/btu_init.c | 11 +- components/bt/bluedroid/stack/btu/btu_task.c | 61 ++- components/bt/bluedroid/stack/gap/gap_api.c | 15 +- .../bluedroid/stack/include/stack/a2d_api.h | 3 +- .../bluedroid/stack/include/stack/avrc_api.h | 5 +- .../bt/bluedroid/stack/include/stack/btu.h | 2 +- .../bluedroid/stack/include/stack/gap_api.h | 4 +- .../bluedroid/stack/include/stack/port_api.h | 3 +- .../bt/bluedroid/stack/rfcomm/port_api.c | 14 +- components/bt/bluedroid/stack/smp/smp_utils.c | 9 +- 29 files changed, 451 insertions(+), 331 deletions(-) diff --git a/components/bt/bluedroid/bta/dm/bta_dm_act.c b/components/bt/bluedroid/bta/dm/bta_dm_act.c index a6fefeacfe..acb6f6291f 100644 --- a/components/bt/bluedroid/bta/dm/bta_dm_act.c +++ b/components/bt/bluedroid/bta/dm/bta_dm_act.c @@ -1298,7 +1298,9 @@ void bta_dm_loc_oob(tBTA_DM_MSG *p_data) *******************************************************************************/ void bta_dm_oob_reply(tBTA_DM_MSG *p_data) { +#if (BLE_INCLUDED) BTM_BleOobDataReply(p_data->oob_reply.bd_addr, BTM_SUCCESS, p_data->oob_reply.len, p_data->oob_reply.value); +#endif } /******************************************************************************* diff --git a/components/bt/bluedroid/bta/dm/bta_dm_cfg.c b/components/bt/bluedroid/bta/dm/bta_dm_cfg.c index 9e018de98d..6769e7141e 100644 --- a/components/bt/bluedroid/bta/dm/bta_dm_cfg.c +++ b/components/bt/bluedroid/bta/dm/bta_dm_cfg.c @@ -441,4 +441,4 @@ tBTA_DM_EIR_CONF bta_dm_eir_cfg = { NULL #endif /* #if (BTC_GAP_BT_INCLUDED == TRUE) */ }; -tBTA_DM_EIR_CONF *const p_bta_dm_eir_cfg = (tBTA_DM_EIR_CONF *) &bta_dm_eir_cfg; +tBTA_DM_EIR_CONF *p_bta_dm_eir_cfg = (tBTA_DM_EIR_CONF *) &bta_dm_eir_cfg; diff --git a/components/bt/bluedroid/bta/dm/bta_dm_main.c b/components/bt/bluedroid/bta/dm/bta_dm_main.c index 3b9a7cc4ce..d62974ea2e 100644 --- a/components/bt/bluedroid/bta/dm/bta_dm_main.c +++ b/components/bt/bluedroid/bta/dm/bta_dm_main.c @@ -160,7 +160,9 @@ const tBTA_DM_ACTION bta_dm_action[BTA_DM_MAX_EVT] = { bta_dm_update_white_list, /* BTA_DM_API_UPDATE_WHITE_LIST_EVT */ bta_dm_ble_read_adv_tx_power, /* BTA_DM_API_BLE_READ_ADV_TX_POWER_EVT */ bta_dm_ble_read_rssi, /* BTA_DM_API_BLE_READ_RSSI_EVT */ +#if BLE_INCLUDED == TRUE bta_dm_ble_update_duplicate_exceptional_list,/* BTA_DM_API_UPDATE_DUPLICATE_EXCEPTIONAL_LIST_EVT */ +#endif }; diff --git a/components/bt/bluedroid/bta/dm/include/bta_dm_int.h b/components/bt/bluedroid/bta/dm/include/bta_dm_int.h index 4fc8e650ce..c87708e358 100644 --- a/components/bt/bluedroid/bta/dm/include/bta_dm_int.h +++ b/components/bt/bluedroid/bta/dm/include/bta_dm_int.h @@ -157,7 +157,9 @@ enum { BTA_DM_API_UPDATE_WHITE_LIST_EVT, BTA_DM_API_BLE_READ_ADV_TX_POWER_EVT, BTA_DM_API_BLE_READ_RSSI_EVT, +#if BLE_INCLUDED == TRUE BTA_DM_API_UPDATE_DUPLICATE_EXCEPTIONAL_LIST_EVT, +#endif BTA_DM_MAX_EVT }; diff --git a/components/bt/bluedroid/bta/gatt/bta_gattc_co.c b/components/bt/bluedroid/bta/gatt/bta_gattc_co.c index 06c51b07e8..a336c23147 100644 --- a/components/bt/bluedroid/bta/gatt/bta_gattc_co.c +++ b/components/bt/bluedroid/bta/gatt/bta_gattc_co.c @@ -269,8 +269,8 @@ tBTA_GATT_STATUS bta_gattc_co_cache_load(tBTA_GATTC_NV_ATTR *attr, UINT8 index) // Read the size of memory space required for blob nvs_get_blob(cache_env->cache_addr[index].cache_fp, cache_key, NULL, &length); // Read previously saved blob if available -#if (!CONFIG_BT_STACK_NO_LOG) esp_err_t err_code = nvs_get_blob(cache_env->cache_addr[index].cache_fp, cache_key, attr, &length); +#if (!CONFIG_BT_STACK_NO_LOG) num_attr = length / sizeof(tBTA_GATTC_NV_ATTR); #endif status = (err_code == ESP_OK && length != 0) ? BTA_GATT_OK : BTA_GATT_ERROR; diff --git a/components/bt/bluedroid/bta/include/bta/bta_sys.h b/components/bt/bluedroid/bta/include/bta/bta_sys.h index a58773de73..a466028b95 100644 --- a/components/bt/bluedroid/bta/include/bta/bta_sys.h +++ b/components/bt/bluedroid/bta/include/bta/bta_sys.h @@ -215,7 +215,7 @@ extern "C" { extern void bta_sys_init(void); extern void bta_sys_free(void); -extern void bta_sys_event(BT_HDR *p_msg); +extern void bta_sys_event(void * param); extern void bta_sys_set_trace_level(UINT8 level); extern void bta_sys_register(UINT8 id, const tBTA_SYS_REG *p_reg); extern void bta_sys_deregister(UINT8 id); diff --git a/components/bt/bluedroid/bta/sys/bta_sys_main.c b/components/bt/bluedroid/bta/sys/bta_sys_main.c index c00abd7394..1ea2ccd702 100644 --- a/components/bt/bluedroid/bta/sys/bta_sys_main.c +++ b/components/bt/bluedroid/bta/sys/bta_sys_main.c @@ -482,8 +482,10 @@ void bta_sys_hw_evt_stack_enabled(tBTA_SYS_HW_MSG *p_sys_hw_msg) ** Returns void ** *******************************************************************************/ -void bta_sys_event(BT_HDR *p_msg) +void bta_sys_event(void * param) { + BT_HDR *p_msg = (BT_HDR *)param; + UINT8 id; BOOLEAN freebuf = TRUE; diff --git a/components/bt/bluedroid/btc/core/btc_task.c b/components/bt/bluedroid/btc/core/btc_task.c index cb8616d9b0..df1e1ddfdc 100644 --- a/components/bt/bluedroid/btc/core/btc_task.c +++ b/components/bt/bluedroid/btc/core/btc_task.c @@ -176,95 +176,121 @@ bt_status_t btc_transfer_context(btc_msg_t *msg, void *arg, int arg_len, btc_arg } #if BTC_DYNAMIC_MENDRY + +static void btc_deinit_mem(void) { + if (btc_dm_cb_ptr) { + osi_free(btc_dm_cb_ptr); + btc_dm_cb_ptr = NULL; + } + + if (btc_profile_cb_tab) { + osi_free(btc_profile_cb_tab); + btc_profile_cb_tab = NULL; + } + +#if (BLE_INCLUDED == TRUE) + if (gl_bta_adv_data_ptr) { + osi_free(gl_bta_adv_data_ptr); + gl_bta_adv_data_ptr = NULL; + } + + if (gl_bta_scan_rsp_data_ptr) { + osi_free(gl_bta_scan_rsp_data_ptr); + gl_bta_scan_rsp_data_ptr = NULL; + } +#endif ///BLE_INCLUDED == TRUE + +#if GATTS_INCLUDED == TRUE && GATT_DYNAMIC_MEMORY == TRUE + if (btc_creat_tab_env_ptr) { + osi_free(btc_creat_tab_env_ptr); + btc_creat_tab_env_ptr = NULL; + } + + if (blufi_env_ptr) { + osi_free(blufi_env_ptr); + blufi_env_ptr = NULL; + } +#endif + +#if BTC_HF_CLIENT_INCLUDED == TRUE && HFP_DYNAMIC_MEMORY == TRUE + if (hf_client_local_param_ptr) { + osi_free(hf_client_local_param_ptr); + hf_client_local_param_ptr = NULL; + } +#endif + +#if BTC_AV_INCLUDED == TRUE && AVRC_DYNAMIC_MEMORY == TRUE + if (btc_rc_cb_ptr) { + osi_free(btc_rc_cb_ptr); + btc_rc_cb_ptr = NULL; + } + if (bta_av_co_cb_ptr) { + osi_free(bta_av_co_cb_ptr); + bta_av_co_cb_ptr = NULL; + } +#endif +} + static bt_status_t btc_init_mem(void) { if ((btc_dm_cb_ptr = (btc_dm_cb_t *)osi_malloc(sizeof(btc_dm_cb_t))) == NULL) { - return BT_STATUS_NOMEM; + goto error_exit; } memset((void *)btc_dm_cb_ptr, 0, sizeof(btc_dm_cb_t)); if ((btc_profile_cb_tab = (void **)osi_malloc(sizeof(void *) * BTC_PID_NUM)) == NULL) { - return BT_STATUS_NOMEM; + goto error_exit; } memset((void *)btc_profile_cb_tab, 0, sizeof(void *) * BTC_PID_NUM); #if (BLE_INCLUDED == TRUE) if ((gl_bta_adv_data_ptr = (tBTA_BLE_ADV_DATA *)osi_malloc(sizeof(tBTA_BLE_ADV_DATA))) == NULL) { - return BT_STATUS_NOMEM; + goto error_exit; } memset((void *)gl_bta_adv_data_ptr, 0, sizeof(tBTA_BLE_ADV_DATA)); if ((gl_bta_scan_rsp_data_ptr = (tBTA_BLE_ADV_DATA *)osi_malloc(sizeof(tBTA_BLE_ADV_DATA))) == NULL) { - return BT_STATUS_NOMEM; + goto error_exit; } memset((void *)gl_bta_scan_rsp_data_ptr, 0, sizeof(tBTA_BLE_ADV_DATA)); #endif ///BLE_INCLUDED == TRUE #if GATTS_INCLUDED == TRUE && GATT_DYNAMIC_MEMORY == TRUE if ((btc_creat_tab_env_ptr = (esp_btc_creat_tab_t *)osi_malloc(sizeof(esp_btc_creat_tab_t))) == NULL) { - return BT_STATUS_NOMEM; + goto error_exit; } memset((void *)btc_creat_tab_env_ptr, 0, sizeof(esp_btc_creat_tab_t)); if ((blufi_env_ptr = (tBLUFI_ENV *)osi_malloc(sizeof(tBLUFI_ENV))) == NULL) { - return BT_STATUS_NOMEM; + goto error_exit; } memset((void *)blufi_env_ptr, 0, sizeof(tBLUFI_ENV)); #endif #if BTC_HF_CLIENT_INCLUDED == TRUE && HFP_DYNAMIC_MEMORY == TRUE if ((hf_client_local_param_ptr = (hf_client_local_param_t *)osi_malloc(sizeof(hf_client_local_param_t))) == NULL) { - return BT_STATUS_NOMEM; + goto error_exit; } memset((void *)hf_client_local_param_ptr, 0, sizeof(hf_client_local_param_t)); #endif #if BTC_AV_INCLUDED == TRUE && AVRC_DYNAMIC_MEMORY == TRUE - if ((btc_rc_vb_ptr = (btc_rc_cb_t *)osi_malloc(sizeof(btc_rc_cb_t))) == NULL) { - return BT_STATUS_NOMEM; + if ((btc_rc_cb_ptr = (btc_rc_cb_t *)osi_malloc(sizeof(btc_rc_cb_t))) == NULL) { + goto error_exit; } - memset((void *)btc_rc_vb_ptr, 0, sizeof(btc_rc_cb_t)); + memset((void *)btc_rc_cb_ptr, 0, sizeof(btc_rc_cb_t)); if ((bta_av_co_cb_ptr = (tBTA_AV_CO_CB *)osi_malloc(sizeof(tBTA_AV_CO_CB))) == NULL) { - return BT_STATUS_NOMEM; + goto error_exit; } memset((void *)bta_av_co_cb_ptr, 0, sizeof(tBTA_AV_CO_CB)); #endif return BT_STATUS_SUCCESS; + +error_exit:; + btc_deinit_mem(); + return BT_STATUS_NOMEM; } - -static void btc_deinit_mem(void) { - osi_free(btc_dm_cb_ptr); - btc_dm_cb_ptr = NULL; - - osi_free(btc_profile_cb_tab); - btc_profile_cb_tab = NULL; - - osi_free(gl_bta_adv_data_ptr); - gl_bta_adv_data_ptr = NULL; - - osi_free(gl_bta_scan_rsp_data_ptr); - gl_bta_scan_rsp_data_ptr = NULL; - -#if GATTS_INCLUDED == TRUE && GATT_DYNAMIC_MEMORY == TRUE - osi_free(btc_creat_tab_env_ptr); - btc_creat_tab_env_ptr = NULL; - osi_free(blufi_env_ptr); - blufi_env_ptr = NULL; -#endif - -#if BTC_HF_CLIENT_INCLUDED == TRUE && HFP_DYNAMIC_MEMORY == TRUE - osi_free(hf_client_local_param_ptr); - hf_client_local_param_ptr = NULL; -#endif - -#if BTC_AV_INCLUDED == TRUE && AVRC_DYNAMIC_MEMORY == TRUE - osi_free(btc_rc_vb_ptr); - btc_rc_vb_ptr = NULL; - osi_free(bta_av_co_cb_ptr); - bta_av_co_cb_ptr = NULL; -#endif -} -#endif +#endif ///BTC_DYNAMIC_MENDRY int btc_init(void) { diff --git a/components/bt/bluedroid/btc/profile/std/avrc/btc_avrc.c b/components/bt/bluedroid/btc/profile/std/avrc/btc_avrc.c index 841d55c360..28ecb85e63 100644 --- a/components/bt/bluedroid/btc/profile/std/avrc/btc_avrc.c +++ b/components/bt/bluedroid/btc/profile/std/avrc/btc_avrc.c @@ -1002,8 +1002,6 @@ static void btc_avrc_ct_init(void) /// initialize CT-TG shared resources if (s_rc_tg_init != BTC_RC_TG_INIT_MAGIC) { memset (&btc_rc_cb, 0, sizeof(btc_rc_cb_t)); - btc_rc_cb.rc_vol_label = MAX_LABEL; - btc_rc_cb.rc_volume = MAX_VOLUME; } } diff --git a/components/bt/bluedroid/btc/profile/std/include/btc_avrc.h b/components/bt/bluedroid/btc/profile/std/include/btc_avrc.h index 7d72393d82..ec98c5ee56 100644 --- a/components/bt/bluedroid/btc/profile/std/include/btc_avrc.h +++ b/components/bt/bluedroid/btc/profile/std/include/btc_avrc.h @@ -98,7 +98,7 @@ typedef enum { #define CHECK_ESP_RC_CONNECTED do { \ BTC_TRACE_DEBUG("## %s ##", __FUNCTION__); \ - if (btc_rc_vb.rc_connected == FALSE) { \ + if (btc_rc_cb.rc_connected == FALSE) { \ BTC_TRACE_WARNING("Function %s() called when RC is not connected", __FUNCTION__); \ return ESP_ERR_INVALID_STATE; \ } \ diff --git a/components/bt/bluedroid/common/include/common/bt_user_config.h b/components/bt/bluedroid/common/include/common/bt_user_config.h index 5fdc1c2183..7fa8ed5c51 100644 --- a/components/bt/bluedroid/common/include/common/bt_user_config.h +++ b/components/bt/bluedroid/common/include/common/bt_user_config.h @@ -15,9 +15,6 @@ #ifndef __BT_USER_CONFIG_H__ #define __BT_USER_CONFIG_H__ - - - /* All the configuration from SDK defined here */ #include "sdkconfig.h" @@ -86,6 +83,7 @@ #define UC_BT_SSP_ENABLED CONFIG_BT_SSP_ENABLED #else #define UC_BT_SSP_ENABLED FALSE +#endif //BLE #ifdef CONFIG_BT_BLE_ENABLED @@ -96,16 +94,16 @@ //GATTS #ifdef CONFIG_BT_GATTS_ENABLE -#define UC_BT_GATTS_ENABLED CONFIG_BT_GATTS_ENABLE +#define UC_BT_GATTS_ENABLE CONFIG_BT_GATTS_ENABLE #else -#define UC_BT_GATTS_ENABLED FALSE +#define UC_BT_GATTS_ENABLE FALSE #endif //GATTC #ifdef CONFIG_BT_GATTC_ENABLE -#define UC_BT_GATTC_ENABLED CONFIG_BT_GATTC_ENABLE +#define UC_BT_GATTC_ENABLE CONFIG_BT_GATTC_ENABLE #else -#define UC_BT_GATTC_ENABLED FALSE +#define UC_BT_GATTC_ENABLE FALSE #endif //GATTC CACHE @@ -117,9 +115,9 @@ //SMP #ifdef CONFIG_BT_SMP_ENABLE -#define UC_BT_SMP_ENABLED CONFIG_BT_SMP_ENABLE +#define UC_BT_SMP_ENABLE CONFIG_BT_SMP_ENABLE #else -#define UC_BT_SMP_ENABLED FALSE +#define UC_BT_SMP_ENABLE FALSE #endif //SMP_SLAVE_CON_PARAMS_UPD_ENABLE @@ -371,7 +369,6 @@ #define UC_BT_LOG_BLUFI_TRACE_LEVEL UC_TRACE_LEVEL_WARNING #endif - #endif /* __BT_USER_CONFIG_H__ */ diff --git a/components/bt/bluedroid/device/controller.c b/components/bt/bluedroid/device/controller.c index df6c5018f6..84d6f59737 100644 --- a/components/bt/bluedroid/device/controller.c +++ b/components/bt/bluedroid/device/controller.c @@ -111,8 +111,8 @@ static void start_up(void) #endif ///C2H_FLOW_CONTROL_INCLUDED == TRUE #if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE) // Enable adv flow control - response = AWAIT_COMMAND(packet_factory->make_set_adv_report_flow_control(HCI_HOST_FLOW_CTRL_ADV_REPORT_ON, (uint16_t)BLE_ADV_REPORT_FLOW_CONTROL_NUM, (uint16_t)BLE_ADV_REPORT_DISCARD_THRSHOLD)); - packet_parser->parse_generic_command_complete(response); + response = AWAIT_COMMAND(controller_param.packet_factory->make_set_adv_report_flow_control(HCI_HOST_FLOW_CTRL_ADV_REPORT_ON, (uint16_t)BLE_ADV_REPORT_FLOW_CONTROL_NUM, (uint16_t)BLE_ADV_REPORT_DISCARD_THRSHOLD)); + controller_param.packet_parser->parse_generic_command_complete(response); #endif // Tell the controller about our buffer sizes and buffer counts next // TODO(zachoverflow): factor this out. eww l2cap contamination. And why just a hardcoded 10? diff --git a/components/bt/bluedroid/main/bte_init.c b/components/bt/bluedroid/main/bte_init.c index 1d6e994291..7f3723ae55 100644 --- a/components/bt/bluedroid/main/bte_init.c +++ b/components/bt/bluedroid/main/bte_init.c @@ -143,168 +143,6 @@ ** F U N C T I O N S * ******************************************************************************/ -/***************************************************************************** -** -** Function BTE_InitStack -** -** Description Initialize control block memory for each component. -** -** Note: The core stack components must be called -** before creating the BTU Task. The rest of the -** components can be initialized at a later time if desired -** as long as the component's init function is called -** before accessing any of its functions. -** -** Returns void -** -******************************************************************************/ -void BTE_InitStack(void) -{ -#if (defined(RFCOMM_INCLUDED) && RFCOMM_INCLUDED == TRUE) - //Initialize the optional stack components - RFCOMM_Init(); -#endif - - //BNEP and its profiles -#if (defined(BNEP_INCLUDED) && BNEP_INCLUDED == TRUE) - BNEP_Init(); - -#if (defined(PAN_INCLUDED) && PAN_INCLUDED == TRUE) - PAN_Init(); -#endif // PAN -#endif // BNEP Included - - //AVDT and its profiles -#if (defined(A2D_INCLUDED) && A2D_INCLUDED == TRUE) - A2D_Init(); -#endif // AADP - -#if (defined(AVRC_INCLUDED) && AVRC_INCLUDED == TRUE) - AVRC_Init(); -#endif - -#if (defined(AVDT_INCLUDED) && AVDT_INCLUDED == TRUE && AVDT_DYNAMIC_MEMORY == TRUE) - if ((avdt_cb_ptr = (tAVDT_CB *)osi_malloc(sizeof(tAVDT_CB))) == NULL) { - return; - } - memset((void *)avdt_cb_ptr, 0, sizeof(tAVDT_CB)); -#endif - -#if (defined(AVCT_INCLUDED) && AVCT_INCLUDED == TRUE && AVCT_DYNAMIC_MEMORY == TRUE) - if ((avct_cb_ptr = (tAVCT_CB *)osi_malloc(sizeof(tAVCT_CB))) == NULL) { - return; - } - memset((void *)avct_cb_ptr, 0, sizeof(tAVCT_CB)); -#endif - -#if (defined(GAP_INCLUDED) && GAP_INCLUDED == TRUE) - GAP_Init(); -#endif - -#if (defined(HID_HOST_INCLUDED) && HID_HOST_INCLUDED == TRUE) - HID_HostInit(); -#endif - -#if (defined(MCA_INCLUDED) && MCA_INCLUDED == TRUE) - MCA_Init(); -#endif - - //BTA Modules -#if (BTA_INCLUDED == TRUE && BTA_DYNAMIC_MEMORY == TRUE) - if ((bta_sys_cb_ptr = (tBTA_SYS_CB *)osi_malloc(sizeof(tBTA_SYS_CB))) == NULL) { - return; - } - if ((bta_dm_cb_ptr = (tBTA_DM_CB *)osi_malloc(sizeof(tBTA_DM_CB))) == NULL) { - return; - } - if ((bta_dm_search_cb_ptr = (tBTA_DM_SEARCH_CB *)osi_malloc(sizeof(tBTA_DM_SEARCH_CB))) == NULL) { - return; - } - if ((bta_dm_di_cb_ptr = (tBTA_DM_DI_CB *)osi_malloc(sizeof(tBTA_DM_DI_CB))) == NULL) { - return; - } - if ((bta_dm_conn_srvcs_ptr = (tBTA_DM_CONNECTED_SRVCS *)osi_malloc(sizeof(tBTA_DM_CONNECTED_SRVCS))) == NULL) { - return; - } - memset((void *)bta_sys_cb_ptr, 0, sizeof(tBTA_SYS_CB)); - memset((void *)bta_dm_cb_ptr, 0, sizeof(tBTA_DM_CB)); - memset((void *)bta_dm_search_cb_ptr, 0, sizeof(tBTA_DM_SEARCH_CB)); - memset((void *)bta_dm_di_cb_ptr, 0, sizeof(tBTA_DM_DI_CB)); - memset((void *)bta_dm_conn_srvcs_ptr, 0, sizeof(tBTA_DM_CONNECTED_SRVCS)); - //memset((void *)bta_prm_cb_ptr, 0, sizeof(tBTA_PRM_CB)); - -#if (defined BTA_HF_INCLUDED && BTA_HF_INCLUDED == TRUE) - if ((bta_hf_client_cb_ptr = (tBTA_HF_CLIENT_CB *)osi_malloc(sizeof(tBTA_HF_CLIENT_CB))) == NULL) { - return; - } - memset((void *)bta_hf_client_cb_ptr, 0, sizeof(tBTA_HF_CLIENT_CB)); -#endif -#if (defined BTA_JV_INCLUDED && BTA_JV_INCLUDED == TRUE) - if ((bta_jv_cb_ptr = (tBTA_JV_CB *)osi_malloc(sizeof(tBTA_JV_CB))) == NULL) { - return; - } - memset((void *)bta_jv_cb_ptr, 0, sizeof(tBTA_JV_CB)); -#endif //JV -#if BTA_HS_INCLUDED == TRUE - memset((void *)bta_hs_cb_ptr, 0, sizeof(tBTA_HS_CB)); -#endif -#if BTA_SDP_INCLUDED == TRUE - if ((bta_sdp_cb_ptr = (tBTA_SDP_CB *)osi_malloc(sizeof(tBTA_SDP_CB))) == NULL) { - return; - } - memset((void *)bta_sdp_cb_ptr, 0, sizeof(tBTA_SDP_CB)); -#endif -#if SDP_INCLUDED == TRUE - if ((g_disc_raw_data_buf = (UINT8 *)osi_malloc(MAX_DISC_RAW_DATA_BUF)) == NULL) { - return; - } - memset((void *)g_disc_raw_data_buf, 0, MAX_DISC_RAW_DATA_BUF); -#endif -#if BTA_AR_INCLUDED==TRUE - if ((bta_ar_cb_ptr = (tBTA_AR_CB *)osi_malloc(sizeof(tBTA_AR_CB))) == NULL) { - return; - } - memset((void *)bta_ar_cb_ptr, 0, sizeof(tBTA_AR_CB)); -#endif -#if BTA_AV_INCLUDED==TRUE - if ((bta_av_cb_ptr = (tBTA_AV_CB *)osi_malloc(sizeof(tBTA_AV_CB))) == NULL) { - return; - } - memset((void *)bta_av_cb_ptr, 0, sizeof(tBTA_AV_CB)); - - if ((bta_av_sbc_ups_cb_ptr = (tBTA_AV_SBC_UPS_CB *)osi_malloc(sizeof(tBTA_AV_SBC_UPS_CB))) == NULL) { - return; - } - memset((void *)bta_av_sbc_ups_cb_ptr, 0, sizeof(tBTA_AV_SBC_UPS_CB)); -#endif -#if BTA_HH_INCLUDED==TRUE - if ((bta_hh_cb_ptr = (tBTA_HH_CB *)osi_malloc(sizeof(tBTA_HH_CB))) == NULL) { - return; - } - memset((void *)bta_hh_cb_ptr, 0, sizeof(tBTA_HH_CB)); -#endif -#if BTA_HL_INCLUDED==TRUE - memset((void *)bta_hl_cb_ptr, 0, sizeof(tBTA_HL_CB)); -#endif -#if GATTC_INCLUDED==TRUE - if ((bta_gattc_cb_ptr = (tBTA_GATTC_CB *)osi_malloc(sizeof(tBTA_GATTC_CB))) == NULL) { - return; - } - memset((void *)bta_gattc_cb_ptr, 0, sizeof(tBTA_GATTC_CB)); -#endif -#if GATTS_INCLUDED == TRUE - if ((bta_gatts_cb_ptr = (tBTA_GATTS_CB *)osi_malloc(sizeof(tBTA_GATTS_CB))) == NULL) { - return; - } - memset((void *)bta_gatts_cb_ptr, 0, sizeof(tBTA_GATTS_CB)); -#endif -#if BTA_PAN_INCLUDED==TRUE - memset((void *)bta_pan_cb_ptr, 0, sizeof(tBTA_PAN_CB)); -#endif - -#endif // BTA_INCLUDED == TRUE -} - /***************************************************************************** ** ** Function BTE_DeinitStack @@ -322,53 +160,83 @@ void BTE_DeinitStack(void) //BTA Modules #if (BTA_INCLUDED == TRUE && BTA_DYNAMIC_MEMORY == TRUE) #if GATTS_INCLUDED == TRUE - osi_free(bta_gatts_cb_ptr); - bta_gatts_cb_ptr = NULL; + if (bta_gatts_cb_ptr){ + osi_free(bta_gatts_cb_ptr); + bta_gatts_cb_ptr = NULL; + } #endif #if GATTC_INCLUDED==TRUE - osi_free(bta_gattc_cb_ptr); - bta_gattc_cb_ptr = NULL; + if (bta_gattc_cb_ptr){ + osi_free(bta_gattc_cb_ptr); + bta_gattc_cb_ptr = NULL; + } #endif #if BTA_HH_INCLUDED==TRUE - osi_free(bta_hh_cb_ptr); - bta_hh_cb_ptr = NULL; + if (bta_hh_cb_ptr){ + osi_free(bta_hh_cb_ptr); + bta_hh_cb_ptr = NULL; + } #endif #if BTA_AV_INCLUDED==TRUE - osi_free(bta_av_cb_ptr); - bta_av_cb_ptr = NULL; - osi_free(bta_av_sbc_ups_cb_ptr); - bta_av_sbc_ups_cb_ptr = NULL; + if (bta_av_cb_ptr){ + osi_free(bta_av_cb_ptr); + bta_av_cb_ptr = NULL; + } + if (bta_av_sbc_ups_cb_ptr){ + osi_free(bta_av_sbc_ups_cb_ptr); + bta_av_sbc_ups_cb_ptr = NULL; + } #endif #if BTA_AR_INCLUDED==TRUE - osi_free(bta_ar_cb_ptr); - bta_ar_cb_ptr = NULL; + if (bta_ar_cb_ptr){ + osi_free(bta_ar_cb_ptr); + bta_ar_cb_ptr = NULL; + } #endif #if SDP_INCLUDED == TRUE - osi_free(g_disc_raw_data_buf); - g_disc_raw_data_buf = NULL; + if (g_disc_raw_data_buf){ + osi_free(g_disc_raw_data_buf); + g_disc_raw_data_buf = NULL; + } #endif #if BTA_SDP_INCLUDED == TRUE - osi_free(bta_sdp_cb_ptr); - bta_sdp_cb_ptr = NULL; + if (bta_sdp_cb_ptr){ + osi_free(bta_sdp_cb_ptr); + bta_sdp_cb_ptr = NULL; + } #endif #if (defined BTA_JV_INCLUDED && BTA_JV_INCLUDED == TRUE) - osi_free(bta_jv_cb_ptr); - bta_jv_cb_ptr = NULL; + if (bta_jv_cb_ptr){ + osi_free(bta_jv_cb_ptr); + bta_jv_cb_ptr = NULL; + } #endif //JV #if (defined BTA_HF_INCLUDED && BTA_HF_INCLUDED == TRUE) - osi_free(bta_hf_client_cb_ptr); - bta_hf_client_cb_ptr = NULL; + if (bta_hf_client_cb_ptr){ + osi_free(bta_hf_client_cb_ptr); + bta_hf_client_cb_ptr = NULL; + } #endif - osi_free(bta_dm_conn_srvcs_ptr); - bta_dm_conn_srvcs_ptr = NULL; - osi_free(bta_dm_di_cb_ptr); - bta_dm_di_cb_ptr = NULL; - osi_free(bta_dm_search_cb_ptr); - bta_dm_search_cb_ptr = NULL; - osi_free(bta_dm_cb_ptr); - bta_dm_cb_ptr = NULL; - osi_free(bta_sys_cb_ptr); - bta_sys_cb_ptr = NULL; + if (bta_dm_conn_srvcs_ptr){ + osi_free(bta_dm_conn_srvcs_ptr); + bta_dm_conn_srvcs_ptr = NULL; + } + if (bta_dm_di_cb_ptr){ + osi_free(bta_dm_di_cb_ptr); + bta_dm_di_cb_ptr = NULL; + } + if (bta_dm_search_cb_ptr){ + osi_free(bta_dm_search_cb_ptr); + bta_dm_search_cb_ptr = NULL; + } + if (bta_dm_cb_ptr){ + osi_free(bta_dm_cb_ptr); + bta_dm_cb_ptr = NULL; + } + if (bta_sys_cb_ptr){ + osi_free(bta_sys_cb_ptr); + bta_sys_cb_ptr = NULL; + } #endif // BTA_INCLUDED == TRUE #if (defined(GAP_INCLUDED) && GAP_INCLUDED == TRUE) @@ -376,13 +244,17 @@ void BTE_DeinitStack(void) #endif #if (defined(AVCT_INCLUDED) && AVCT_INCLUDED == TRUE && AVCT_DYNAMIC_MEMORY == TRUE) - osi_free(avct_cb_ptr); - avct_cb_ptr = NULL; + if (avct_cb_ptr){ + osi_free(avct_cb_ptr); + avct_cb_ptr = NULL; + } #endif #if (defined(AVDT_INCLUDED) && AVDT_INCLUDED == TRUE && AVDT_DYNAMIC_MEMORY == TRUE) - osi_free(avdt_cb_ptr); - avdt_cb_ptr = NULL; + if (avdt_cb_ptr){ + osi_free(avdt_cb_ptr); + avdt_cb_ptr = NULL; + } #endif #if (defined(AVRC_INCLUDED) && AVRC_INCLUDED == TRUE) @@ -397,3 +269,179 @@ void BTE_DeinitStack(void) RFCOMM_Deinit(); #endif } + +/***************************************************************************** +** +** Function BTE_InitStack +** +** Description Initialize control block memory for each component. +** +** Note: The core stack components must be called +** before creating the BTU Task. The rest of the +** components can be initialized at a later time if desired +** as long as the component's init function is called +** before accessing any of its functions. +** +** Returns status +** +******************************************************************************/ +bt_status_t BTE_InitStack(void) +{ +#if (defined(RFCOMM_INCLUDED) && RFCOMM_INCLUDED == TRUE) + //Initialize the optional stack components + if (RFCOMM_Init() != BT_STATUS_SUCCESS) { + goto error_exit; + } +#endif + + //BNEP and its profiles +#if (defined(BNEP_INCLUDED) && BNEP_INCLUDED == TRUE) + BNEP_Init(); + +#if (defined(PAN_INCLUDED) && PAN_INCLUDED == TRUE) + PAN_Init(); +#endif // PAN +#endif // BNEP Included + + //AVDT and its profiles +#if (defined(A2D_INCLUDED) && A2D_INCLUDED == TRUE) + if (A2D_Init() != BT_STATUS_SUCCESS) { + goto error_exit; + } +#endif // AADP + +#if (defined(AVRC_INCLUDED) && AVRC_INCLUDED == TRUE) + if (AVRC_Init() != BT_STATUS_SUCCESS) { + goto error_exit; + } +#endif + +#if (defined(AVDT_INCLUDED) && AVDT_INCLUDED == TRUE && AVDT_DYNAMIC_MEMORY == TRUE) + if ((avdt_cb_ptr = (tAVDT_CB *)osi_malloc(sizeof(tAVDT_CB))) == NULL) { + goto error_exit; + } + memset((void *)avdt_cb_ptr, 0, sizeof(tAVDT_CB)); +#endif + +#if (defined(AVCT_INCLUDED) && AVCT_INCLUDED == TRUE && AVCT_DYNAMIC_MEMORY == TRUE) + if ((avct_cb_ptr = (tAVCT_CB *)osi_malloc(sizeof(tAVCT_CB))) == NULL) { + goto error_exit; + } + memset((void *)avct_cb_ptr, 0, sizeof(tAVCT_CB)); +#endif + +#if (defined(GAP_INCLUDED) && GAP_INCLUDED == TRUE) + if (GAP_Init() != BT_STATUS_SUCCESS) { + goto error_exit; + } +#endif + +#if (defined(HID_HOST_INCLUDED) && HID_HOST_INCLUDED == TRUE) + HID_HostInit(); +#endif + +#if (defined(MCA_INCLUDED) && MCA_INCLUDED == TRUE) + MCA_Init(); +#endif + + //BTA Modules +#if (BTA_INCLUDED == TRUE && BTA_DYNAMIC_MEMORY == TRUE) + if ((bta_sys_cb_ptr = (tBTA_SYS_CB *)osi_malloc(sizeof(tBTA_SYS_CB))) == NULL) { + goto error_exit; + } + if ((bta_dm_cb_ptr = (tBTA_DM_CB *)osi_malloc(sizeof(tBTA_DM_CB))) == NULL) { + goto error_exit; + } + if ((bta_dm_search_cb_ptr = (tBTA_DM_SEARCH_CB *)osi_malloc(sizeof(tBTA_DM_SEARCH_CB))) == NULL) { + goto error_exit; + } + if ((bta_dm_di_cb_ptr = (tBTA_DM_DI_CB *)osi_malloc(sizeof(tBTA_DM_DI_CB))) == NULL) { + goto error_exit; + } + if ((bta_dm_conn_srvcs_ptr = (tBTA_DM_CONNECTED_SRVCS *)osi_malloc(sizeof(tBTA_DM_CONNECTED_SRVCS))) == NULL) { + goto error_exit; + } + memset((void *)bta_sys_cb_ptr, 0, sizeof(tBTA_SYS_CB)); + memset((void *)bta_dm_cb_ptr, 0, sizeof(tBTA_DM_CB)); + memset((void *)bta_dm_search_cb_ptr, 0, sizeof(tBTA_DM_SEARCH_CB)); + memset((void *)bta_dm_di_cb_ptr, 0, sizeof(tBTA_DM_DI_CB)); + memset((void *)bta_dm_conn_srvcs_ptr, 0, sizeof(tBTA_DM_CONNECTED_SRVCS)); + //memset((void *)bta_prm_cb_ptr, 0, sizeof(tBTA_PRM_CB)); + +#if (defined BTA_HF_INCLUDED && BTA_HF_INCLUDED == TRUE) + if ((bta_hf_client_cb_ptr = (tBTA_HF_CLIENT_CB *)osi_malloc(sizeof(tBTA_HF_CLIENT_CB))) == NULL) { + goto error_exit; + } + memset((void *)bta_hf_client_cb_ptr, 0, sizeof(tBTA_HF_CLIENT_CB)); +#endif +#if (defined BTA_JV_INCLUDED && BTA_JV_INCLUDED == TRUE) + if ((bta_jv_cb_ptr = (tBTA_JV_CB *)osi_malloc(sizeof(tBTA_JV_CB))) == NULL) { + goto error_exit; + } + memset((void *)bta_jv_cb_ptr, 0, sizeof(tBTA_JV_CB)); +#endif //JV +#if BTA_HS_INCLUDED == TRUE + memset((void *)bta_hs_cb_ptr, 0, sizeof(tBTA_HS_CB)); +#endif +#if BTA_SDP_INCLUDED == TRUE + if ((bta_sdp_cb_ptr = (tBTA_SDP_CB *)osi_malloc(sizeof(tBTA_SDP_CB))) == NULL) { + goto error_exit; + } + memset((void *)bta_sdp_cb_ptr, 0, sizeof(tBTA_SDP_CB)); +#endif +#if SDP_INCLUDED == TRUE + if ((g_disc_raw_data_buf = (UINT8 *)osi_malloc(MAX_DISC_RAW_DATA_BUF)) == NULL) { + goto error_exit; + } + memset((void *)g_disc_raw_data_buf, 0, MAX_DISC_RAW_DATA_BUF); +#endif +#if BTA_AR_INCLUDED==TRUE + if ((bta_ar_cb_ptr = (tBTA_AR_CB *)osi_malloc(sizeof(tBTA_AR_CB))) == NULL) { + goto error_exit; + } + memset((void *)bta_ar_cb_ptr, 0, sizeof(tBTA_AR_CB)); +#endif +#if BTA_AV_INCLUDED==TRUE + if ((bta_av_cb_ptr = (tBTA_AV_CB *)osi_malloc(sizeof(tBTA_AV_CB))) == NULL) { + goto error_exit; + } + memset((void *)bta_av_cb_ptr, 0, sizeof(tBTA_AV_CB)); + + if ((bta_av_sbc_ups_cb_ptr = (tBTA_AV_SBC_UPS_CB *)osi_malloc(sizeof(tBTA_AV_SBC_UPS_CB))) == NULL) { + goto error_exit; + } + memset((void *)bta_av_sbc_ups_cb_ptr, 0, sizeof(tBTA_AV_SBC_UPS_CB)); +#endif +#if BTA_HH_INCLUDED==TRUE + if ((bta_hh_cb_ptr = (tBTA_HH_CB *)osi_malloc(sizeof(tBTA_HH_CB))) == NULL) { + goto error_exit; + } + memset((void *)bta_hh_cb_ptr, 0, sizeof(tBTA_HH_CB)); +#endif +#if BTA_HL_INCLUDED==TRUE + memset((void *)bta_hl_cb_ptr, 0, sizeof(tBTA_HL_CB)); +#endif +#if GATTC_INCLUDED==TRUE + if ((bta_gattc_cb_ptr = (tBTA_GATTC_CB *)osi_malloc(sizeof(tBTA_GATTC_CB))) == NULL) { + goto error_exit; + } + memset((void *)bta_gattc_cb_ptr, 0, sizeof(tBTA_GATTC_CB)); +#endif +#if GATTS_INCLUDED == TRUE + if ((bta_gatts_cb_ptr = (tBTA_GATTS_CB *)osi_malloc(sizeof(tBTA_GATTS_CB))) == NULL) { + goto error_exit; + } + memset((void *)bta_gatts_cb_ptr, 0, sizeof(tBTA_GATTS_CB)); +#endif +#if BTA_PAN_INCLUDED==TRUE + memset((void *)bta_pan_cb_ptr, 0, sizeof(tBTA_PAN_CB)); +#endif + +#endif // BTA_INCLUDED == TRUE + return BT_STATUS_SUCCESS; + +error_exit:; + LOG_ERROR("%s failed due to no memory", __func__); + BTE_DeinitStack(); + return BT_STATUS_NOMEM; +} diff --git a/components/bt/bluedroid/stack/a2dp/a2d_api.c b/components/bt/bluedroid/stack/a2dp/a2d_api.c index 937e46b1e1..8adb200dcd 100644 --- a/components/bt/bluedroid/stack/a2dp/a2d_api.c +++ b/components/bt/bluedroid/stack/a2dp/a2d_api.c @@ -372,13 +372,16 @@ UINT8 A2D_BitsSet(UINT8 num) ** other API functions for this layer. It is typically called ** once during the start up of the stack. ** -** Returns void +** Returns status ** *******************************************************************************/ -void A2D_Init(void) +bt_status_t A2D_Init(void) { #if (A2D_DYNAMIC_MEMORY) a2d_cb_ptr = (tA2D_CB *)osi_malloc(sizeof(tA2D_CB)); + if (!a2d_cb_ptr) { + return BT_STATUS_NOMEM; + } #endif /* #if (A2D_DYNAMIC_MEMORY) */ memset(&a2d_cb, 0, sizeof(tA2D_CB)); @@ -389,6 +392,7 @@ void A2D_Init(void) #else a2d_cb.trace_level = BT_TRACE_LEVEL_NONE; #endif + return BT_STATUS_SUCCESS; } /******************************************************************************* @@ -404,8 +408,10 @@ void A2D_Init(void) void A2D_Deinit(void) { #if (A2D_DYNAMIC_MEMORY) - osi_free(a2d_cb_ptr); - a2d_cb_ptr = NULL; + if (a2d_cb_ptr) { + osi_free(a2d_cb_ptr); + a2d_cb_ptr = NULL; + } #endif /* #if (A2D_DYNAMIC_MEMORY) */ } diff --git a/components/bt/bluedroid/stack/avrc/avrc_sdp.c b/components/bt/bluedroid/stack/avrc/avrc_sdp.c index d3616f3233..11a994d7ac 100644 --- a/components/bt/bluedroid/stack/avrc/avrc_sdp.c +++ b/components/bt/bluedroid/stack/avrc/avrc_sdp.c @@ -340,13 +340,16 @@ UINT8 AVRC_SetTraceLevel (UINT8 new_level) ** control block (if using dynamic memory), and initializes the ** control block and tracing level. ** -** Returns void +** Returns status ** *******************************************************************************/ -void AVRC_Init(void) +bt_status_t AVRC_Init(void) { #if AVRC_DYNAMIC_MEMORY avrc_cb_ptr = (tAVRC_CB *)osi_malloc(sizeof(tAVRC_CB)); + if (!avrc_cb_ptr) { + return BT_STATUS_NOMEM; + } #endif /* #if AVRC_DYNAMIC_MEMORY */ memset(&avrc_cb, 0, sizeof(tAVRC_CB)); @@ -355,6 +358,7 @@ void AVRC_Init(void) #else avrc_cb.trace_level = BT_TRACE_LEVEL_NONE; #endif + return BT_STATUS_SUCCESS; } /******************************************************************************* @@ -371,8 +375,10 @@ void AVRC_Init(void) void AVRC_Deinit(void) { #if AVRC_DYNAMIC_MEMORY - osi_free(avrc_cb_ptr); - avrc_cb_ptr = NULL; + if (avrc_cb_ptr){ + osi_free(avrc_cb_ptr); + avrc_cb_ptr = NULL; + } #endif /* #if AVRC_DYNAMIC_MEMORY */ } diff --git a/components/bt/bluedroid/stack/btm/btm_ble_gap.c b/components/bt/bluedroid/stack/btm/btm_ble_gap.c index e7a4ba955b..5a7f60da94 100644 --- a/components/bt/bluedroid/stack/btm/btm_ble_gap.c +++ b/components/bt/bluedroid/stack/btm/btm_ble_gap.c @@ -246,7 +246,7 @@ uint8_t adv_param_status = 0; uint8_t scan_enable_status = 0; uint8_t scan_param_status = 0; -void btm_lock_init(void) +void btm_ble_lock_init(void) { osi_mutex_new(&adv_enable_lock); osi_mutex_new(&adv_data_lock); @@ -255,7 +255,7 @@ void btm_lock_init(void) osi_mutex_new(&scan_param_lock); } -void btm_lock_free(void) +void btm_ble_lock_free(void) { osi_mutex_free(&adv_enable_lock); osi_mutex_free(&adv_data_lock); @@ -264,7 +264,7 @@ void btm_lock_free(void) osi_mutex_free(&scan_param_lock); } -void btm_sem_init(void) +void btm_ble_sem_init(void) { osi_sem_new(&adv_enable_sem, 1, 0); osi_sem_new(&adv_data_sem, 1, 0); @@ -273,7 +273,7 @@ void btm_sem_init(void) osi_sem_new(&scan_param_sem, 1, 0); } -void btm_sem_free(void) +void btm_ble_sem_free(void) { osi_sem_free(&adv_enable_sem); osi_sem_free(&adv_data_sem); diff --git a/components/bt/bluedroid/stack/btm/btm_devctl.c b/components/bt/bluedroid/stack/btm/btm_devctl.c index 23be3eb303..437a3cd1a3 100644 --- a/components/bt/bluedroid/stack/btm/btm_devctl.c +++ b/components/bt/bluedroid/stack/btm/btm_devctl.c @@ -693,6 +693,7 @@ tBTM_STATUS BTM_VendorSpecificCommand(UINT16 opcode, UINT8 param_len, void btm_vsc_complete (UINT8 *p, UINT16 opcode, UINT16 evt_len, tBTM_CMPL_CB *p_vsc_cplt_cback) { +#if (BLE_INCLUDED == TRUE) tBTM_BLE_CB *ble_cb = &btm_cb.ble_ctr_cb; switch(opcode) { case HCI_VENDOR_BLE_LONG_ADV_DATA: @@ -721,6 +722,7 @@ void btm_vsc_complete (UINT8 *p, UINT16 opcode, UINT16 evt_len, vcs_cplt_params.p_param_buf = p; (*p_vsc_cplt_cback)(&vcs_cplt_params); /* Call the VSC complete callback function */ } +#endif } /******************************************************************************* diff --git a/components/bt/bluedroid/stack/btm/btm_main.c b/components/bt/bluedroid/stack/btm/btm_main.c index b6b853d5c6..cf950f21a9 100644 --- a/components/bt/bluedroid/stack/btm/btm_main.c +++ b/components/bt/bluedroid/stack/btm/btm_main.c @@ -75,8 +75,10 @@ void btm_init (void) #endif btm_dev_init(); /* Device Manager Structures & HCI_Reset */ - btm_lock_init(); - btm_sem_init(); +#if BLE_INCLUDED == TRUE + btm_ble_lock_init(); + btm_ble_sem_init(); +#endif } @@ -96,6 +98,8 @@ void btm_free(void) #if BTM_DYNAMIC_MEMORY FREE_AND_RESET(btm_cb_ptr); #endif - btm_lock_free(); - btm_sem_free(); +#if BLE_INCLUDED == TRUE + btm_ble_lock_free(); + btm_ble_sem_free(); +#endif } diff --git a/components/bt/bluedroid/stack/btm/include/btm_int.h b/components/bt/bluedroid/stack/btm/include/btm_int.h index f575d8958a..4ef7285084 100644 --- a/components/bt/bluedroid/stack/btm/include/btm_int.h +++ b/components/bt/bluedroid/stack/btm/include/btm_int.h @@ -1148,13 +1148,13 @@ void btm_acl_paging (BT_HDR *p, BD_ADDR dest); UINT8 btm_sec_clr_service_by_psm (UINT16 psm); void btm_sec_clr_temp_auth_service (BD_ADDR bda); -void btm_lock_init(void); +void btm_ble_lock_init(void); -void btm_sem_init(void); +void btm_ble_sem_init(void); -void btm_sem_free(void); +void btm_ble_sem_free(void); -void btm_lock_free(void); +void btm_ble_lock_free(void); /* #ifdef __cplusplus diff --git a/components/bt/bluedroid/stack/btu/btu_init.c b/components/bt/bluedroid/stack/btu/btu_init.c index 455cc9fe6a..617bb5de66 100644 --- a/components/bt/bluedroid/stack/btu/btu_init.c +++ b/components/bt/bluedroid/stack/btu/btu_init.c @@ -66,7 +66,7 @@ osi_thread_t *btu_thread = NULL; extern void PLATFORM_DisableHciTransport(UINT8 bDisable); extern void btu_task_thread_handler(void *arg); -void btu_task_start_up(void); +void btu_task_start_up(void * param); void btu_task_shut_down(void); /***************************************************************************** @@ -197,6 +197,15 @@ error_exit:; BTU_ShutDown(); } +/***************************************************************************** +** +** Function BTU_ShutDown +** +** Description Deinitializes the BTU control block. +** +** Returns void +** +******************************************************************************/ void BTU_ShutDown(void) { #if BTU_DYNAMIC_MEMORY diff --git a/components/bt/bluedroid/stack/btu/btu_task.c b/components/bt/bluedroid/stack/btu/btu_task.c index 40448357d5..3e73cbb85c 100644 --- a/components/bt/bluedroid/stack/btu/btu_task.c +++ b/components/bt/bluedroid/stack/btu/btu_task.c @@ -90,7 +90,7 @@ typedef struct { //#include "bt_app_common.h" //#endif -extern void BTE_InitStack(void); +extern bt_status_t BTE_InitStack(void); extern void BTE_DeinitStack(void); /* Define BTU storage area @@ -119,17 +119,19 @@ extern bluedroid_init_done_cb_t bluedroid_init_done_cb; /* Define a function prototype to allow a generic timeout handler */ typedef void (tUSER_TIMEOUT_FUNC) (TIMER_LIST_ENT *p_tle); -static void btu_l2cap_alarm_process(TIMER_LIST_ENT *p_tle); -static void btu_general_alarm_process(TIMER_LIST_ENT *p_tle); -static void btu_hci_msg_process(BT_HDR *p_msg); +static void btu_l2cap_alarm_process(void *param); +static void btu_general_alarm_process(void *param); +static void btu_hci_msg_process(void *param); #if (defined(BTA_INCLUDED) && BTA_INCLUDED == TRUE) -static void btu_bta_alarm_process(TIMER_LIST_ENT *p_tle); +static void btu_bta_alarm_process(void *param); #endif -static void btu_hci_msg_process(BT_HDR *p_msg) +static void btu_hci_msg_process(void *param) { /* Determine the input message type. */ + BT_HDR *p_msg = (BT_HDR *)param; + switch (p_msg->event & BT_EVT_MASK) { case BTU_POST_TO_TASK_NO_GOOD_HORRIBLE_HACK: // TODO(zachoverflow): remove this { @@ -196,8 +198,9 @@ static void btu_hci_msg_process(BT_HDR *p_msg) } #if (defined(BTA_INCLUDED) && BTA_INCLUDED == TRUE) -static void btu_bta_alarm_process(TIMER_LIST_ENT *p_tle) +static void btu_bta_alarm_process(void *param) { + TIMER_LIST_ENT *p_tle = (TIMER_LIST_ENT *)param; // call timer callback if (p_tle->p_cback) { (*p_tle->p_cback)(p_tle); @@ -213,56 +216,42 @@ static void btu_bta_alarm_process(TIMER_LIST_ENT *p_tle) } #endif -void btu_thread_handler(void *arg) +bool btu_task_post(uint32_t sig, void *param, uint32_t timeout) { - btu_thread_evt_t *evt = (btu_thread_evt_t *)arg; + bool status = false; - switch (evt->sig) { + switch (sig) { case SIG_BTU_START_UP: - btu_task_start_up(); + status = osi_thread_post(btu_thread, btu_task_start_up, param, 0, timeout); break; case SIG_BTU_HCI_MSG: - btu_hci_msg_process((BT_HDR *)evt->param); + status = osi_thread_post(btu_thread, btu_hci_msg_process, param, 0, timeout); break; #if (defined(BTA_INCLUDED) && BTA_INCLUDED == TRUE) case SIG_BTU_BTA_MSG: - bta_sys_event((BT_HDR *)evt->param); + status = osi_thread_post(btu_thread, bta_sys_event, param, 0, timeout); break; case SIG_BTU_BTA_ALARM: - btu_bta_alarm_process((TIMER_LIST_ENT *)evt->param); + status = osi_thread_post(btu_thread, btu_bta_alarm_process, param, 0, timeout); break; #endif case SIG_BTU_GENERAL_ALARM: case SIG_BTU_ONESHOT_ALARM: - btu_general_alarm_process((TIMER_LIST_ENT *)evt->param); + status = osi_thread_post(btu_thread, btu_general_alarm_process, param, 0, timeout); break; case SIG_BTU_L2CAP_ALARM: - btu_l2cap_alarm_process((TIMER_LIST_ENT *)evt->param); + status = osi_thread_post(btu_thread, btu_l2cap_alarm_process, param, 0, timeout); break; default: break; } - osi_free(evt); + return status; } -bool btu_task_post(uint32_t sig, void *param, uint32_t timeout) -{ - btu_thread_evt_t *evt; - - evt = (btu_thread_evt_t *)osi_malloc(sizeof(btu_thread_evt_t)); - if (evt == NULL) { - return false; - } - - evt->sig = sig; - evt->param = param; - - return osi_thread_post(btu_thread, btu_thread_handler, evt, 0, timeout); -} - -void btu_task_start_up(void) +void btu_task_start_up(void *param) { + UNUSED(param); /* Initialize the mandatory core stack control blocks (BTU, BTM, L2CAP, and SDP) */ @@ -305,8 +294,9 @@ void btu_task_shut_down(void) ** Returns void ** *******************************************************************************/ -static void btu_general_alarm_process(TIMER_LIST_ENT *p_tle) +static void btu_general_alarm_process(void *param) { + TIMER_LIST_ENT *p_tle = (TIMER_LIST_ENT *)param; assert(p_tle != NULL); switch (p_tle->event) { @@ -511,8 +501,9 @@ void btu_free_timer(TIMER_LIST_ENT *p_tle) ** Returns void ** *******************************************************************************/ -static void btu_l2cap_alarm_process(TIMER_LIST_ENT *p_tle) +static void btu_l2cap_alarm_process(void *param) { + TIMER_LIST_ENT *p_tle = (TIMER_LIST_ENT *)param; assert(p_tle != NULL); switch (p_tle->event) { diff --git a/components/bt/bluedroid/stack/gap/gap_api.c b/components/bt/bluedroid/stack/gap/gap_api.c index 69b5d9629b..b4e7c1b4fb 100644 --- a/components/bt/bluedroid/stack/gap/gap_api.c +++ b/components/bt/bluedroid/stack/gap/gap_api.c @@ -57,13 +57,16 @@ UINT8 GAP_SetTraceLevel (UINT8 new_level) ** This routine should not be called except once per ** stack invocation. ** -** Returns Nothing +** Returns status ** *******************************************************************************/ -void GAP_Init(void) +bt_status_t GAP_Init(void) { #if GAP_DYNAMIC_MEMORY == TRUE gap_cb_ptr = (tGAP_CB *)osi_malloc(sizeof(tGAP_CB)); + if (!gap_cb_ptr) { + return BT_STATUS_NOMEM; + } #endif memset (&gap_cb, 0, sizeof (tGAP_CB)); @@ -81,6 +84,8 @@ void GAP_Init(void) #if BLE_INCLUDED == TRUE && GATTS_INCLUDED == TRUE gap_attr_db_init(); #endif + + return BT_STATUS_SUCCESS; } /******************************************************************************* @@ -96,7 +101,9 @@ void GAP_Init(void) void GAP_Deinit(void) { #if GAP_DYNAMIC_MEMORY == TRUE - osi_free(gap_cb_ptr); - gap_cb_ptr = NULL; + if (gap_cb_ptr) { + osi_free(gap_cb_ptr); + gap_cb_ptr = NULL; + } #endif } \ No newline at end of file diff --git a/components/bt/bluedroid/stack/include/stack/a2d_api.h b/components/bt/bluedroid/stack/include/stack/a2d_api.h index 7509544fb5..466f9fad72 100644 --- a/components/bt/bluedroid/stack/include/stack/a2d_api.h +++ b/components/bt/bluedroid/stack/include/stack/a2d_api.h @@ -23,6 +23,7 @@ ******************************************************************************/ #ifndef A2D_API_H #define A2D_API_H +#include "common/bt_defs.h" #include "stack/sdp_api.h" #if (A2D_INCLUDED == TRUE) /***************************************************************************** @@ -250,7 +251,7 @@ extern UINT8 A2D_BitsSet(UINT8 num); ** Returns void ** *******************************************************************************/ -extern void A2D_Init(void); +extern bt_status_t A2D_Init(void); extern void A2D_Deinit(void); #endif ///A2D_INCLUDED #endif /* A2D_API_H */ diff --git a/components/bt/bluedroid/stack/include/stack/avrc_api.h b/components/bt/bluedroid/stack/include/stack/avrc_api.h index 85e8db1b2e..cbbb4b1c69 100644 --- a/components/bt/bluedroid/stack/include/stack/avrc_api.h +++ b/components/bt/bluedroid/stack/include/stack/avrc_api.h @@ -24,6 +24,7 @@ #ifndef AVRC_API_H #define AVRC_API_H #include "common/bt_target.h" +#include "common/bt_defs.h" #include "stack/avct_api.h" #include "stack/sdp_api.h" #include "stack/avrc_defs.h" @@ -549,10 +550,10 @@ extern UINT8 AVRC_SetTraceLevel (UINT8 new_level); ** control block (if using dynamic memory), and initializes the ** control block and tracing level. ** -** Returns void +** Returns status ** *******************************************************************************/ -extern void AVRC_Init(void); +extern bt_status_t AVRC_Init(void); /******************************************************************************* ** diff --git a/components/bt/bluedroid/stack/include/stack/btu.h b/components/bt/bluedroid/stack/include/stack/btu.h index b3269e2ca6..fd5d1c989d 100644 --- a/components/bt/bluedroid/stack/include/stack/btu.h +++ b/components/bt/bluedroid/stack/include/stack/btu.h @@ -283,7 +283,7 @@ void btu_free_core(void); void BTU_StartUp(void); void BTU_ShutDown(void); -void btu_task_start_up(void); +void btu_task_start_up(void *param); void btu_task_shut_down(void); UINT16 BTU_BleAclPktSize(void); diff --git a/components/bt/bluedroid/stack/include/stack/gap_api.h b/components/bt/bluedroid/stack/include/stack/gap_api.h index 03af2956ce..62062d2f21 100644 --- a/components/bt/bluedroid/stack/include/stack/gap_api.h +++ b/components/bt/bluedroid/stack/include/stack/gap_api.h @@ -320,10 +320,10 @@ extern UINT8 GAP_SetTraceLevel (UINT8 new_level); ** This routine should not be called except once per ** stack invocation. ** -** Returns Nothing +** Returns status ** *******************************************************************************/ -extern void GAP_Init(void); +extern bt_status_t GAP_Init(void); /******************************************************************************* ** diff --git a/components/bt/bluedroid/stack/include/stack/port_api.h b/components/bt/bluedroid/stack/include/stack/port_api.h index 8145a177a0..145ef22889 100644 --- a/components/bt/bluedroid/stack/include/stack/port_api.h +++ b/components/bt/bluedroid/stack/include/stack/port_api.h @@ -25,6 +25,7 @@ #define PORT_API_H #include "common/bt_target.h" +#include "common/bt_defs.h" /***************************************************************************** ** Constants and Types @@ -621,7 +622,7 @@ extern int PORT_Test (UINT16 handle, UINT8 *p_data, UINT16 len); ** Description This function is called to initialize RFCOMM layer ** *******************************************************************************/ -extern void RFCOMM_Init (void); +extern bt_status_t RFCOMM_Init (void); /******************************************************************************* ** diff --git a/components/bt/bluedroid/stack/rfcomm/port_api.c b/components/bt/bluedroid/stack/rfcomm/port_api.c index f6e0ed4d4f..dacf544b9b 100644 --- a/components/bt/bluedroid/stack/rfcomm/port_api.c +++ b/components/bt/bluedroid/stack/rfcomm/port_api.c @@ -1711,11 +1711,16 @@ int PORT_Test (UINT16 handle, UINT8 *p_data, UINT16 len) ** ** Description This function is called to initialize RFCOMM layer ** +** Returns status +** *******************************************************************************/ -void RFCOMM_Init (void) +bt_status_t RFCOMM_Init (void) { #if RFC_DYNAMIC_MEMORY == TRUE rfc_cb_ptr = (tRFC_CB *)osi_malloc(sizeof(tRFC_CB)); + if (rfc_cb_ptr == NULL) { + return BT_STATUS_NOMEM; + } #endif /* #if (RFC_DYNAMIC_MEMORY) */ memset (&rfc_cb, 0, sizeof (tRFC_CB)); /* Init RFCOMM control block */ @@ -1728,6 +1733,7 @@ void RFCOMM_Init (void) #endif rfcomm_l2cap_if_init (); + return BT_STATUS_SUCCESS; } /******************************************************************************* @@ -1743,8 +1749,10 @@ void RFCOMM_Init (void) void RFCOMM_Deinit(void) { #if RFC_DYNAMIC_MEMORY == TRUE - osi_free(rfc_cb_ptr); - rfc_cb_ptr = NULL; + if (rfc_cb_ptr){ + osi_free(rfc_cb_ptr); + rfc_cb_ptr = NULL; + } #endif } diff --git a/components/bt/bluedroid/stack/smp/smp_utils.c b/components/bt/bluedroid/stack/smp/smp_utils.c index 5edf0b8ab2..165b11a2a3 100644 --- a/components/bt/bluedroid/stack/smp/smp_utils.c +++ b/components/bt/bluedroid/stack/smp/smp_utils.c @@ -974,13 +974,14 @@ void smp_proc_pairing_cmpl(tSMP_CB *p_cb) tSMP_EVT_DATA evt_data = {0}; tSMP_CALLBACK *p_callback = p_cb->p_callback; BD_ADDR pairing_bda; - tBTM_SEC_DEV_REC *p_rec = btm_find_dev (p_cb->pairing_bda); SMP_TRACE_DEBUG ("smp_proc_pairing_cmpl \n"); evt_data.cmplt.reason = p_cb->status; evt_data.cmplt.smp_over_br = p_cb->smp_over_br; evt_data.cmplt.auth_mode = 0; +#if (BLE_INCLUDED == TRUE) + tBTM_SEC_DEV_REC *p_rec = btm_find_dev (p_cb->pairing_bda); if (p_cb->status == SMP_SUCCESS) { evt_data.cmplt.sec_level = p_cb->sec_level; if (p_cb->auth_mode) { // the first encryption @@ -992,6 +993,12 @@ void smp_proc_pairing_cmpl(tSMP_CB *p_cb) evt_data.cmplt.auth_mode = p_rec->ble.auth_mode; } } +#else + if (p_cb->status == SMP_SUCCESS) { + evt_data.cmplt.sec_level = p_cb->sec_level; + evt_data.cmplt.auth_mode = p_cb->auth_mode; + } +#endif evt_data.cmplt.is_pair_cancel = FALSE; From 0ed590640eb50c484172d103618bb5533ae4c779 Mon Sep 17 00:00:00 2001 From: Konstantin Kondrashov Date: Mon, 29 Apr 2019 12:25:50 +0800 Subject: [PATCH 108/486] bootloader: Move some structs to separated file Moved structures which describe the app image to separated file. Closes: IDF-597 --- .../include/esp_app_format.h | 109 ++++++++++++++++++ .../include/esp_image_format.h | 84 +------------- 2 files changed, 110 insertions(+), 83 deletions(-) create mode 100644 components/bootloader_support/include/esp_app_format.h diff --git a/components/bootloader_support/include/esp_app_format.h b/components/bootloader_support/include/esp_app_format.h new file mode 100644 index 0000000000..4917dd0f7b --- /dev/null +++ b/components/bootloader_support/include/esp_app_format.h @@ -0,0 +1,109 @@ +// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// +// 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 + +/** + * @brief SPI flash mode, used in esp_image_header_t + */ +typedef enum { + ESP_IMAGE_SPI_MODE_QIO, /*!< SPI mode QIO */ + ESP_IMAGE_SPI_MODE_QOUT, /*!< SPI mode QOUT */ + ESP_IMAGE_SPI_MODE_DIO, /*!< SPI mode DIO */ + ESP_IMAGE_SPI_MODE_DOUT, /*!< SPI mode DOUT */ + ESP_IMAGE_SPI_MODE_FAST_READ, /*!< SPI mode FAST_READ */ + ESP_IMAGE_SPI_MODE_SLOW_READ /*!< SPI mode SLOW_READ */ +} esp_image_spi_mode_t; + +/** + * @brief SPI flash clock frequency + */ +typedef enum { + ESP_IMAGE_SPI_SPEED_40M, /*!< SPI clock frequency 40 MHz */ + ESP_IMAGE_SPI_SPEED_26M, /*!< SPI clock frequency 26 MHz */ + ESP_IMAGE_SPI_SPEED_20M, /*!< SPI clock frequency 20 MHz */ + ESP_IMAGE_SPI_SPEED_80M = 0xF /*!< SPI clock frequency 80 MHz */ +} esp_image_spi_freq_t; + +/** + * @brief Supported SPI flash sizes + */ +typedef enum { + ESP_IMAGE_FLASH_SIZE_1MB = 0, /*!< SPI flash size 1 MB */ + ESP_IMAGE_FLASH_SIZE_2MB, /*!< SPI flash size 2 MB */ + ESP_IMAGE_FLASH_SIZE_4MB, /*!< SPI flash size 4 MB */ + ESP_IMAGE_FLASH_SIZE_8MB, /*!< SPI flash size 8 MB */ + ESP_IMAGE_FLASH_SIZE_16MB, /*!< SPI flash size 16 MB */ + ESP_IMAGE_FLASH_SIZE_MAX /*!< SPI flash size MAX */ +} esp_image_flash_size_t; + +#define ESP_IMAGE_HEADER_MAGIC 0xE9 /*!< The magic word for the esp_image_header_t structure. */ + +/** + * @brief Main header of binary image + */ +typedef struct { + uint8_t magic; /*!< Magic word ESP_IMAGE_HEADER_MAGIC */ + uint8_t segment_count; /*!< Count of memory segments */ + uint8_t spi_mode; /*!< flash read mode (esp_image_spi_mode_t as uint8_t) */ + uint8_t spi_speed: 4; /*!< flash frequency (esp_image_spi_freq_t as uint8_t) */ + uint8_t spi_size: 4; /*!< flash chip size (esp_image_flash_size_t as uint8_t) */ + uint32_t entry_addr; /*!< Entry address */ + uint8_t wp_pin; /*!< WP pin when SPI pins set via efuse (read by ROM bootloader, + * the IDF bootloader uses software to configure the WP + * pin and sets this field to 0xEE=disabled) */ + uint8_t spi_pin_drv[3]; /*!< Drive settings for the SPI flash pins (read by ROM bootloader) */ + uint8_t reserved[11]; /*!< Reserved bytes in ESP32 additional header space, currently unused */ + uint8_t hash_appended; /*!< If 1, a SHA256 digest "simple hash" (of the entire image) is appended after the checksum. + * Included in image length. This digest + * is separate to secure boot and only used for detecting corruption. + * For secure boot signed images, the signature + * is appended after this (and the simple hash is included in the signed data). */ +} __attribute__((packed)) esp_image_header_t; + +/** @cond */ +_Static_assert(sizeof(esp_image_header_t) == 24, "binary image header should be 24 bytes"); +/** @endcond */ + + +/** + * @brief Header of binary image segment + */ +typedef struct { + uint32_t load_addr; /*!< Address of segment */ + uint32_t data_len; /*!< Length of data */ +} esp_image_segment_header_t; + +#define ESP_IMAGE_MAX_SEGMENTS 16 /*!< Max count of segments in the image. */ + +#define ESP_APP_DESC_MAGIC_WORD 0xABCD5432 /*!< The magic word for the esp_app_desc structure that is in DROM. */ + +/** + * @brief Description about application. + */ +typedef struct { + uint32_t magic_word; /*!< Magic word ESP_APP_DESC_MAGIC_WORD */ + uint32_t secure_version; /*!< Secure version */ + uint32_t reserv1[2]; /*!< reserv1 */ + char version[32]; /*!< Application version */ + char project_name[32]; /*!< Project name */ + char time[16]; /*!< Compile time */ + char date[16]; /*!< Compile date*/ + char idf_ver[32]; /*!< Version IDF */ + uint8_t app_elf_sha256[32]; /*!< sha256 of elf file */ + uint32_t reserv2[20]; /*!< reserv2 */ +} esp_app_desc_t; + +/** @cond */ +_Static_assert(sizeof(esp_app_desc_t) == 256, "esp_app_desc_t should be 256 bytes"); +/** @endcond */ diff --git a/components/bootloader_support/include/esp_image_format.h b/components/bootloader_support/include/esp_image_format.h index 7006cae98d..b66b66f5fd 100644 --- a/components/bootloader_support/include/esp_image_format.h +++ b/components/bootloader_support/include/esp_image_format.h @@ -16,6 +16,7 @@ #include #include #include "esp_flash_partitions.h" +#include "esp_app_format.h" #define ESP_ERR_IMAGE_BASE 0x2000 #define ESP_ERR_IMAGE_FLASH_FAIL (ESP_ERR_IMAGE_BASE + 1) @@ -25,91 +26,8 @@ Can be compiled as part of app or bootloader code. */ -/* SPI flash mode, used in esp_image_header_t */ -typedef enum { - ESP_IMAGE_SPI_MODE_QIO, - ESP_IMAGE_SPI_MODE_QOUT, - ESP_IMAGE_SPI_MODE_DIO, - ESP_IMAGE_SPI_MODE_DOUT, - ESP_IMAGE_SPI_MODE_FAST_READ, - ESP_IMAGE_SPI_MODE_SLOW_READ -} esp_image_spi_mode_t; - -/* SPI flash clock frequency */ -typedef enum { - ESP_IMAGE_SPI_SPEED_40M, - ESP_IMAGE_SPI_SPEED_26M, - ESP_IMAGE_SPI_SPEED_20M, - ESP_IMAGE_SPI_SPEED_80M = 0xF -} esp_image_spi_freq_t; - -/* Supported SPI flash sizes */ -typedef enum { - ESP_IMAGE_FLASH_SIZE_1MB = 0, - ESP_IMAGE_FLASH_SIZE_2MB, - ESP_IMAGE_FLASH_SIZE_4MB, - ESP_IMAGE_FLASH_SIZE_8MB, - ESP_IMAGE_FLASH_SIZE_16MB, - ESP_IMAGE_FLASH_SIZE_MAX -} esp_image_flash_size_t; - -#define ESP_IMAGE_HEADER_MAGIC 0xE9 - -/* Main header of binary image */ -typedef struct { - uint8_t magic; - uint8_t segment_count; - /* flash read mode (esp_image_spi_mode_t as uint8_t) */ - uint8_t spi_mode; - /* flash frequency (esp_image_spi_freq_t as uint8_t) */ - uint8_t spi_speed: 4; - /* flash chip size (esp_image_flash_size_t as uint8_t) */ - uint8_t spi_size: 4; - uint32_t entry_addr; - /* WP pin when SPI pins set via efuse (read by ROM bootloader, the IDF bootloader uses software to configure the WP - * pin and sets this field to 0xEE=disabled) */ - uint8_t wp_pin; - /* Drive settings for the SPI flash pins (read by ROM bootloader) */ - uint8_t spi_pin_drv[3]; - /* Reserved bytes in ESP32 additional header space, currently unused */ - uint8_t reserved[11]; - /* If 1, a SHA256 digest "simple hash" (of the entire image) is appended after the checksum. Included in image length. This digest - * is separate to secure boot and only used for detecting corruption. For secure boot signed images, the signature - * is appended after this (and the simple hash is included in the signed data). */ - uint8_t hash_appended; -} __attribute__((packed)) esp_image_header_t; - -_Static_assert(sizeof(esp_image_header_t) == 24, "binary image header should be 24 bytes"); - #define ESP_IMAGE_HASH_LEN 32 /* Length of the appended SHA-256 digest */ -/* Header of binary image segment */ -typedef struct { - uint32_t load_addr; - uint32_t data_len; -} esp_image_segment_header_t; - -#define ESP_APP_DESC_MAGIC_WORD 0xABCD5432 /*!< The magic word for the esp_app_desc structure that is in DROM. */ - -/** - * @brief Description about application. - */ -typedef struct { - uint32_t magic_word; /*!< Magic word ESP_APP_DESC_MAGIC_WORD */ - uint32_t secure_version; /*!< Secure version */ - uint32_t reserv1[2]; /*!< --- */ - char version[32]; /*!< Application version */ - char project_name[32]; /*!< Project name */ - char time[16]; /*!< Compile time */ - char date[16]; /*!< Compile date*/ - char idf_ver[32]; /*!< Version IDF */ - uint8_t app_elf_sha256[32]; /*!< sha256 of elf file */ - uint32_t reserv2[20]; /*!< --- */ -} esp_app_desc_t; -_Static_assert(sizeof(esp_app_desc_t) == 256, "esp_app_desc_t should be 256 bytes"); - -#define ESP_IMAGE_MAX_SEGMENTS 16 - /* Structure to hold on-flash image metadata */ typedef struct { uint32_t start_addr; /* Start address of image */ From e8582e9aa441c3b2d988c73b582b2985f41042ac Mon Sep 17 00:00:00 2001 From: Renz Christian Bagaporo Date: Fri, 21 Jun 2019 19:48:41 +0800 Subject: [PATCH 109/486] esptool_py: use passed offset and image when template is given esptool_py defines command `esptool_py_flash_project_args` that generates arg file for esptool.py. Two of the arguments are the offset and image, which are not being used when a template file is given. This commit makes variables OFFSET and IMAGE available to the template file, which will holds the value of the offset and image arguments to `esptool_py_flash_project_args`. --- components/bootloader/CMakeLists.txt | 3 +-- components/bootloader/flash_bootloader_args.in | 2 +- components/esptool_py/project_include.cmake | 15 +++++++++++++-- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/components/bootloader/CMakeLists.txt b/components/bootloader/CMakeLists.txt index a9dbf01afe..5e243520e9 100644 --- a/components/bootloader/CMakeLists.txt +++ b/components/bootloader/CMakeLists.txt @@ -7,8 +7,7 @@ endif() # Set values used in flash_bootloader_args.in and generate flash file # for bootloader -set(BOOTLOADER_OFFSET 0x1000) -esptool_py_flash_project_args(bootloader ${BOOTLOADER_OFFSET} +esptool_py_flash_project_args(bootloader 0x1000 ${BOOTLOADER_BUILD_DIR}/bootloader.bin FLASH_IN_PROJECT FLASH_FILE_TEMPLATE flash_bootloader_args.in) \ No newline at end of file diff --git a/components/bootloader/flash_bootloader_args.in b/components/bootloader/flash_bootloader_args.in index 610ba7626b..2867c5aafd 100644 --- a/components/bootloader/flash_bootloader_args.in +++ b/components/bootloader/flash_bootloader_args.in @@ -1,4 +1,4 @@ --flash_mode ${ESPFLASHMODE} --flash_size ${ESPFLASHSIZE} --flash_freq ${ESPFLASHFREQ} -${BOOTLOADER_OFFSET} bootloader/bootloader.bin +${OFFSET} ${IMAGE} diff --git a/components/esptool_py/project_include.cmake b/components/esptool_py/project_include.cmake index b847680c88..544888f054 100644 --- a/components/esptool_py/project_include.cmake +++ b/components/esptool_py/project_include.cmake @@ -148,7 +148,10 @@ add_custom_target(flash_project_args_target) # esptool_py_flash_project_args # -# Add file to the flasher args list, to be flashed at a particular offset +# Add file to the flasher args list, to be flashed at a particular offset. +# +# When a template FLASH_FILE_TEMPLATE is given, the variables OFFSET and IMAGE +# hold the value of arguments offset and image, respectively. function(esptool_py_flash_project_args entry offset image) set(options FLASH_IN_PROJECT) # flash the image when flashing the project set(single_value FLASH_FILE_TEMPLATE) # template file to use to be able to @@ -172,8 +175,16 @@ function(esptool_py_flash_project_args entry offset image) if(NOT __FLASH_FILE_TEMPLATE) file(GENERATE OUTPUT ${entry_flash_args} CONTENT "${offset} ${image}") else() + set(OFFSET ${offset}) + set(IMAGE ${image}) get_filename_component(template "${__FLASH_FILE_TEMPLATE}" ABSOLUTE) - file(GENERATE OUTPUT ${entry_flash_args} INPUT ${template}) + configure_file(${template} ${CMAKE_CURRENT_BINARY_DIR}/${template}.in2) + file(GENERATE OUTPUT ${entry_flash_args} INPUT ${CMAKE_CURRENT_BINARY_DIR}/${template}.in2) + set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + APPEND PROPERTY + ADDITIONAL_MAKE_CLEAN_FILES ${CMAKE_CURRENT_BINARY_DIR}/${template}.in2}) + unset(OFFSET) + unset(IMAGE) endif() set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} From 9eccd7c0826d6cc2e9de59304d1e5f76c0063ccf Mon Sep 17 00:00:00 2001 From: Renz Christian Bagaporo Date: Sun, 28 Apr 2019 15:38:23 +0800 Subject: [PATCH 110/486] components: use new component registration api --- components/app_trace/CMakeLists.txt | 19 +- components/app_trace/test/CMakeLists.txt | 9 +- components/app_update/CMakeLists.txt | 10 +- components/app_update/test/CMakeLists.txt | 9 +- components/asio/CMakeLists.txt | 9 +- components/bootloader/CMakeLists.txt | 2 +- .../components/micro-ecc/CMakeLists.txt | 5 +- .../bootloader/subproject/main/CMakeLists.txt | 6 +- components/bootloader_support/CMakeLists.txt | 39 ++-- .../bootloader_support/test/CMakeLists.txt | 9 +- components/bt/CMakeLists.txt | 17 +- components/bt/test/CMakeLists.txt | 10 +- components/coap/CMakeLists.txt | 8 +- components/console/CMakeLists.txt | 13 +- components/cxx/CMakeLists.txt | 5 +- components/cxx/test/CMakeLists.txt | 9 +- components/driver/CMakeLists.txt | 55 +++--- components/driver/test/CMakeLists.txt | 9 +- components/efuse/CMakeLists.txt | 15 +- components/efuse/test/CMakeLists.txt | 9 +- components/esp-tls/CMakeLists.txt | 11 +- components/esp32/CMakeLists.txt | 71 ++++--- components/esp32/test/CMakeLists.txt | 9 +- components/esp_adc_cal/CMakeLists.txt | 9 +- components/esp_common/CMakeLists.txt | 30 ++- components/esp_event/CMakeLists.txt | 26 +-- components/esp_event/test/CMakeLists.txt | 8 +- components/esp_http_client/CMakeLists.txt | 19 +- .../esp_http_client/test/CMakeLists.txt | 9 +- components/esp_http_server/CMakeLists.txt | 23 +-- .../esp_http_server/test/CMakeLists.txt | 9 +- components/esp_https_ota/CMakeLists.txt | 11 +- components/esp_https_server/CMakeLists.txt | 11 +- components/esp_ringbuf/CMakeLists.txt | 10 +- components/esp_ringbuf/test/CMakeLists.txt | 9 +- components/esp_rom/CMakeLists.txt | 11 +- components/esp_wifi/CMakeLists.txt | 13 +- components/esp_wifi/test/CMakeLists.txt | 9 +- components/espcoredump/CMakeLists.txt | 18 +- components/espcoredump/test/CMakeLists.txt | 7 +- components/esptool_py/CMakeLists.txt | 3 +- components/ethernet/CMakeLists.txt | 20 +- components/ethernet/test/CMakeLists.txt | 8 +- components/expat/CMakeLists.txt | 15 +- components/expat/test/CMakeLists.txt | 8 +- components/fatfs/CMakeLists.txt | 29 ++- components/fatfs/test/CMakeLists.txt | 12 +- components/freemodbus/CMakeLists.txt | 74 +++---- components/freertos/CMakeLists.txt | 46 +++-- components/freertos/test/CMakeLists.txt | 9 +- components/heap/CMakeLists.txt | 20 +- components/heap/test/CMakeLists.txt | 9 +- components/idf_test/CMakeLists.txt | 3 +- components/jsmn/CMakeLists.txt | 5 +- components/json/CMakeLists.txt | 9 +- components/libsodium/CMakeLists.txt | 17 +- components/libsodium/test/CMakeLists.txt | 12 +- components/log/CMakeLists.txt | 7 +- components/lwip/CMakeLists.txt | 181 +++++++++--------- components/mbedtls/CMakeLists.txt | 8 +- components/mbedtls/test/CMakeLists.txt | 9 +- components/mdns/CMakeLists.txt | 14 +- components/mdns/test/CMakeLists.txt | 5 +- components/mqtt/CMakeLists.txt | 17 +- components/newlib/CMakeLists.txt | 43 +++-- components/newlib/test/CMakeLists.txt | 9 +- components/nghttp/CMakeLists.txt | 50 ++--- components/nvs_flash/CMakeLists.txt | 23 +-- components/nvs_flash/test/CMakeLists.txt | 9 +- components/openssl/CMakeLists.txt | 25 ++- components/partition_table/CMakeLists.txt | 2 +- .../partition_table/test/CMakeLists.txt | 9 +- components/protobuf-c/CMakeLists.txt | 6 +- components/protocomm/CMakeLists.txt | 35 ++-- components/protocomm/test/CMakeLists.txt | 11 +- components/pthread/CMakeLists.txt | 9 +- components/pthread/test/CMakeLists.txt | 9 +- components/sdmmc/CMakeLists.txt | 19 +- components/sdmmc/test/CMakeLists.txt | 9 +- components/smartconfig_ack/CMakeLists.txt | 9 +- components/soc/CMakeLists.txt | 16 +- components/soc/test/CMakeLists.txt | 11 +- components/spi_flash/CMakeLists.txt | 20 +- components/spi_flash/test/CMakeLists.txt | 9 +- components/spiffs/CMakeLists.txt | 25 ++- components/spiffs/test/CMakeLists.txt | 9 +- components/tcp_transport/CMakeLists.txt | 20 +- components/tcpip_adapter/CMakeLists.txt | 10 +- components/ulp/CMakeLists.txt | 7 +- components/ulp/test/CMakeLists.txt | 9 +- components/unity/CMakeLists.txt | 14 +- components/vfs/CMakeLists.txt | 9 +- components/vfs/test/CMakeLists.txt | 12 +- components/wear_levelling/CMakeLists.txt | 21 +- components/wear_levelling/test/CMakeLists.txt | 12 +- components/wifi_provisioning/CMakeLists.txt | 27 ++- components/wpa_supplicant/CMakeLists.txt | 156 ++++++++------- components/wpa_supplicant/test/CMakeLists.txt | 9 +- components/xtensa/CMakeLists.txt | 15 +- 99 files changed, 810 insertions(+), 1008 deletions(-) diff --git a/components/app_trace/CMakeLists.txt b/components/app_trace/CMakeLists.txt index dc6e8baa7a..5276fa01ed 100644 --- a/components/app_trace/CMakeLists.txt +++ b/components/app_trace/CMakeLists.txt @@ -1,16 +1,16 @@ -set(COMPONENT_SRCS "app_trace.c" +set(srcs "app_trace.c" "app_trace_util.c" "host_file_io.c" "gcov/gcov_rtio.c") -set(COMPONENT_ADD_INCLUDEDIRS "include") +set(include_dirs "include") if(CONFIG_SYSVIEW_ENABLE) - list(APPEND COMPONENT_ADD_INCLUDEDIRS + list(APPEND include_dirs sys_view/Config sys_view/SEGGER sys_view/Sample/OS) - list(APPEND COMPONENT_SRCS "sys_view/SEGGER/SEGGER_SYSVIEW.c" + list(APPEND srcs "sys_view/SEGGER/SEGGER_SYSVIEW.c" "sys_view/Sample/Config/SEGGER_SYSVIEW_Config_FreeRTOS.c" "sys_view/Sample/OS/SEGGER_SYSVIEW_FreeRTOS.c" "sys_view/esp32/SEGGER_RTT_esp32.c" @@ -19,14 +19,13 @@ if(CONFIG_SYSVIEW_ENABLE) endif() if(CONFIG_HEAP_TRACING_TOHOST) - list(APPEND COMPONENT_SRCS "heap_trace_tohost.c") + list(APPEND srcs "heap_trace_tohost.c") endif() -set(COMPONENT_REQUIRES) -set(COMPONENT_PRIV_REQUIRES heap soc) -set(COMPONENT_ADD_LDFRAGMENTS linker.lf) - -register_component() +idf_component_register(SRCS "${srcs}" + INCLUDE_DIRS "${include_dirs}" + PRIV_REQUIRES soc + LDFRAGMENTS linker.lf) # disable --coverage for this component, as it is used as transport # for gcov diff --git a/components/app_trace/test/CMakeLists.txt b/components/app_trace/test/CMakeLists.txt index 884ca8b6da..16aca87790 100644 --- a/components/app_trace/test/CMakeLists.txt +++ b/components/app_trace/test/CMakeLists.txt @@ -1,6 +1,3 @@ -set(COMPONENT_SRCDIRS ".") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -set(COMPONENT_REQUIRES unity) - -register_component() \ No newline at end of file +idf_component_register(SRC_DIRS "." + INCLUDE_DIRS "." + REQUIRES unity) \ No newline at end of file diff --git a/components/app_update/CMakeLists.txt b/components/app_update/CMakeLists.txt index 68ab01c4f9..acb02accb2 100644 --- a/components/app_update/CMakeLists.txt +++ b/components/app_update/CMakeLists.txt @@ -1,10 +1,6 @@ -set(COMPONENT_SRCS "esp_ota_ops.c" - "esp_app_desc.c") -set(COMPONENT_ADD_INCLUDEDIRS "include") - -set(COMPONENT_REQUIRES spi_flash partition_table bootloader_support) - -register_component() +idf_component_register(SRCS "esp_ota_ops.c" "esp_app_desc.c" + INCLUDE_DIRS "include" + REQUIRES spi_flash partition_table bootloader_support) # esp_app_desc structure is added as an undefined symbol because otherwise the # linker will ignore this structure as it has no other files depending on it. diff --git a/components/app_update/test/CMakeLists.txt b/components/app_update/test/CMakeLists.txt index e42488e75e..e56fd72838 100644 --- a/components/app_update/test/CMakeLists.txt +++ b/components/app_update/test/CMakeLists.txt @@ -1,6 +1,3 @@ -set(COMPONENT_SRCDIRS ".") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -set(COMPONENT_REQUIRES unity test_utils app_update bootloader_support nvs_flash) - -register_component() \ No newline at end of file +idf_component_register(SRC_DIRS "." + INCLUDE_DIRS "." + REQUIRES unity test_utils app_update bootloader_support nvs_flash) \ No newline at end of file diff --git a/components/asio/CMakeLists.txt b/components/asio/CMakeLists.txt index 43d428f8d7..f2038278de 100644 --- a/components/asio/CMakeLists.txt +++ b/components/asio/CMakeLists.txt @@ -1,6 +1,3 @@ -set(COMPONENT_ADD_INCLUDEDIRS asio/asio/include port/include) -set(COMPONENT_SRCS "asio/asio/src/asio.cpp") - -set(COMPONENT_REQUIRES lwip) - -register_component() +idf_component_register(SRCS "asio/asio/src/asio.cpp" + INCLUDE_DIRS "asio/asio/include" "port/include" + REQUIRES lwip) diff --git a/components/bootloader/CMakeLists.txt b/components/bootloader/CMakeLists.txt index 5e243520e9..6d50ca58f5 100644 --- a/components/bootloader/CMakeLists.txt +++ b/components/bootloader/CMakeLists.txt @@ -1,4 +1,4 @@ -register_component() +idf_component_register() # Do not generate flash file when building bootloader or is in early expansion of the build if(BOOTLOADER_BUILD) diff --git a/components/bootloader/subproject/components/micro-ecc/CMakeLists.txt b/components/bootloader/subproject/components/micro-ecc/CMakeLists.txt index d609b61e70..7d4bfc4d19 100644 --- a/components/bootloader/subproject/components/micro-ecc/CMakeLists.txt +++ b/components/bootloader/subproject/components/micro-ecc/CMakeLists.txt @@ -1,4 +1,3 @@ # only compile the "micro-ecc/uECC.c" source file -set(COMPONENT_SRCS "micro-ecc/uECC.c") -set(COMPONENT_ADD_INCLUDEDIRS micro-ecc) -register_component() +idf_component_register(SRCS "micro-ecc/uECC.c" + INCLUDE_DIRS micro-ecc) diff --git a/components/bootloader/subproject/main/CMakeLists.txt b/components/bootloader/subproject/main/CMakeLists.txt index ab3ed94fd0..d73d46debc 100644 --- a/components/bootloader/subproject/main/CMakeLists.txt +++ b/components/bootloader/subproject/main/CMakeLists.txt @@ -1,7 +1,5 @@ -set(COMPONENT_SRCS "bootloader_start.c") -set(COMPONENT_ADD_INCLUDEDIRS "") -set(COMPONENT_REQUIRES bootloader bootloader_support) -register_component() +idf_component_register(SRCS "bootloader_start.c" + REQUIRES bootloader bootloader_support) idf_build_get_property(target IDF_TARGET) set(scripts "${target}.bootloader.ld" diff --git a/components/bootloader_support/CMakeLists.txt b/components/bootloader_support/CMakeLists.txt index 2437829d54..a8db5c4873 100644 --- a/components/bootloader_support/CMakeLists.txt +++ b/components/bootloader_support/CMakeLists.txt @@ -1,4 +1,4 @@ -set(COMPONENT_SRCS "src/bootloader_clock.c" +set(srcs "src/bootloader_clock.c" "src/bootloader_common.c" "src/bootloader_flash.c" "src/bootloader_random.c" @@ -8,14 +8,14 @@ set(COMPONENT_SRCS "src/bootloader_clock.c" "src/flash_qio_mode.c") if(BOOTLOADER_BUILD) - set(COMPONENT_ADD_INCLUDEDIRS "include include_bootloader") - set(COMPONENT_REQUIRES soc) #unfortunately the header directly uses SOC registers - set(COMPONENT_PRIV_REQUIRES micro-ecc spi_flash efuse) - list(APPEND COMPONENT_SRCS "src/bootloader_init.c" - "src/${IDF_TARGET}/bootloader_sha.c" - "src/${IDF_TARGET}/flash_encrypt.c" - "src/${IDF_TARGET}/secure_boot_signatures.c" - "src/${IDF_TARGET}/secure_boot.c") + set(include_dirs "include" "include_bootloader") + set(requires soc) #unfortunately the header directly uses SOC registers + set(priv_requires micro-ecc spi_flash efuse) + list(APPEND srcs "src/bootloader_init.c" + "src/${IDF_TARGET}/bootloader_sha.c" + "src/${IDF_TARGET}/flash_encrypt.c" + "src/${IDF_TARGET}/secure_boot_signatures.c" + "src/${IDF_TARGET}/secure_boot.c") if(CONFIG_SECURE_SIGNED_APPS) get_filename_component(secure_boot_verification_key @@ -45,18 +45,23 @@ if(BOOTLOADER_BUILD) DEPENDS "${orig_secure_boot_verification_key}" VERBATIM) endif() - set(COMPONENT_EMBED_FILES "${secure_boot_verification_key}") + set(embed_files "${secure_boot_verification_key}") set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES "${secure_boot_verification_key}") endif() else() - list(APPEND COMPONENT_SRCS "src/idf/bootloader_sha.c" - "src/idf/secure_boot_signatures.c") - set(COMPONENT_ADD_INCLUDEDIRS "include") - set(COMPONENT_PRIV_INCLUDEDIRS "include_bootloader") - set(COMPONENT_REQUIRES soc) #unfortunately the header directly uses SOC registers - set(COMPONENT_PRIV_REQUIRES spi_flash mbedtls efuse) + list(APPEND srcs "src/idf/bootloader_sha.c" + "src/idf/secure_boot_signatures.c") + set(include_dirs "include") + set(priv_include_dirs "include_bootloader") + set(requires soc) #unfortunately the header directly uses SOC registers + set(priv_requires spi_flash mbedtls efuse) endif() -register_component() \ No newline at end of file +idf_component_register(SRCS "${srcs}" + INCLUDE_DIRS "${include_dirs}" + PRIV_INCLUDE_DIRS "${priv_include_dirs}" + REQUIRES "${requires}" + PRIV_REQUIRES "${priv_requires}" + EMBED_FILES "${embed_files}") \ No newline at end of file diff --git a/components/bootloader_support/test/CMakeLists.txt b/components/bootloader_support/test/CMakeLists.txt index 587c81609c..a31c179345 100644 --- a/components/bootloader_support/test/CMakeLists.txt +++ b/components/bootloader_support/test/CMakeLists.txt @@ -1,6 +1,3 @@ -set(COMPONENT_SRCDIRS ".") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -set(COMPONENT_REQUIRES unity bootloader_support app_update) - -register_component() \ No newline at end of file +idf_component_register(SRC_DIRS "." + INCLUDE_DIRS "." + REQUIRES unity bootloader_support app_update) diff --git a/components/bt/CMakeLists.txt b/components/bt/CMakeLists.txt index e1d1fef01c..2057ea0c75 100644 --- a/components/bt/CMakeLists.txt +++ b/components/bt/CMakeLists.txt @@ -1,11 +1,11 @@ if(CONFIG_BT_ENABLED) - set(COMPONENT_SRCS "bt.c") - set(COMPONENT_ADD_INCLUDEDIRS include) + set(srcs "bt.c") + set(include_dirs include) if(CONFIG_BT_BLUEDROID_ENABLED) - list(APPEND COMPONENT_PRIV_INCLUDEDIRS + list(APPEND priv_include_dirs bluedroid/bta/include bluedroid/bta/ar/include bluedroid/bta/av/include @@ -41,9 +41,9 @@ if(CONFIG_BT_ENABLED) bluedroid/stack/include bluedroid/common/include) - list(APPEND COMPONENT_ADD_INCLUDEDIRS bluedroid/api/include/api) + list(APPEND include_dirs bluedroid/api/include/api) - list(APPEND COMPONENT_SRCS "bluedroid/api/esp_a2dp_api.c" + list(APPEND srcs "bluedroid/api/esp_a2dp_api.c" "bluedroid/api/esp_avrc_api.c" "bluedroid/api/esp_blufi_api.c" "bluedroid/api/esp_bt_device.c" @@ -287,9 +287,10 @@ if(CONFIG_BT_ENABLED) endif() # requirements can't depend on config -set(COMPONENT_PRIV_REQUIRES nvs_flash soc) - -register_component() +idf_component_register(SRCS "${srcs}" + INCLUDE_DIRS "${include_dirs}" + PRIV_INCLUDE_DIRS "${priv_include_dirs}" + REQUIRES nvs_flash soc) if(CONFIG_BT_ENABLED) if(GCC_NOT_5_2_0) diff --git a/components/bt/test/CMakeLists.txt b/components/bt/test/CMakeLists.txt index e59c45744a..0012e8bd17 100644 --- a/components/bt/test/CMakeLists.txt +++ b/components/bt/test/CMakeLists.txt @@ -1,7 +1,5 @@ if(CONFIG_BT_ENABLED OR CMAKE_BUILD_EARLY_EXPANSION) - set(COMPONENT_SRCDIRS ".") - set(COMPONENT_ADD_INCLUDEDIRS ".") - set(COMPONENT_REQUIRES unity nvs_flash bt) - - register_component() -endif() + idf_component_register(SRC_DIRS "." + INCLUDE_DIRS "." + REQUIRES unity nvs_flash bt) +endif() \ No newline at end of file diff --git a/components/coap/CMakeLists.txt b/components/coap/CMakeLists.txt index 246a0e28e4..78015bb056 100644 --- a/components/coap/CMakeLists.txt +++ b/components/coap/CMakeLists.txt @@ -1,6 +1,6 @@ -set(COMPONENT_ADD_INCLUDEDIRS port/include port/include/coap libcoap/include libcoap/include/coap2) +set(include_dirs port/include port/include/coap libcoap/include libcoap/include/coap2) -set(COMPONENT_SRCS "libcoap/src/address.c" +set(srcs "libcoap/src/address.c" "libcoap/src/async.c" "libcoap/src/block.c" "libcoap/src/coap_event.c" @@ -22,7 +22,9 @@ set(COMPONENT_SRCS "libcoap/src/address.c" set(COMPONENT_REQUIRES lwip) -register_component() +idf_component_register(SRCS "${srcs}" + INCLUDE_DIRS "${include_dirs}" + REQUIRES lwip) # Silence format truncation warning, until it is fixed upstream set_source_files_properties(libcoap/src/coap_debug.c PROPERTIES COMPILE_FLAGS -Wno-format-truncation) diff --git a/components/console/CMakeLists.txt b/components/console/CMakeLists.txt index 33dddc81a9..244834c224 100644 --- a/components/console/CMakeLists.txt +++ b/components/console/CMakeLists.txt @@ -1,7 +1,6 @@ -set(COMPONENT_ADD_INCLUDEDIRS .) -set(COMPONENT_SRCS "commands.c" - "split_argv.c" - "argtable3/argtable3.c" - "linenoise/linenoise.c") -register_component() - +idf_component_register(SRCS "commands.c" + "split_argv.c" + "argtable3/argtable3.c" + "linenoise/linenoise.c" + INCLUDE_DIRS "." + REQUIRES vfs) diff --git a/components/cxx/CMakeLists.txt b/components/cxx/CMakeLists.txt index dbfe20dd32..4e81992ad0 100644 --- a/components/cxx/CMakeLists.txt +++ b/components/cxx/CMakeLists.txt @@ -1,6 +1,5 @@ -set(COMPONENT_SRCS "cxx_exception_stubs.cpp" - "cxx_guards.cpp") -register_component() +idf_component_register(SRCS "cxx_exception_stubs.cpp" + "cxx_guards.cpp") target_link_libraries(${COMPONENT_LIB} PUBLIC stdc++ gcc) target_link_libraries(${COMPONENT_LIB} INTERFACE "-u __cxa_guard_dummy") diff --git a/components/cxx/test/CMakeLists.txt b/components/cxx/test/CMakeLists.txt index 884ca8b6da..16aca87790 100644 --- a/components/cxx/test/CMakeLists.txt +++ b/components/cxx/test/CMakeLists.txt @@ -1,6 +1,3 @@ -set(COMPONENT_SRCDIRS ".") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -set(COMPONENT_REQUIRES unity) - -register_component() \ No newline at end of file +idf_component_register(SRC_DIRS "." + INCLUDE_DIRS "." + REQUIRES unity) \ No newline at end of file diff --git a/components/driver/CMakeLists.txt b/components/driver/CMakeLists.txt index 9b36ca854b..ddeb98d108 100644 --- a/components/driver/CMakeLists.txt +++ b/components/driver/CMakeLists.txt @@ -1,33 +1,32 @@ -set(COMPONENT_SRCS "can.c" - "gpio.c" - "i2c.c" - "i2s.c" - "ledc.c" - "mcpwm.c" - "pcnt.c" - "periph_ctrl.c" - "rmt.c" - "rtc_module.c" - "sdio_slave.c" - "sdmmc_host.c" - "sdmmc_transaction.c" - "sdspi_crc.c" - "sdspi_host.c" - "sdspi_transaction.c" - "sigmadelta.c" - "spi_common.c" - "spi_master.c" - "spi_slave.c" - "timer.c" - "uart.c") -set(COMPONENT_ADD_INCLUDEDIRS "include") -set(COMPONENT_PRIV_INCLUDEDIRS "include/driver") -set(COMPONENT_REQUIRES esp_ringbuf soc) #cannot totally hide soc headers, since there are a lot arguments in the driver are chip-dependent - -register_component() +set(srcs "can.c" + "gpio.c" + "i2c.c" + "i2s.c" + "ledc.c" + "mcpwm.c" + "pcnt.c" + "periph_ctrl.c" + "rmt.c" + "rtc_module.c" + "sdio_slave.c" + "sdmmc_host.c" + "sdmmc_transaction.c" + "sdspi_crc.c" + "sdspi_host.c" + "sdspi_transaction.c" + "sigmadelta.c" + "spi_common.c" + "spi_master.c" + "spi_slave.c" + "timer.c" + "uart.c") +idf_component_register(SRCS "${srcs}" + INCLUDE_DIRS "include" + PRIV_INCLUDE_DIRS "include/driver" + REQUIRES esp_ringbuf soc) #cannot totally hide soc headers, since there are a lot arguments in the driver are chip-dependent if(GCC_NOT_5_2_0) # uses C11 atomic feature set_source_files_properties(spi_master.c PROPERTIES COMPILE_FLAGS -std=gnu11) -endif() +endif() \ No newline at end of file diff --git a/components/driver/test/CMakeLists.txt b/components/driver/test/CMakeLists.txt index b48610adcd..7102bae72e 100644 --- a/components/driver/test/CMakeLists.txt +++ b/components/driver/test/CMakeLists.txt @@ -1,6 +1,3 @@ -set(COMPONENT_SRCDIRS ". param_test") -set(COMPONENT_ADD_INCLUDEDIRS "include param_test/include") - -set(COMPONENT_REQUIRES unity test_utils driver nvs_flash) - -register_component() \ No newline at end of file +idf_component_register(SRC_DIRS "." "param_test" + INCLUDE_DIRS "include" "param_test/include" + REQUIRES unity test_utils driver nvs_flash) \ No newline at end of file diff --git a/components/efuse/CMakeLists.txt b/components/efuse/CMakeLists.txt index f649a242f4..f43a2e6030 100644 --- a/components/efuse/CMakeLists.txt +++ b/components/efuse/CMakeLists.txt @@ -3,16 +3,17 @@ idf_build_get_property(soc_name IDF_TARGET) if(EXISTS "${COMPONENT_DIR}/${soc_name}") include(${COMPONENT_DIR}/${soc_name}/sources.cmake) spaces2list(EFUSE_SOC_SRCS) - add_prefix(COMPONENT_SRCS "${soc_name}/" ${EFUSE_SOC_SRCS}) - set(COMPONENT_ADD_INCLUDEDIRS include ${soc_name}/include) + add_prefix(srcs "${soc_name}/" ${EFUSE_SOC_SRCS}) + set(include_dirs include ${soc_name}/include) endif() -list(APPEND COMPONENT_SRCS "src/esp_efuse_api.c" - "src/esp_efuse_fields.c" - "src/esp_efuse_utility.c") +list(APPEND srcs "src/esp_efuse_api.c" + "src/esp_efuse_fields.c" + "src/esp_efuse_utility.c") -set(COMPONENT_PRIV_REQUIRES bootloader_support soc) -register_component() +idf_component_register(SRCS "${srcs}" + PRIV_REQUIRES bootloader_support soc + INCLUDE_DIRS "${include_dirs}") set(GEN_EFUSE_TABLE_ARG --max_blk_len ${CONFIG_EFUSE_MAX_BLK_LEN}) diff --git a/components/efuse/test/CMakeLists.txt b/components/efuse/test/CMakeLists.txt index 28d0b2e7aa..e239bfe5ce 100644 --- a/components/efuse/test/CMakeLists.txt +++ b/components/efuse/test/CMakeLists.txt @@ -1,6 +1,3 @@ -set(COMPONENT_SRCDIRS ".") -set(COMPONENT_ADD_INCLUDEDIRS "." "include") - -set(COMPONENT_REQUIRES unity test_utils efuse bootloader_support) - -register_component() \ No newline at end of file +idf_component_register(SRC_DIRS "." + INCLUDE_DIRS "." "include" + REQUIRES unity test_utils efuse bootloader_support) \ No newline at end of file diff --git a/components/esp-tls/CMakeLists.txt b/components/esp-tls/CMakeLists.txt index 23f953d940..e08418e5a1 100644 --- a/components/esp-tls/CMakeLists.txt +++ b/components/esp-tls/CMakeLists.txt @@ -1,7 +1,4 @@ -set(COMPONENT_SRCS "esp_tls.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -set(COMPONENT_REQUIRES mbedtls) -set(COMPONENT_PRIV_REQUIRES lwip nghttp) - -register_component() +idf_component_register(SRCS "esp_tls.c" + INCLUDE_DIRS "." + REQUIRES mbedtls + PRIV_REQUIRES lwip nghttp) diff --git a/components/esp32/CMakeLists.txt b/components/esp32/CMakeLists.txt index 9d763b41b9..b69f3747ed 100644 --- a/components/esp32/CMakeLists.txt +++ b/components/esp32/CMakeLists.txt @@ -1,50 +1,49 @@ -require_idf_targets(esp32) - if(BOOTLOADER_BUILD) # For bootloader, all we need from esp32 is headers - set(COMPONENT_ADD_INCLUDEDIRS include) - register_component() + idf_component_register(INCLUDE_DIRS include) target_linker_script(${COMPONENT_LIB} INTERFACE "ld/esp32.peripherals.ld") else() # Regular app build + set(srcs "brownout.c" + "cache_err_int.c" + "cache_sram_mmu.c" + "clk.c" + "cpu_start.c" + "crosscore_int.c" + "dport_access.c" + "dport_panic_highint_hdl.S" + "esp_adapter.c" + "esp_timer_esp32.c" + "esp_himem.c" + "gdbstub.c" + "hw_random.c" + "int_wdt.c" + "intr_alloc.c" + "panic.c" + "pm_esp32.c" + "pm_trace.c" + "reset_reason.c" + "sleep_modes.c" + "spiram.c" + "spiram_psram.c" + "system_api.c" + "task_wdt.c") + set(include_dirs "include") - set(COMPONENT_SRCS "brownout.c" - "cache_err_int.c" - "cache_sram_mmu.c" - "clk.c" - "cpu_start.c" - "crosscore_int.c" - "dport_access.c" - "dport_panic_highint_hdl.S" - "esp_adapter.c" - "esp_timer_esp32.c" - "esp_himem.c" - "gdbstub.c" - "hw_random.c" - "int_wdt.c" - "intr_alloc.c" - "panic.c" - "pm_esp32.c" - "pm_trace.c" - "reset_reason.c" - "sleep_modes.c" - "spiram.c" - "spiram_psram.c" - "system_api.c" - "task_wdt.c") - set(COMPONENT_ADD_INCLUDEDIRS "include") - - set(COMPONENT_REQUIRES driver esp_event efuse soc) #unfortunately rom/uart uses SOC registers directly + set(requires driver esp_event efuse soc) #unfortunately rom/uart uses SOC registers directly # driver is a public requirement because esp_sleep.h uses gpio_num_t & touch_pad_t # app_update is added here because cpu_start.c uses esp_ota_get_app_description() function. - set(COMPONENT_PRIV_REQUIRES - app_trace app_update bootloader_support log mbedtls nvs_flash pthread + set(priv_requires app_trace app_update bootloader_support log mbedtls nvs_flash pthread smartconfig_ack spi_flash vfs wpa_supplicant espcoredump esp_common esp_wifi) + set(fragments linker.lf ld/esp32_fragments.lf) - set(COMPONENT_ADD_LDFRAGMENTS linker.lf ld/esp32_fragments.lf) - - register_component() + idf_component_register(SRCS "${srcs}" + INCLUDE_DIRS "${include_dirs}" + LDFRAGMENTS "${fragments}" + REQUIRES "${requires}" + PRIV_REQUIRES "${priv_requires}" + REQUIRED_IDF_TARGETS esp32) target_linker_script(${COMPONENT_LIB} INTERFACE "${CMAKE_CURRENT_BINARY_DIR}/esp32_out.ld") diff --git a/components/esp32/test/CMakeLists.txt b/components/esp32/test/CMakeLists.txt index 1ced21ed8a..b36af1bd1d 100644 --- a/components/esp32/test/CMakeLists.txt +++ b/components/esp32/test/CMakeLists.txt @@ -1,9 +1,6 @@ -set(COMPONENT_SRCDIRS ".") -set(COMPONENT_ADD_INCLUDEDIRS ". ${CMAKE_CURRENT_BINARY_DIR}") - -set(COMPONENT_REQUIRES unity test_utils nvs_flash ulp esp_common) - -register_component() +idf_component_register(SRC_DIRS "." + INCLUDE_DIRS "." "${CMAKE_CURRENT_BINARY_DIR}" + REQUIRES unity test_utils nvs_flash ulp esp_common) add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/test_tjpgd_logo.h" COMMAND xxd -i "logo.jpg" "${CMAKE_CURRENT_BINARY_DIR}/test_tjpgd_logo.h" diff --git a/components/esp_adc_cal/CMakeLists.txt b/components/esp_adc_cal/CMakeLists.txt index 8bb99092e2..baef0b690e 100644 --- a/components/esp_adc_cal/CMakeLists.txt +++ b/components/esp_adc_cal/CMakeLists.txt @@ -1,6 +1,3 @@ -set(COMPONENT_SRCS "esp_adc_cal.c") -set(COMPONENT_ADD_INCLUDEDIRS "include") - -set(COMPONENT_REQUIRES) - -register_component() +idf_component_register(SRCS "esp_adc_cal.c" + INCLUDE_DIRS "include" + REQUIRES driver) diff --git a/components/esp_common/CMakeLists.txt b/components/esp_common/CMakeLists.txt index 18ffe93678..bb9bd8a7a4 100644 --- a/components/esp_common/CMakeLists.txt +++ b/components/esp_common/CMakeLists.txt @@ -1,27 +1,19 @@ if(BOOTLOADER_BUILD) # For bootloader, all we need from esp_common is headers - set(COMPONENT_ADD_INCLUDEDIRS include) - set(COMPONENT_REQUIRES ${IDF_COMPONENTS}) - set(COMPONENT_SRCS ) - register_component() + idf_component_register(INCLUDE_DIRS include) set_property(TARGET ${COMPONENT_LIB} APPEND PROPERTY INTERFACE_LINK_LIBRARIES "-Wl,--gc-sections") else() # Regular app build - set(COMPONENT_SRCS - "src/dbg_stubs.c" - "src/esp_err_to_name.c" - "src/esp_timer.c" - "src/ets_timer_legacy.c" - "src/freertos_hooks.c" - "src/ipc.c" - "src/pm_locks.c" - "src/stack_check.c") - set(COMPONENT_ADD_INCLUDEDIRS "include") - set(COMPONENT_PRIV_INCLUDEDIRS) - set(COMPONENT_REQUIRES) - set(COMPONENT_PRIV_REQUIRES soc) - - register_component() + idf_component_register(SRCS "src/dbg_stubs.c" + "src/esp_err_to_name.c" + "src/esp_timer.c" + "src/ets_timer_legacy.c" + "src/freertos_hooks.c" + "src/ipc.c" + "src/pm_locks.c" + "src/stack_check.c" + INCLUDE_DIRS include + PRIV_REQUIRES soc) set_source_files_properties( "src/stack_check.c" diff --git a/components/esp_event/CMakeLists.txt b/components/esp_event/CMakeLists.txt index 53aff6b0a2..becd3ba7f6 100644 --- a/components/esp_event/CMakeLists.txt +++ b/components/esp_event/CMakeLists.txt @@ -1,19 +1,13 @@ -set(COMPONENT_SRCS "default_event_loop.c" - "esp_event.c" - "esp_event_private.c" - "event_loop_legacy.c" - "event_send.c") - -set(COMPONENT_ADD_INCLUDEDIRS "include") -set(COMPONENT_PRIV_INCLUDEDIRS "private_include") - -set(COMPONENT_REQUIRES log tcpip_adapter) -set(COMPONENT_PRIV_REQUIRES ethernet) -set(COMPONENT_REQUIRES log tcpip_adapter ethernet) - -set(COMPONENT_ADD_LDFRAGMENTS linker.lf) - -register_component() +idf_component_register(SRCS "default_event_loop.c" + "esp_event.c" + "esp_event_private.c" + "event_loop_legacy.c" + "event_send.c" + INCLUDE_DIRS "include" + PRIV_INCLUDE_DIRS "private_include" + REQUIRES log tcpip_adapter + PRIV_REQUIRES ethernet + LDFRAGMENTS linker.lf) if(GCC_NOT_5_2_0 AND CONFIG_ESP_EVENT_LOOP_PROFILING) # uses C11 atomic feature diff --git a/components/esp_event/test/CMakeLists.txt b/components/esp_event/test/CMakeLists.txt index 287a2ba928..8264f10dda 100644 --- a/components/esp_event/test/CMakeLists.txt +++ b/components/esp_event/test/CMakeLists.txt @@ -1,5 +1,3 @@ -set(COMPONENT_SRCDIRS ".") -set(COMPONENT_PRIV_INCLUDEDIRS "../private_include" ".") -set(COMPONENT_PRIV_REQUIRES unity test_utils esp_event driver) - -register_component() \ No newline at end of file +idf_component_register(SRC_DIRS "." + PRIV_INCLUDE_DIRS "../private_include" "." + REQUIRES unity test_utils esp_event driver) diff --git a/components/esp_http_client/CMakeLists.txt b/components/esp_http_client/CMakeLists.txt index 2bea7ce022..327e90778a 100644 --- a/components/esp_http_client/CMakeLists.txt +++ b/components/esp_http_client/CMakeLists.txt @@ -1,11 +1,8 @@ -set(COMPONENT_SRCS "esp_http_client.c" - "lib/http_auth.c" - "lib/http_header.c" - "lib/http_utils.c") -set(COMPONENT_ADD_INCLUDEDIRS "include") -set(COMPONENT_PRIV_INCLUDEDIRS "lib/include") - -set(COMPONENT_REQUIRES "nghttp") -set(COMPONENT_PRIV_REQUIRES "mbedtls" "lwip" "esp-tls" "tcp_transport") - -register_component() +idf_component_register(SRCS "esp_http_client.c" + "lib/http_auth.c" + "lib/http_header.c" + "lib/http_utils.c" + INCLUDE_DIRS "include" + PRIV_INCLUDE_DIRS "lib/include" + REQUIRES nghttp + PRIV_REQUIRES mbedtls lwip esp-tls tcp_transport) diff --git a/components/esp_http_client/test/CMakeLists.txt b/components/esp_http_client/test/CMakeLists.txt index 6b99d7546a..70fc98dcad 100644 --- a/components/esp_http_client/test/CMakeLists.txt +++ b/components/esp_http_client/test/CMakeLists.txt @@ -1,6 +1,3 @@ -set(COMPONENT_SRCDIRS ".") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -set(COMPONENT_REQUIRES unity test_utils esp_http_client) - -register_component() +idf_component_register(SRC_DIRS "." + INCLUDE_DIRS "." + REQUIRES unity test_utils esp_http_client) \ No newline at end of file diff --git a/components/esp_http_server/CMakeLists.txt b/components/esp_http_server/CMakeLists.txt index 700a0b9cb1..4bf15aa866 100644 --- a/components/esp_http_server/CMakeLists.txt +++ b/components/esp_http_server/CMakeLists.txt @@ -1,13 +1,10 @@ -set(COMPONENT_ADD_INCLUDEDIRS include) -set(COMPONENT_PRIV_INCLUDEDIRS src/port/esp32 src/util) -set(COMPONENT_SRCS "src/httpd_main.c" - "src/httpd_parse.c" - "src/httpd_sess.c" - "src/httpd_txrx.c" - "src/httpd_uri.c" - "src/util/ctrl_sock.c") - -set(COMPONENT_REQUIRES nghttp) # for http_parser.h -set(COMPONENT_PRIV_REQUIRES lwip) - -register_component() +idf_component_register(SRCS "src/httpd_main.c" + "src/httpd_parse.c" + "src/httpd_sess.c" + "src/httpd_txrx.c" + "src/httpd_uri.c" + "src/util/ctrl_sock.c" + INCLUDE_DIRS "include" + PRIV_INCLUDE_DIRS "src/port/esp32" "src/util" + REQUIRES nghttp + PRIV_REQUIRES lwip) # for http_parser.h diff --git a/components/esp_http_server/test/CMakeLists.txt b/components/esp_http_server/test/CMakeLists.txt index de20de38f5..e2ceeb2986 100644 --- a/components/esp_http_server/test/CMakeLists.txt +++ b/components/esp_http_server/test/CMakeLists.txt @@ -1,6 +1,3 @@ -set(COMPONENT_SRCDIRS ".") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -set(COMPONENT_REQUIRES unity test_utils esp_http_server) - -register_component() +idf_component_register(SRC_DIRS "." + INCLUDE_DIRS "." + REQUIRES unity test_utils esp_http_server) \ No newline at end of file diff --git a/components/esp_https_ota/CMakeLists.txt b/components/esp_https_ota/CMakeLists.txt index 6ef0a5c9e0..8608666fde 100644 --- a/components/esp_https_ota/CMakeLists.txt +++ b/components/esp_https_ota/CMakeLists.txt @@ -1,7 +1,4 @@ -set(COMPONENT_ADD_INCLUDEDIRS include) -set(COMPONENT_SRCS "src/esp_https_ota.c") - -set(COMPONENT_REQUIRES esp_http_client) -set(COMPONENT_PRIV_REQUIRES log app_update) - -register_component() +idf_component_register(SRCS "src/esp_https_ota.c" + INCLUDE_DIRS "include" + REQUIRES esp_http_client + PRIV_REQUIRES log app_update) diff --git a/components/esp_https_server/CMakeLists.txt b/components/esp_https_server/CMakeLists.txt index dfdaa11ecc..c63549b0e2 100644 --- a/components/esp_https_server/CMakeLists.txt +++ b/components/esp_https_server/CMakeLists.txt @@ -1,7 +1,4 @@ -set(COMPONENT_ADD_INCLUDEDIRS include) -set(COMPONENT_SRCS "src/https_server.c") - -set(COMPONENT_REQUIRES esp_http_server openssl) -set(COMPONENT_PRIV_REQUIRES lwip) - -register_component() +idf_component_register(SRCS "src/https_server.c" + INCLUDE_DIRS "include" + REQUIRES esp_http_server openssl + PRIV_REQUIRES lwip) diff --git a/components/esp_ringbuf/CMakeLists.txt b/components/esp_ringbuf/CMakeLists.txt index bba1dfd6d8..4c4b114af9 100644 --- a/components/esp_ringbuf/CMakeLists.txt +++ b/components/esp_ringbuf/CMakeLists.txt @@ -1,7 +1,3 @@ -set(COMPONENT_ADD_INCLUDEDIRS "include") -set(COMPONENT_SRCS "ringbuf.c") -set(COMPONENT_ADD_LDFRAGMENTS linker.lf) - -set(COMPONENT_REQUIRES) - -register_component() +idf_component_register(SRCS "ringbuf.c" + INCLUDE_DIRS "include" + LDFRAGMENTS linker.lf) diff --git a/components/esp_ringbuf/test/CMakeLists.txt b/components/esp_ringbuf/test/CMakeLists.txt index 236536c476..b531a14504 100644 --- a/components/esp_ringbuf/test/CMakeLists.txt +++ b/components/esp_ringbuf/test/CMakeLists.txt @@ -1,6 +1,3 @@ -set(COMPONENT_SRCDIRS ".") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -set(COMPONENT_REQUIRES unity test_utils) - -register_component() +idf_component_register(SRC_DIRS "." + INCLUDE_DIRS "." + REQUIRES unity test_utils) \ No newline at end of file diff --git a/components/esp_rom/CMakeLists.txt b/components/esp_rom/CMakeLists.txt index 188f511eac..2c903a5100 100644 --- a/components/esp_rom/CMakeLists.txt +++ b/components/esp_rom/CMakeLists.txt @@ -1,9 +1,6 @@ if(BOOTLOADER_BUILD) # For bootloader, all we need is headers - set(COMPONENT_ADD_INCLUDEDIRS "include") - set(COMPONENT_REQUIRES ${IDF_COMPONENTS}) - set(COMPONENT_SRCS) - register_component() + idf_component_register(INCLUDE_DIRS include) set(scripts "esp32/ld/esp32.rom.ld" "esp32/ld/esp32.rom.newlib-funcs.ld" @@ -12,10 +9,8 @@ if(BOOTLOADER_BUILD) target_linker_script(${COMPONENT_LIB} INTERFACE "${scripts}") else() # Regular app build - set(COMPONENT_SRCS "esp_rom.c") - set(COMPONENT_ADD_INCLUDEDIRS "include") - - register_component() + idf_component_register(SRCS "esp_rom.c" + INCLUDE_DIRS include) set(scripts "esp32/ld/esp32.rom.ld" diff --git a/components/esp_wifi/CMakeLists.txt b/components/esp_wifi/CMakeLists.txt index 5349e905ac..4cf4723b4a 100644 --- a/components/esp_wifi/CMakeLists.txt +++ b/components/esp_wifi/CMakeLists.txt @@ -1,7 +1,7 @@ idf_build_get_property(idf_target IDF_TARGET) idf_build_get_property(build_dir BUILD_DIR) -set(COMPONENT_SRCS +set(srcs "src/coexist.c" "src/fast_crypto_ops.c" "src/lib_printf.c" @@ -9,15 +9,16 @@ set(COMPONENT_SRCS "src/phy_init.c" "src/restore.c" "src/wifi_init.c") -set(COMPONENT_ADD_INCLUDEDIRS "include" "${idf_target}/include") -set(COMPONENT_PRIV_INCLUDEDIRS) -set(COMPONENT_PRIV_REQUIRES wpa_supplicant nvs_flash) if(NOT CONFIG_ESP32_NO_BLOBS) - set(COMPONENT_ADD_LDFRAGMENTS "linker.lf") + set(ldfragments "linker.lf") endif() -register_component() +idf_component_register(SRCS "${srcs}" + INCLUDE_DIRS "include" "${idf_target}/include" + PRIV_REQUIRES wpa_supplicant nvs_flash + LDFRAGMENTS "${ldfragments}") + target_link_libraries(${COMPONENT_LIB} PUBLIC "-L ${CMAKE_CURRENT_SOURCE_DIR}/lib_${idf_target}") if(NOT CONFIG_ESP32_NO_BLOBS) diff --git a/components/esp_wifi/test/CMakeLists.txt b/components/esp_wifi/test/CMakeLists.txt index 6f9211dcc6..a3ff3757c3 100644 --- a/components/esp_wifi/test/CMakeLists.txt +++ b/components/esp_wifi/test/CMakeLists.txt @@ -1,9 +1,6 @@ -set(COMPONENT_SRCDIRS ".") -set(COMPONENT_ADD_INCLUDEDIRS ". ${CMAKE_CURRENT_BINARY_DIR}") - -set(COMPONENT_REQUIRES unity test_utils nvs_flash ulp esp_common) - -register_component() +idf_component_register(SRCDIRS "." + INCLUDE_DIRS "." "${CMAKE_CURRENT_BINARY_DIR}" + REQUIRES unity test_utils nvs_flash ulp esp_common) idf_component_get_property(esp_wifi_dir esp_wifi COMPONENT_DIR) diff --git a/components/espcoredump/CMakeLists.txt b/components/espcoredump/CMakeLists.txt index 8ef4c1ce70..f925f93db2 100644 --- a/components/espcoredump/CMakeLists.txt +++ b/components/espcoredump/CMakeLists.txt @@ -1,10 +1,8 @@ -set(COMPONENT_PRIV_INCLUDEDIRS "include_core_dump") -set(COMPONENT_ADD_INCLUDEDIRS "include") -set(COMPONENT_PRIV_REQUIRES spi_flash soc) -set(COMPONENT_ADD_LDFRAGMENTS linker.lf) -set(COMPONENT_SRCS "src/core_dump_common.c" - "src/core_dump_flash.c" - "src/core_dump_port.c" - "src/core_dump_uart.c") - -register_component() +idf_component_register(SRCS "src/core_dump_common.c" + "src/core_dump_flash.c" + "src/core_dump_port.c" + "src/core_dump_uart.c" + INCLUDE_DIRS "include" + PRIV_INCLUDE_DIRS "include_core_dump" + LDFRAGMENTS linker.lf + PRIV_REQUIRES spi_flash soc) diff --git a/components/espcoredump/test/CMakeLists.txt b/components/espcoredump/test/CMakeLists.txt index 6704c238b9..c000ba4e57 100644 --- a/components/espcoredump/test/CMakeLists.txt +++ b/components/espcoredump/test/CMakeLists.txt @@ -1,8 +1,7 @@ if(TESTS_ALL EQUAL 1) message("not linking coredump test from CI.") else() - set(COMPONENT_SRCDIRS ".") - set(COMPONENT_ADD_INCLUDEDIRS ".") - set(COMPONENT_REQUIRES unity nvs_flash) - register_component() + idf_component_register(SRC_DIRS "." + INCLUDE_DIRS "." + REQUIRES unity nvs_flash) endif() \ No newline at end of file diff --git a/components/esptool_py/CMakeLists.txt b/components/esptool_py/CMakeLists.txt index 09764ccf05..10a0f3457d 100644 --- a/components/esptool_py/CMakeLists.txt +++ b/components/esptool_py/CMakeLists.txt @@ -1,5 +1,4 @@ -set(COMPONENT_PRIV_REQUIRES bootloader) -register_component() +idf_component_register(REQUIRES bootloader) string(REPLACE ";" " " ESPTOOLPY_FLASH_PROJECT_OPTIONS "${ESPTOOLPY_ELF2IMAGE_FLASH_OPTIONS}") set(ESPTOOLPY_FLASH_PROJECT_OPTIONS diff --git a/components/ethernet/CMakeLists.txt b/components/ethernet/CMakeLists.txt index 704d21a7d7..3951c97c22 100644 --- a/components/ethernet/CMakeLists.txt +++ b/components/ethernet/CMakeLists.txt @@ -1,12 +1,8 @@ -set(COMPONENT_SRCS "emac_dev.c" - "emac_main.c" - "eth_phy/phy_common.c" - "eth_phy/phy_lan8720.c" - "eth_phy/phy_tlk110.c" - "eth_phy/phy_ip101.c") -set(COMPONENT_ADD_INCLUDEDIRS "include") - -set(COMPONENT_REQUIRES) -set(COMPONENT_PRIV_REQUIRES tcpip_adapter esp_event soc) - -register_component() +idf_component_register(SRCS "emac_dev.c" + "emac_main.c" + "eth_phy/phy_common.c" + "eth_phy/phy_lan8720.c" + "eth_phy/phy_tlk110.c" + "eth_phy/phy_ip101.c" + INCLUDE_DIRS "include" + PRIV_REQUIRES tcpip_adapter esp_event soc) diff --git a/components/ethernet/test/CMakeLists.txt b/components/ethernet/test/CMakeLists.txt index d5d44577ae..ae46c13bfd 100644 --- a/components/ethernet/test/CMakeLists.txt +++ b/components/ethernet/test/CMakeLists.txt @@ -1,5 +1,3 @@ -set(COMPONENT_SRCDIRS ".") -set(COMPONENT_ADD_INCLUDEDIRS ".") -set(COMPONENT_REQUIRES unity ethernet) - -register_component() \ No newline at end of file +idf_component_register(SRC_DIRS "." + INCLUDE_DIRS "." + REQUIRES unity ethernet) \ No newline at end of file diff --git a/components/expat/CMakeLists.txt b/components/expat/CMakeLists.txt index 596aee917c..a6c5882c79 100644 --- a/components/expat/CMakeLists.txt +++ b/components/expat/CMakeLists.txt @@ -1,11 +1,10 @@ -set(COMPONENT_ADD_INCLUDEDIRS expat/expat/lib port/include) -set(COMPONENT_SRCS "expat/expat/lib/loadlibrary.c" - "expat/expat/lib/xmlparse.c" - "expat/expat/lib/xmlrole.c" - "expat/expat/lib/xmltok.c" - "expat/expat/lib/xmltok_impl.c" - "expat/expat/lib/xmltok_ns.c") -register_component() +idf_component_register(SRCS "expat/expat/lib/loadlibrary.c" + "expat/expat/lib/xmlparse.c" + "expat/expat/lib/xmlrole.c" + "expat/expat/lib/xmltok.c" + "expat/expat/lib/xmltok_impl.c" + "expat/expat/lib/xmltok_ns.c" + INCLUDE_DIRS expat/expat/lib port/include) target_compile_definitions(${COMPONENT_LIB} PRIVATE HAVE_EXPAT_CONFIG_H) target_compile_definitions(${COMPONENT_LIB} PRIVATE HAVE_GETRANDOM) diff --git a/components/expat/test/CMakeLists.txt b/components/expat/test/CMakeLists.txt index be2a822177..28d0afb413 100644 --- a/components/expat/test/CMakeLists.txt +++ b/components/expat/test/CMakeLists.txt @@ -1,5 +1,3 @@ -set(COMPONENT_SRCDIRS ".") -set(COMPONENT_ADD_INCLUDEDIRS ".") -set(COMPONENT_REQUIRES unity expat) - -register_component() +idf_component_register(SRC_DIRS "." + INCLUDE_DIRS "." + REQUIRES unity expat) \ No newline at end of file diff --git a/components/fatfs/CMakeLists.txt b/components/fatfs/CMakeLists.txt index 4df07d30b9..d46f077252 100644 --- a/components/fatfs/CMakeLists.txt +++ b/components/fatfs/CMakeLists.txt @@ -1,15 +1,14 @@ -set(COMPONENT_SRCS "src/diskio.c" - "src/diskio_rawflash.c" - "src/diskio_sdmmc.c" - "src/diskio_wl.c" - "src/ff.c" - "src/ffsystem.c" - "src/ffunicode.c" - "src/vfs_fat.c" - "src/vfs_fat_sdmmc.c" - "src/vfs_fat_spiflash.c") -set(COMPONENT_ADD_INCLUDEDIRS src) - -set(COMPONENT_REQUIRES wear_levelling sdmmc) - -register_component() +set(srcs "src/diskio.c" + "src/diskio_rawflash.c" + "src/diskio_sdmmc.c" + "src/diskio_wl.c" + "src/ff.c" + "src/ffsystem.c" + "src/ffunicode.c" + "src/vfs_fat.c" + "src/vfs_fat_sdmmc.c" + "src/vfs_fat_spiflash.c") + +idf_component_register(SRCS "${srcs}" + INCLUDE_DIRS src + REQUIRES wear_levelling sdmmc) diff --git a/components/fatfs/test/CMakeLists.txt b/components/fatfs/test/CMakeLists.txt index 5e0431c469..2796e95eb4 100644 --- a/components/fatfs/test/CMakeLists.txt +++ b/components/fatfs/test/CMakeLists.txt @@ -1,8 +1,4 @@ -set(COMPONENT_SRCDIRS ".") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -set(COMPONENT_REQUIRES unity test_utils vfs fatfs) - -set(COMPONENT_EMBED_TXTFILES fatfs.img) - -register_component() \ No newline at end of file +idf_component_register(SRC_DIRS "." + INCLUDE_DIRS "." + REQUIRES unity test_utils vfs fatfs + EMBED_TXTFILES fatfs.img) \ No newline at end of file diff --git a/components/freemodbus/CMakeLists.txt b/components/freemodbus/CMakeLists.txt index 4b57c16619..0bf6b6af07 100644 --- a/components/freemodbus/CMakeLists.txt +++ b/components/freemodbus/CMakeLists.txt @@ -1,41 +1,43 @@ # The following five lines of boilerplate have to be in your project's # CMakeLists in this exact order for cmake to work correctly -set(COMPONENT_SRCS "common/esp_modbus_master.c" - "common/esp_modbus_slave.c" - "modbus/mb.c" - "modbus/mb_m.c" - "modbus/ascii/mbascii.c" - "modbus/rtu/mbrtu_m.c" - "modbus/rtu/mbrtu.c" - "modbus/rtu/mbcrc.c" - "modbus/tcp/mbtcp.c" - "port/port.c" - "port/portevent.c" - "port/portevent_m.c" - "port/portother.c" - "port/portother_m.c" - "port/portserial.c" - "port/portserial_m.c" - "port/porttimer.c" - "port/porttimer_m.c" - "modbus/functions/mbfunccoils.c" - "modbus/functions/mbfunccoils_m.c" - "modbus/functions/mbfuncdiag.c" - "modbus/functions/mbfuncdisc.c" - "modbus/functions/mbfuncdisc_m.c" - "modbus/functions/mbfuncholding.c" - "modbus/functions/mbfuncholding_m.c" - "modbus/functions/mbfuncinput.c" - "modbus/functions/mbfuncinput_m.c" - "modbus/functions/mbfuncother.c" - "modbus/functions/mbutils.c" - "serial_slave/modbus_controller/mbc_serial_slave.c" - "serial_master/modbus_controller/mbc_serial_master.c") -set(COMPONENT_ADD_INCLUDEDIRS common/include) -set(COMPONENT_PRIV_INCLUDEDIRS common port modbus modbus/ascii modbus/functions +set(srcs "common/esp_modbus_master.c" + "common/esp_modbus_slave.c" + "modbus/mb.c" + "modbus/mb_m.c" + "modbus/ascii/mbascii.c" + "modbus/rtu/mbrtu_m.c" + "modbus/rtu/mbrtu.c" + "modbus/rtu/mbcrc.c" + "modbus/tcp/mbtcp.c" + "port/port.c" + "port/portevent.c" + "port/portevent_m.c" + "port/portother.c" + "port/portother_m.c" + "port/portserial.c" + "port/portserial_m.c" + "port/porttimer.c" + "port/porttimer_m.c" + "modbus/functions/mbfunccoils.c" + "modbus/functions/mbfunccoils_m.c" + "modbus/functions/mbfuncdiag.c" + "modbus/functions/mbfuncdisc.c" + "modbus/functions/mbfuncdisc_m.c" + "modbus/functions/mbfuncholding.c" + "modbus/functions/mbfuncholding_m.c" + "modbus/functions/mbfuncinput.c" + "modbus/functions/mbfuncinput_m.c" + "modbus/functions/mbfuncother.c" + "modbus/functions/mbutils.c" + "serial_slave/modbus_controller/mbc_serial_slave.c" + "serial_master/modbus_controller/mbc_serial_master.c") +set(include_dirs common/include) +set(priv_include_dirs common port modbus modbus/ascii modbus/functions modbus/rtu modbus/tcp modbus/include) -list(APPEND COMPONENT_PRIV_INCLUDEDIRS serial_slave/port serial_slave/modbus_controller +list(APPEND priv_include_dirs serial_slave/port serial_slave/modbus_controller serial_master/port serial_master/modbus_controller) -set(COMPONENT_REQUIRES "driver") -register_component() +idf_component_register(SRCS "${srcs}" + INCLUDE_DIRS "${include_dirs}" + PRIV_INCLUDE_DIRS "${priv_include_dirs}" + REQUIRES driver) diff --git a/components/freertos/CMakeLists.txt b/components/freertos/CMakeLists.txt index a7a83bbebc..0efd886e0d 100644 --- a/components/freertos/CMakeLists.txt +++ b/components/freertos/CMakeLists.txt @@ -1,29 +1,27 @@ -set(COMPONENT_ADD_INCLUDEDIRS include) -set(COMPONENT_PRIV_INCLUDEDIRS include/freertos .) -set(COMPONENT_SRCS "croutine.c" - "event_groups.c" - "FreeRTOS-openocd.c" - "list.c" - "port.c" - "portasm.S" - "queue.c" - "tasks.c" - "timers.c" - "xtensa_context.S" - "xtensa_init.c" - "xtensa_intr.c" - "xtensa_intr_asm.S" - "xtensa_overlay_os_hook.c" - "xtensa_vector_defaults.S" - "xtensa_vectors.S") - +set(srcs "croutine.c" + "event_groups.c" + "FreeRTOS-openocd.c" + "list.c" + "port.c" + "portasm.S" + "queue.c" + "tasks.c" + "timers.c" + "xtensa_context.S" + "xtensa_init.c" + "xtensa_intr.c" + "xtensa_intr_asm.S" + "xtensa_overlay_os_hook.c" + "xtensa_vector_defaults.S" + "xtensa_vectors.S") # app_trace is required by FreeRTOS headers only when CONFIG_SYSVIEW_ENABLE=y, # but requirements can't depend on config options, so always require it. -set(COMPONENT_REQUIRES app_trace) -set(COMPONENT_PRIV_REQUIRES esp_common soc) -set(COMPONENT_ADD_LDFRAGMENTS linker.lf) - -register_component() +idf_component_register(SRCS "${srcs}" + INCLUDE_DIRS include + PRIV_INCLUDE_DIRS include/freertos . + LDFRAGMENTS linker.lf + REQUIRES app_trace + PRIV_REQUIRES soc) target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--undefined=uxTopUsedPriority") diff --git a/components/freertos/test/CMakeLists.txt b/components/freertos/test/CMakeLists.txt index 66a8c82315..b531a14504 100644 --- a/components/freertos/test/CMakeLists.txt +++ b/components/freertos/test/CMakeLists.txt @@ -1,6 +1,3 @@ -set(COMPONENT_SRCDIRS ".") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -set(COMPONENT_REQUIRES unity test_utils) - -register_component() \ No newline at end of file +idf_component_register(SRC_DIRS "." + INCLUDE_DIRS "." + REQUIRES unity test_utils) \ No newline at end of file diff --git a/components/heap/CMakeLists.txt b/components/heap/CMakeLists.txt index 8ed72d2f2f..40fac3710b 100644 --- a/components/heap/CMakeLists.txt +++ b/components/heap/CMakeLists.txt @@ -1,23 +1,23 @@ -set(COMPONENT_SRCS "heap_caps.c" - "heap_caps_init.c" - "multi_heap.c") +set(srcs "heap_caps.c" + "heap_caps_init.c" + "multi_heap.c") if(NOT CONFIG_HEAP_POISONING_DISABLED) - list(APPEND COMPONENT_SRCS "multi_heap_poisoning.c") + list(APPEND srcs "multi_heap_poisoning.c") endif() if(CONFIG_HEAP_TASK_TRACKING) - list(APPEND COMPONENT_SRCS "heap_task_info.c") + list(APPEND srcs "heap_task_info.c") endif() if(CONFIG_HEAP_TRACING_STANDALONE) - list(APPEND COMPONENT_SRCS "heap_trace_standalone.c") + list(APPEND srcs "heap_trace_standalone.c") endif() -set(COMPONENT_ADD_INCLUDEDIRS "include") -set(COMPONENT_ADD_LDFRAGMENTS linker.lf) -set(COMPONENT_PRIV_REQUIRES soc) -register_component() +idf_component_register(SRCS "${srcs}" + INCLUDE_DIRS include + LDFRAGMENTS linker.lf + PRIV_REQUIRES soc) if(CONFIG_HEAP_TRACING) set(WRAP_FUNCTIONS diff --git a/components/heap/test/CMakeLists.txt b/components/heap/test/CMakeLists.txt index aed6c9743e..b70637f263 100644 --- a/components/heap/test/CMakeLists.txt +++ b/components/heap/test/CMakeLists.txt @@ -1,6 +1,3 @@ -set(COMPONENT_SRCDIRS ".") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -set(COMPONENT_REQUIRES unity test_utils heap) - -register_component() \ No newline at end of file +idf_component_register(SRC_DIRS "." + INCLUDE_DIRS "." + REQUIRES unity test_utils heap) \ No newline at end of file diff --git a/components/idf_test/CMakeLists.txt b/components/idf_test/CMakeLists.txt index e307838a8b..eafb7ac220 100644 --- a/components/idf_test/CMakeLists.txt +++ b/components/idf_test/CMakeLists.txt @@ -1,2 +1 @@ -set(COMPONENT_ADD_INCLUDEDIRS "include") -register_component() +idf_component_register(INCLUDE_DIRS include) diff --git a/components/jsmn/CMakeLists.txt b/components/jsmn/CMakeLists.txt index 562a2cadcb..0c7a0ab99e 100644 --- a/components/jsmn/CMakeLists.txt +++ b/components/jsmn/CMakeLists.txt @@ -1,3 +1,2 @@ -set(COMPONENT_SRCS "src/jsmn.c") -set(COMPONENT_ADD_INCLUDEDIRS "include") -register_component() +idf_component_register(SRCS "src/jsmn.c" + INCLUDE_DIRS "include") diff --git a/components/json/CMakeLists.txt b/components/json/CMakeLists.txt index 4a85a0b3d1..1ecadb3858 100644 --- a/components/json/CMakeLists.txt +++ b/components/json/CMakeLists.txt @@ -1,5 +1,4 @@ -set(COMPONENT_SRCS "cJSON/cJSON.c" - "cJSON/cJSON_Utils.c" - "cJSON/test.c") -set(COMPONENT_ADD_INCLUDEDIRS cJSON) -register_component() +idf_component_register(SRCS "cJSON/cJSON.c" + "cJSON/cJSON_Utils.c" + "cJSON/test.c" + INCLUDE_DIRS cJSON) diff --git a/components/libsodium/CMakeLists.txt b/components/libsodium/CMakeLists.txt index caef522233..358a840d25 100644 --- a/components/libsodium/CMakeLists.txt +++ b/components/libsodium/CMakeLists.txt @@ -1,7 +1,7 @@ set(SRC libsodium/src/libsodium) # Derived from libsodium/src/libsodium/Makefile.am # (ignoring the !MINIMAL set) -set(COMPONENT_SRCS "${SRC}/crypto_aead/chacha20poly1305/sodium/aead_chacha20poly1305.c" +set(srcs "${SRC}/crypto_aead/chacha20poly1305/sodium/aead_chacha20poly1305.c" "${SRC}/crypto_aead/xchacha20poly1305/sodium/aead_xchacha20poly1305.c" "${SRC}/crypto_auth/crypto_auth.c" "${SRC}/crypto_auth/hmacsha256/auth_hmacsha256.c" @@ -116,18 +116,19 @@ set(COMPONENT_SRCS "${SRC}/crypto_aead/chacha20poly1305/sodium/aead_chacha20poly "port/randombytes_esp32.c") if(CONFIG_LIBSODIUM_USE_MBEDTLS_SHA) - list(APPEND COMPONENT_SRCS "port/crypto_hash_mbedtls/crypto_hash_sha256_mbedtls.c" + list(APPEND srcs "port/crypto_hash_mbedtls/crypto_hash_sha256_mbedtls.c" "port/crypto_hash_mbedtls/crypto_hash_sha512_mbedtls.c") else() - list(APPEND COMPONENT_SRCS "${SRC}/crypto_hash/sha256/cp/hash_sha256_cp.c" + list(APPEND srcs "${SRC}/crypto_hash/sha256/cp/hash_sha256_cp.c" "${SRC}/crypto_hash/sha512/cp/hash_sha512_cp.c") endif() -set(COMPONENT_ADD_INCLUDEDIRS ${SRC}/include port_include) -set(COMPONENT_PRIV_INCLUDEDIRS ${SRC}/include/sodium port_include/sodium port) - -set(COMPONENT_REQUIRES mbedtls) -register_component() +set(include_dirs ${SRC}/include port_include) +set(priv_include_dirs ${SRC}/include/sodium port_include/sodium port) +idf_component_register(SRCS "${srcs}" + INCLUDE_DIRS "${include_dirs}" + PRIV_INCLUDE_DIRS "${priv_include_dirs}" + REQUIRES mbedtls) target_compile_definitions(${COMPONENT_LIB} PRIVATE CONFIGURED diff --git a/components/libsodium/test/CMakeLists.txt b/components/libsodium/test/CMakeLists.txt index 76ac24d2fc..2e3a43bad6 100644 --- a/components/libsodium/test/CMakeLists.txt +++ b/components/libsodium/test/CMakeLists.txt @@ -2,11 +2,7 @@ if(TESTS_ALL EQUAL 1) message("not linking libsodium tests, use '-T libsodium' to test it") else() get_filename_component(LS_TESTDIR "${CMAKE_CURRENT_LIST_DIR}/../libsodium/test/default" ABSOLUTE) - - set(COMPONENT_ADD_INCLUDEDIRS "." "${LS_TESTDIR}/../quirks") - - set(COMPONENT_REQUIRES unity libsodium) - + set(TEST_CASES "chacha20;aead_chacha20poly1305;box;box2;ed25519_convert;sign;hash") foreach(test_case ${TEST_CASES}) @@ -14,9 +10,9 @@ else() list(APPEND TEST_CASES_FILES ${test_case_file}) endforeach() - set(COMPONENT_SRCS "${TEST_CASES_FILES};test_sodium.c") - - register_component() + idf_component_register(SRCS "${TEST_CASES_FILES}" "test_sodium.c" + INCLUDE_DIRS "." "${LS_TESTDIR}/../quirks" + REQUIRES unity libsodium) # The libsodium test suite is designed to be run each test case as an executable on a desktop computer and uses # filesytem to write & then compare contents of each file. diff --git a/components/log/CMakeLists.txt b/components/log/CMakeLists.txt index 505dc93186..4ca3c1a544 100644 --- a/components/log/CMakeLists.txt +++ b/components/log/CMakeLists.txt @@ -1,4 +1,3 @@ -set(COMPONENT_SRCS "log.c") -set(COMPONENT_ADD_INCLUDEDIRS "include") -set(COMPONENT_PRIV_REQUIRES soc) -register_component() +idf_component_register(SRCS "log.c" + INCLUDE_DIRS "include" + PRIV_REQUIRES soc) diff --git a/components/lwip/CMakeLists.txt b/components/lwip/CMakeLists.txt index 6a15d6f5c8..6eda8db37e 100644 --- a/components/lwip/CMakeLists.txt +++ b/components/lwip/CMakeLists.txt @@ -1,4 +1,4 @@ -set(COMPONENT_ADD_INCLUDEDIRS +set(include_dirs include/apps include/apps/sntp lwip/src/include @@ -6,92 +6,92 @@ set(COMPONENT_ADD_INCLUDEDIRS port/esp32/include/arch ) -set(COMPONENT_SRCS "apps/dhcpserver/dhcpserver.c" - "apps/ping/esp_ping.c" - "apps/ping/ping.c" - "apps/sntp/sntp.c" - "lwip/src/api/api_lib.c" - "lwip/src/api/api_msg.c" - "lwip/src/api/err.c" - "lwip/src/api/netbuf.c" - "lwip/src/api/netdb.c" - "lwip/src/api/netifapi.c" - "lwip/src/api/sockets.c" - "lwip/src/api/tcpip.c" - "lwip/src/apps/sntp/sntp.c" - "lwip/src/core/def.c" - "lwip/src/core/dns.c" - "lwip/src/core/inet_chksum.c" - "lwip/src/core/init.c" - "lwip/src/core/ip.c" - "lwip/src/core/mem.c" - "lwip/src/core/memp.c" - "lwip/src/core/netif.c" - "lwip/src/core/pbuf.c" - "lwip/src/core/raw.c" - "lwip/src/core/stats.c" - "lwip/src/core/sys.c" - "lwip/src/core/tcp.c" - "lwip/src/core/tcp_in.c" - "lwip/src/core/tcp_out.c" - "lwip/src/core/timeouts.c" - "lwip/src/core/udp.c" - "lwip/src/core/ipv4/autoip.c" - "lwip/src/core/ipv4/dhcp.c" - "lwip/src/core/ipv4/etharp.c" - "lwip/src/core/ipv4/icmp.c" - "lwip/src/core/ipv4/igmp.c" - "lwip/src/core/ipv4/ip4.c" - "lwip/src/core/ipv4/ip4_addr.c" - "lwip/src/core/ipv4/ip4_frag.c" - "lwip/src/core/ipv6/dhcp6.c" - "lwip/src/core/ipv6/ethip6.c" - "lwip/src/core/ipv6/icmp6.c" - "lwip/src/core/ipv6/inet6.c" - "lwip/src/core/ipv6/ip6.c" - "lwip/src/core/ipv6/ip6_addr.c" - "lwip/src/core/ipv6/ip6_frag.c" - "lwip/src/core/ipv6/mld6.c" - "lwip/src/core/ipv6/nd6.c" - "lwip/src/netif/ethernet.c" - "lwip/src/netif/ethernetif.c" - "lwip/src/netif/lowpan6.c" - "lwip/src/netif/slipif.c" - "lwip/src/netif/ppp/auth.c" - "lwip/src/netif/ppp/ccp.c" - "lwip/src/netif/ppp/chap-md5.c" - "lwip/src/netif/ppp/chap-new.c" - "lwip/src/netif/ppp/chap_ms.c" - "lwip/src/netif/ppp/demand.c" - "lwip/src/netif/ppp/eap.c" - "lwip/src/netif/ppp/ecp.c" - "lwip/src/netif/ppp/eui64.c" - "lwip/src/netif/ppp/fsm.c" - "lwip/src/netif/ppp/ipcp.c" - "lwip/src/netif/ppp/ipv6cp.c" - "lwip/src/netif/ppp/lcp.c" - "lwip/src/netif/ppp/magic.c" - "lwip/src/netif/ppp/mppe.c" - "lwip/src/netif/ppp/multilink.c" - "lwip/src/netif/ppp/ppp.c" - "lwip/src/netif/ppp/pppapi.c" - "lwip/src/netif/ppp/pppcrypt.c" - "lwip/src/netif/ppp/pppoe.c" - "lwip/src/netif/ppp/pppol2tp.c" - "lwip/src/netif/ppp/pppos.c" - "lwip/src/netif/ppp/upap.c" - "lwip/src/netif/ppp/utils.c" - "lwip/src/netif/ppp/vj.c" - "port/esp32/vfs_lwip.c" - "port/esp32/debug/lwip_debug.c" - "port/esp32/freertos/sys_arch.c" - "port/esp32/netif/dhcp_state.c" - "port/esp32/netif/ethernetif.c" - "port/esp32/netif/nettestif.c" - "port/esp32/netif/wlanif.c") +set(srcs "apps/dhcpserver/dhcpserver.c" + "apps/ping/esp_ping.c" + "apps/ping/ping.c" + "apps/sntp/sntp.c" + "lwip/src/api/api_lib.c" + "lwip/src/api/api_msg.c" + "lwip/src/api/err.c" + "lwip/src/api/netbuf.c" + "lwip/src/api/netdb.c" + "lwip/src/api/netifapi.c" + "lwip/src/api/sockets.c" + "lwip/src/api/tcpip.c" + "lwip/src/apps/sntp/sntp.c" + "lwip/src/core/def.c" + "lwip/src/core/dns.c" + "lwip/src/core/inet_chksum.c" + "lwip/src/core/init.c" + "lwip/src/core/ip.c" + "lwip/src/core/mem.c" + "lwip/src/core/memp.c" + "lwip/src/core/netif.c" + "lwip/src/core/pbuf.c" + "lwip/src/core/raw.c" + "lwip/src/core/stats.c" + "lwip/src/core/sys.c" + "lwip/src/core/tcp.c" + "lwip/src/core/tcp_in.c" + "lwip/src/core/tcp_out.c" + "lwip/src/core/timeouts.c" + "lwip/src/core/udp.c" + "lwip/src/core/ipv4/autoip.c" + "lwip/src/core/ipv4/dhcp.c" + "lwip/src/core/ipv4/etharp.c" + "lwip/src/core/ipv4/icmp.c" + "lwip/src/core/ipv4/igmp.c" + "lwip/src/core/ipv4/ip4.c" + "lwip/src/core/ipv4/ip4_addr.c" + "lwip/src/core/ipv4/ip4_frag.c" + "lwip/src/core/ipv6/dhcp6.c" + "lwip/src/core/ipv6/ethip6.c" + "lwip/src/core/ipv6/icmp6.c" + "lwip/src/core/ipv6/inet6.c" + "lwip/src/core/ipv6/ip6.c" + "lwip/src/core/ipv6/ip6_addr.c" + "lwip/src/core/ipv6/ip6_frag.c" + "lwip/src/core/ipv6/mld6.c" + "lwip/src/core/ipv6/nd6.c" + "lwip/src/netif/ethernet.c" + "lwip/src/netif/ethernetif.c" + "lwip/src/netif/lowpan6.c" + "lwip/src/netif/slipif.c" + "lwip/src/netif/ppp/auth.c" + "lwip/src/netif/ppp/ccp.c" + "lwip/src/netif/ppp/chap-md5.c" + "lwip/src/netif/ppp/chap-new.c" + "lwip/src/netif/ppp/chap_ms.c" + "lwip/src/netif/ppp/demand.c" + "lwip/src/netif/ppp/eap.c" + "lwip/src/netif/ppp/ecp.c" + "lwip/src/netif/ppp/eui64.c" + "lwip/src/netif/ppp/fsm.c" + "lwip/src/netif/ppp/ipcp.c" + "lwip/src/netif/ppp/ipv6cp.c" + "lwip/src/netif/ppp/lcp.c" + "lwip/src/netif/ppp/magic.c" + "lwip/src/netif/ppp/mppe.c" + "lwip/src/netif/ppp/multilink.c" + "lwip/src/netif/ppp/ppp.c" + "lwip/src/netif/ppp/pppapi.c" + "lwip/src/netif/ppp/pppcrypt.c" + "lwip/src/netif/ppp/pppoe.c" + "lwip/src/netif/ppp/pppol2tp.c" + "lwip/src/netif/ppp/pppos.c" + "lwip/src/netif/ppp/upap.c" + "lwip/src/netif/ppp/utils.c" + "lwip/src/netif/ppp/vj.c" + "port/esp32/vfs_lwip.c" + "port/esp32/debug/lwip_debug.c" + "port/esp32/freertos/sys_arch.c" + "port/esp32/netif/dhcp_state.c" + "port/esp32/netif/ethernetif.c" + "port/esp32/netif/nettestif.c" + "port/esp32/netif/wlanif.c") if(CONFIG_LWIP_PPP_SUPPORT) - list(APPEND COMPONENT_SRCS "lwip/src/netif/ppp/auth.c" + list(APPEND srcs "lwip/src/netif/ppp/auth.c" "lwip/src/netif/ppp/ccp.c" "lwip/src/netif/ppp/chap-md5.c" "lwip/src/netif/ppp/chap-new.c" @@ -123,12 +123,11 @@ if(CONFIG_LWIP_PPP_SUPPORT) "lwip/src/netif/ppp/polarssl/sha1.c") endif() -set(COMPONENT_REQUIRES vfs esp_wifi) -set(COMPONENT_PRIV_REQUIRES ethernet tcpip_adapter nvs_flash) - -set(COMPONENT_ADD_LDFRAGMENTS linker.lf) - -register_component() +idf_component_register(SRCS "${srcs}" + INCLUDE_DIRS "${include_dirs}" + LDFRAGMENTS linker.lf + REQUIRES vfs esp_wifi + PRIV_REQUIRES ethernet tcpip_adapter nvs_flash) # lots of LWIP source files evaluate macros that check address of stack variables target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-address) diff --git a/components/mbedtls/CMakeLists.txt b/components/mbedtls/CMakeLists.txt index eaa420ef2e..91ef70234a 100644 --- a/components/mbedtls/CMakeLists.txt +++ b/components/mbedtls/CMakeLists.txt @@ -1,8 +1,6 @@ -set(COMPONENT_ADD_INCLUDEDIRS "port/include" "mbedtls/include") -set(COMPONENT_SRCS "mbedtls.c") -set(COMPONENT_REQUIRES lwip) - -register_component() +idf_component_register(SRCS "mbedtls.c" + INCLUDE_DIRS "port/include" "mbedtls/include" + REQUIRES lwip) # Only build mbedtls libraries set(ENABLE_TESTING CACHE BOOL OFF) diff --git a/components/mbedtls/test/CMakeLists.txt b/components/mbedtls/test/CMakeLists.txt index 5a8cfd8d33..8a083482a6 100644 --- a/components/mbedtls/test/CMakeLists.txt +++ b/components/mbedtls/test/CMakeLists.txt @@ -1,9 +1,6 @@ -set(COMPONENT_SRCDIRS ".") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -set(COMPONENT_REQUIRES unity test_utils mbedtls) - -register_component() +idf_component_register(SRC_DIRS "." + INCLUDE_DIRS "." + REQUIRES unity test_utils mbedtls) idf_component_get_property(mbedtls mbedtls COMPONENT_LIB) target_compile_definitions(${mbedtls} PUBLIC "-DMBEDTLS_DEPRECATED_WARNING") diff --git a/components/mdns/CMakeLists.txt b/components/mdns/CMakeLists.txt index 0933e30059..7b744dde56 100644 --- a/components/mdns/CMakeLists.txt +++ b/components/mdns/CMakeLists.txt @@ -1,9 +1,7 @@ -set(COMPONENT_SRCS "mdns.c" - "mdns_console.c" - "mdns_networking.c") -set(COMPONENT_ADD_INCLUDEDIRS "include") -set(COMPONENT_PRIV_INCLUDEDIRS "private_include") -set(COMPONENT_REQUIRES lwip mbedtls console tcpip_adapter) - -register_component() +idf_component_register(SRCS "mdns.c" + "mdns_console.c" + "mdns_networking.c" + INCLUDE_DIRS "include" + PRIV_INCLUDE_DIRS "private_include" + REQUIRES lwip mbedtls console tcpip_adapter) diff --git a/components/mdns/test/CMakeLists.txt b/components/mdns/test/CMakeLists.txt index 7e694470f6..516ee0097b 100644 --- a/components/mdns/test/CMakeLists.txt +++ b/components/mdns/test/CMakeLists.txt @@ -1,3 +1,2 @@ -set(COMPONENT_SRCDIRS ".") -set(COMPONENT_PRIV_REQUIRES unity test_utils mdns) -register_component() \ No newline at end of file +idf_component_register(SRC_DIRS "." + PRIV_REQUIRES unity test_utils mdns) \ No newline at end of file diff --git a/components/mqtt/CMakeLists.txt b/components/mqtt/CMakeLists.txt index 85f63cc9a8..1c30e3a913 100644 --- a/components/mqtt/CMakeLists.txt +++ b/components/mqtt/CMakeLists.txt @@ -1,10 +1,7 @@ -set(COMPONENT_ADD_INCLUDEDIRS esp-mqtt/include) -set(COMPONENT_PRIV_INCLUDEDIRS "esp-mqtt/lib/include") -set(COMPONENT_SRCS "esp-mqtt/mqtt_client.c" - "esp-mqtt/lib/mqtt_msg.c" - "esp-mqtt/lib/mqtt_outbox.c" - "esp-mqtt/lib/platform_esp32_idf.c") - -set(COMPONENT_REQUIRES lwip nghttp mbedtls tcp_transport) - -register_component() +idf_component_register(SRCS "esp-mqtt/mqtt_client.c" + "esp-mqtt/lib/mqtt_msg.c" + "esp-mqtt/lib/mqtt_outbox.c" + "esp-mqtt/lib/platform_esp32_idf.c" + INCLUDE_DIRS esp-mqtt/include + PRIV_INCLUDE_DIRS "esp-mqtt/lib/include" + REQUIRES lwip nghttp mbedtls tcp_transport) diff --git a/components/newlib/CMakeLists.txt b/components/newlib/CMakeLists.txt index 920842a73e..97a6d773b7 100644 --- a/components/newlib/CMakeLists.txt +++ b/components/newlib/CMakeLists.txt @@ -1,20 +1,20 @@ -set(COMPONENT_SRCS "heap.c" - "locks.c" - "poll.c" - "pthread.c" - "random.c" - "reent_init.c" - "select.c" - "syscall_table.c" - "syscalls.c" - "termios.c" - "time.c" - "utime.c") -set(COMPONENT_ADD_INCLUDEDIRS platform_include) +set(srcs "heap.c" + "locks.c" + "poll.c" + "pthread.c" + "random.c" + "reent_init.c" + "select.c" + "syscall_table.c" + "syscalls.c" + "termios.c" + "time.c" + "utime.c") +set(include_dirs platform_include) if(GCC_NOT_5_2_0) if(CONFIG_SPIRAM_CACHE_WORKAROUND) - set(COMPONENT_ADD_LDFRAGMENTS esp32-spiram-rom-functions-c.lf) + set(ldfragments esp32-spiram-rom-functions-c.lf) endif() # Forces the linker to include locks, heap, and syscalls from this component, @@ -27,20 +27,21 @@ else() # Remove this section when GCC 5.2.0 is no longer supported # 'include' and 'lib' directories should also be removed. # An if statement about LIB_PATH below should also be removed. - list(APPEND COMPONENT_ADD_INCLUDEDIRS include) + list(APPEND include_dirs include) set(LIB_PATH ${CMAKE_CURRENT_SOURCE_DIR}/lib) if(CONFIG_SPIRAM_CACHE_WORKAROUND) - set(COMPONENT_ADD_LDFRAGMENTS esp32-spiram-rom-functions-psram-workaround.lf) + set(ldfragments esp32-spiram-rom-functions-psram-workaround.lf) endif() endif() -set(COMPONENT_REQUIRES vfs) # for sys/ioctl.h -set(COMPONENT_PRIV_REQUIRES soc) +list(APPEND ldfragments newlib.lf) -list(APPEND COMPONENT_ADD_LDFRAGMENTS newlib.lf) - -register_component() +idf_component_register(SRCS "${srcs}" + INCLUDE_DIRS "${include_dirs}" + REQUIRES vfs + PRIV_REQUIRES soc + LDFRAGMENTS "${ldfragments}") if(LIB_PATH) target_link_libraries(${COMPONENT_LIB} INTERFACE "-L ${LIB_PATH}") diff --git a/components/newlib/test/CMakeLists.txt b/components/newlib/test/CMakeLists.txt index 66a8c82315..b531a14504 100644 --- a/components/newlib/test/CMakeLists.txt +++ b/components/newlib/test/CMakeLists.txt @@ -1,6 +1,3 @@ -set(COMPONENT_SRCDIRS ".") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -set(COMPONENT_REQUIRES unity test_utils) - -register_component() \ No newline at end of file +idf_component_register(SRC_DIRS "." + INCLUDE_DIRS "." + REQUIRES unity test_utils) \ No newline at end of file diff --git a/components/nghttp/CMakeLists.txt b/components/nghttp/CMakeLists.txt index c1c4d80942..32bd379eda 100644 --- a/components/nghttp/CMakeLists.txt +++ b/components/nghttp/CMakeLists.txt @@ -1,28 +1,28 @@ -set(COMPONENT_ADD_INCLUDEDIRS port/include nghttp2/lib/includes) -set(COMPONENT_SRCS "nghttp2/lib/nghttp2_buf.c" - "nghttp2/lib/nghttp2_callbacks.c" - "nghttp2/lib/nghttp2_debug.c" - "nghttp2/lib/nghttp2_frame.c" - "nghttp2/lib/nghttp2_hd.c" - "nghttp2/lib/nghttp2_hd_huffman.c" - "nghttp2/lib/nghttp2_hd_huffman_data.c" - "nghttp2/lib/nghttp2_helper.c" - "nghttp2/lib/nghttp2_http.c" - "nghttp2/lib/nghttp2_map.c" - "nghttp2/lib/nghttp2_mem.c" - "nghttp2/lib/nghttp2_npn.c" - "nghttp2/lib/nghttp2_option.c" - "nghttp2/lib/nghttp2_outbound_item.c" - "nghttp2/lib/nghttp2_pq.c" - "nghttp2/lib/nghttp2_priority_spec.c" - "nghttp2/lib/nghttp2_queue.c" - "nghttp2/lib/nghttp2_rcbuf.c" - "nghttp2/lib/nghttp2_session.c" - "nghttp2/lib/nghttp2_stream.c" - "nghttp2/lib/nghttp2_submit.c" - "nghttp2/lib/nghttp2_version.c" - "port/http_parser.c") +set(srcs "nghttp2/lib/nghttp2_buf.c" + "nghttp2/lib/nghttp2_callbacks.c" + "nghttp2/lib/nghttp2_debug.c" + "nghttp2/lib/nghttp2_frame.c" + "nghttp2/lib/nghttp2_hd.c" + "nghttp2/lib/nghttp2_hd_huffman.c" + "nghttp2/lib/nghttp2_hd_huffman_data.c" + "nghttp2/lib/nghttp2_helper.c" + "nghttp2/lib/nghttp2_http.c" + "nghttp2/lib/nghttp2_map.c" + "nghttp2/lib/nghttp2_mem.c" + "nghttp2/lib/nghttp2_npn.c" + "nghttp2/lib/nghttp2_option.c" + "nghttp2/lib/nghttp2_outbound_item.c" + "nghttp2/lib/nghttp2_pq.c" + "nghttp2/lib/nghttp2_priority_spec.c" + "nghttp2/lib/nghttp2_queue.c" + "nghttp2/lib/nghttp2_rcbuf.c" + "nghttp2/lib/nghttp2_session.c" + "nghttp2/lib/nghttp2_stream.c" + "nghttp2/lib/nghttp2_submit.c" + "nghttp2/lib/nghttp2_version.c" + "port/http_parser.c") -register_component() +idf_component_register(SRCS "${srcs}" + INCLUDE_DIRS port/include nghttp2/lib/includes) target_compile_definitions(${COMPONENT_LIB} PUBLIC "-DHAVE_CONFIG_H") diff --git a/components/nvs_flash/CMakeLists.txt b/components/nvs_flash/CMakeLists.txt index 2c815b31ca..1d7e5862dd 100644 --- a/components/nvs_flash/CMakeLists.txt +++ b/components/nvs_flash/CMakeLists.txt @@ -1,13 +1,10 @@ -set(COMPONENT_SRCS "src/nvs_api.cpp" - "src/nvs_encr.cpp" - "src/nvs_item_hash_list.cpp" - "src/nvs_ops.cpp" - "src/nvs_page.cpp" - "src/nvs_pagemanager.cpp" - "src/nvs_storage.cpp" - "src/nvs_types.cpp") -set(COMPONENT_ADD_INCLUDEDIRS include) - -set(COMPONENT_REQUIRES spi_flash mbedtls) - -register_component() +idf_component_register(SRCS "src/nvs_api.cpp" + "src/nvs_encr.cpp" + "src/nvs_item_hash_list.cpp" + "src/nvs_ops.cpp" + "src/nvs_page.cpp" + "src/nvs_pagemanager.cpp" + "src/nvs_storage.cpp" + "src/nvs_types.cpp" + REQUIRES spi_flash mbedtls + INCLUDE_DIRS include) diff --git a/components/nvs_flash/test/CMakeLists.txt b/components/nvs_flash/test/CMakeLists.txt index f473de05af..ede2a57bae 100644 --- a/components/nvs_flash/test/CMakeLists.txt +++ b/components/nvs_flash/test/CMakeLists.txt @@ -1,6 +1,3 @@ -set(COMPONENT_SRCDIRS ".") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -set(COMPONENT_REQUIRES unity test_utils nvs_flash bootloader_support) - -register_component() +idf_component_register(SRCS "." + INCLUDE_DIRS "." + REQUIRES unity test_utils nvs_flash bootloader_support) \ No newline at end of file diff --git a/components/openssl/CMakeLists.txt b/components/openssl/CMakeLists.txt index 3a96598f01..bba4006f1a 100644 --- a/components/openssl/CMakeLists.txt +++ b/components/openssl/CMakeLists.txt @@ -1,14 +1,11 @@ -set(COMPONENT_ADD_INCLUDEDIRS include) -set(COMPONENT_PRIV_INCLUDEDIRS include/internal include/platform include/openssl) -set(COMPONENT_SRCS "library/ssl_cert.c" - "library/ssl_lib.c" - "library/ssl_methods.c" - "library/ssl_pkey.c" - "library/ssl_stack.c" - "library/ssl_x509.c" - "platform/ssl_pm.c" - "platform/ssl_port.c") - -set(COMPONENT_REQUIRES mbedtls) - -register_component() +idf_component_register(SRCS "library/ssl_cert.c" + "library/ssl_lib.c" + "library/ssl_methods.c" + "library/ssl_pkey.c" + "library/ssl_stack.c" + "library/ssl_x509.c" + "platform/ssl_pm.c" + "platform/ssl_port.c" + REQUIRES mbedtls + INCLUDE_DIRS include + PRIV_INCLUDE_DIRS include/internal include/platform include/openssl) diff --git a/components/partition_table/CMakeLists.txt b/components/partition_table/CMakeLists.txt index d94e3e162f..4fe76cc93e 100644 --- a/components/partition_table/CMakeLists.txt +++ b/components/partition_table/CMakeLists.txt @@ -1,4 +1,4 @@ -register_config_only_component() +idf_component_register() if(BOOTLOADER_BUILD) return() diff --git a/components/partition_table/test/CMakeLists.txt b/components/partition_table/test/CMakeLists.txt index 66a8c82315..b531a14504 100644 --- a/components/partition_table/test/CMakeLists.txt +++ b/components/partition_table/test/CMakeLists.txt @@ -1,6 +1,3 @@ -set(COMPONENT_SRCDIRS ".") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -set(COMPONENT_REQUIRES unity test_utils) - -register_component() \ No newline at end of file +idf_component_register(SRC_DIRS "." + INCLUDE_DIRS "." + REQUIRES unity test_utils) \ No newline at end of file diff --git a/components/protobuf-c/CMakeLists.txt b/components/protobuf-c/CMakeLists.txt index 1c9a3ffc3f..1801093194 100644 --- a/components/protobuf-c/CMakeLists.txt +++ b/components/protobuf-c/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_ADD_INCLUDEDIRS protobuf-c) -set(COMPONENT_SRCS "protobuf-c/protobuf-c/protobuf-c.c") - -register_component() +idf_component_register(SRCS "protobuf-c/protobuf-c/protobuf-c.c" + INCLUDE_DIRS protobuf-c) diff --git a/components/protocomm/CMakeLists.txt b/components/protocomm/CMakeLists.txt index 9f9343815f..22bb08254c 100644 --- a/components/protocomm/CMakeLists.txt +++ b/components/protocomm/CMakeLists.txt @@ -1,25 +1,26 @@ -set(COMPONENT_ADD_INCLUDEDIRS include/common - include/security - include/transports) -set(COMPONENT_PRIV_INCLUDEDIRS proto-c src/common src/simple_ble) -set(COMPONENT_SRCS "src/common/protocomm.c" - "src/security/security0.c" - "src/security/security1.c" - "proto-c/constants.pb-c.c" - "proto-c/sec0.pb-c.c" - "proto-c/sec1.pb-c.c" - "proto-c/session.pb-c.c" - "src/transports/protocomm_console.c" - "src/transports/protocomm_httpd.c") - -set(COMPONENT_PRIV_REQUIRES protobuf-c mbedtls console esp_http_server bt) +set(include_dirs include/common + include/security + include/transports) +set(priv_include_dirs proto-c src/common src/simple_ble) +set(srcs "src/common/protocomm.c" + "src/security/security0.c" + "src/security/security1.c" + "proto-c/constants.pb-c.c" + "proto-c/sec0.pb-c.c" + "proto-c/sec1.pb-c.c" + "proto-c/session.pb-c.c" + "src/transports/protocomm_console.c" + "src/transports/protocomm_httpd.c") if(CONFIG_BT_ENABLED) if(CONFIG_BT_BLUEDROID_ENABLED) - list(APPEND COMPONENT_SRCS + list(APPEND srcs "src/simple_ble/simple_ble.c" "src/transports/protocomm_ble.c") endif() endif() -register_component() +idf_component_register(SRCS "${srcs}" + INCLUDE_DIRS "${include_dirs}" + PRIV_INCLUDE_DIRS "${priv_include_dirs}" + PRIV_REQUIRES protobuf-c mbedtls console esp_http_server bt) diff --git a/components/protocomm/test/CMakeLists.txt b/components/protocomm/test/CMakeLists.txt index f25cb01a18..6e04b5332e 100644 --- a/components/protocomm/test/CMakeLists.txt +++ b/components/protocomm/test/CMakeLists.txt @@ -1,7 +1,4 @@ -set(COMPONENT_SRCDIRS ".") -set(COMPONENT_ADD_INCLUDEDIRS ".") -set(COMPONENT_PRIV_INCLUDEDIRS "../proto-c/") - -set(COMPONENT_REQUIRES unity mbedtls protocomm protobuf-c) - -register_component() +idf_component_register(SRC_DIRS "." + INCLUDE_DIRS "." + PRIV_INCLUDE_DIRS "../proto-c/" + REQUIRES unity mbedtls protocomm protobuf-c) \ No newline at end of file diff --git a/components/pthread/CMakeLists.txt b/components/pthread/CMakeLists.txt index 5c8bc04a04..d488c92a9d 100644 --- a/components/pthread/CMakeLists.txt +++ b/components/pthread/CMakeLists.txt @@ -1,8 +1,7 @@ -set(COMPONENT_SRCS "pthread.c" - "pthread_cond_var.c" - "pthread_local_storage.c") -set(COMPONENT_ADD_INCLUDEDIRS "include") -register_component() +idf_component_register(SRCS "pthread.c" + "pthread_cond_var.c" + "pthread_local_storage.c" + INCLUDE_DIRS include) if(CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP) target_link_libraries(${COMPONENT_LIB} "-Wl,--wrap=vPortCleanUpTCB") diff --git a/components/pthread/test/CMakeLists.txt b/components/pthread/test/CMakeLists.txt index f57f843b1b..bb02dc7d41 100644 --- a/components/pthread/test/CMakeLists.txt +++ b/components/pthread/test/CMakeLists.txt @@ -1,6 +1,3 @@ -set(COMPONENT_SRCDIRS ".") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -set(COMPONENT_REQUIRES unity test_utils pthread) - -register_component() \ No newline at end of file +idf_component_register(SRC_DIRS "." + INCLUDE_DIRS "." + REQUIRES unity test_utils pthread) \ No newline at end of file diff --git a/components/sdmmc/CMakeLists.txt b/components/sdmmc/CMakeLists.txt index fdba0cf790..cb7de0e805 100644 --- a/components/sdmmc/CMakeLists.txt +++ b/components/sdmmc/CMakeLists.txt @@ -1,10 +1,9 @@ -set(COMPONENT_SRCS "sdmmc_cmd.c" - "sdmmc_common.c" - "sdmmc_init.c" - "sdmmc_io.c" - "sdmmc_mmc.c" - "sdmmc_sd.c") -set(COMPONENT_ADD_INCLUDEDIRS "include") -set(COMPONENT_REQUIRES driver) -set(COMPONENT_PRIV_REQUIRES soc) -register_component() +idf_component_register(SRCS "sdmmc_cmd.c" + "sdmmc_common.c" + "sdmmc_init.c" + "sdmmc_io.c" + "sdmmc_mmc.c" + "sdmmc_sd.c" + INCLUDE_DIRS include + REQUIRES driver + PRIV_REQUIRES soc) \ No newline at end of file diff --git a/components/sdmmc/test/CMakeLists.txt b/components/sdmmc/test/CMakeLists.txt index 885cda77a6..c7c2d52a9b 100644 --- a/components/sdmmc/test/CMakeLists.txt +++ b/components/sdmmc/test/CMakeLists.txt @@ -1,6 +1,3 @@ -set(COMPONENT_SRCDIRS ".") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -set(COMPONENT_REQUIRES unity sdmmc) - -register_component() \ No newline at end of file +idf_component_register(SRC_DIRS "." + INCLUDE_DIRS "." + REQUIRES unity sdmmc) \ No newline at end of file diff --git a/components/smartconfig_ack/CMakeLists.txt b/components/smartconfig_ack/CMakeLists.txt index 1518776ce9..eb34b0713c 100644 --- a/components/smartconfig_ack/CMakeLists.txt +++ b/components/smartconfig_ack/CMakeLists.txt @@ -1,6 +1,3 @@ -set(COMPONENT_SRCS "smartconfig_ack.c") -set(COMPONENT_ADD_INCLUDEDIRS "include") - -set(COMPONENT_PRIV_REQUIRES lwip tcpip_adapter) - -register_component() +idf_component_register(SRCS "smartconfig_ack.c" + INCLUDE_DIRS include + PRIV_REQUIRES lwip tcpip_adapter) \ No newline at end of file diff --git a/components/soc/CMakeLists.txt b/components/soc/CMakeLists.txt index 572fb38f68..0a2b29b00e 100644 --- a/components/soc/CMakeLists.txt +++ b/components/soc/CMakeLists.txt @@ -4,12 +4,12 @@ if(EXISTS "${COMPONENT_DIR}/${soc_name}") include(${COMPONENT_DIR}/${soc_name}/sources.cmake) spaces2list(SOC_SRCS) - add_prefix(COMPONENT_SRCS "${soc_name}/" ${SOC_SRCS}) - set(COMPONENT_ADD_INCLUDEDIRS ${soc_name}/include) + add_prefix(srcs "${soc_name}/" ${SOC_SRCS}) + set(include_dirs ${soc_name}/include) endif() -list(APPEND COMPONENT_ADD_INCLUDEDIRS include) -list(APPEND COMPONENT_SRCS "src/memory_layout_utils.c" +list(APPEND include_dirs include) +list(APPEND srcs "src/memory_layout_utils.c" "src/lldesc.c" "src/hal/spi_hal.c" "src/hal/spi_hal_iram.c" @@ -20,8 +20,6 @@ list(APPEND COMPONENT_SRCS "src/memory_layout_utils.c" "src/hal/spi_flash_hal_iram.c" ) -set(COMPONENT_ADD_LDFRAGMENTS linker.lf) - -set(COMPONENT_REQUIRES) - -register_component() +idf_component_register(SRCS "${srcs}" + INCLUDE_DIRS "${include_dirs}" + LDFRAGMENTS linker.lf) diff --git a/components/soc/test/CMakeLists.txt b/components/soc/test/CMakeLists.txt index 276f56651a..619190b156 100644 --- a/components/soc/test/CMakeLists.txt +++ b/components/soc/test/CMakeLists.txt @@ -1,10 +1,11 @@ idf_build_get_property(soc_name IDF_TARGET) + get_filename_component(soc_test "${CMAKE_CURRENT_SOURCE_DIR}/../${soc_name}/test" ABSOLUTE) if(EXISTS "${soc_test}") - set(COMPONENT_SRCS "${soc_test}") - set(COMPONENT_ADD_INCLUDEDIRS "${soc_test}") + set(srcs "${soc_test}") + set(include_dirs "${soc_test}") endif() -set(COMPONENT_REQUIRES unity test_utils) - -register_component() +idf_component_register(SRC_DIRS "${src_dirs}" + INCLUDE_DIRS "${include_dirs}" + REQUIRES unity test_utils) diff --git a/components/spi_flash/CMakeLists.txt b/components/spi_flash/CMakeLists.txt index eb17b31eef..4a0dd4ecc4 100644 --- a/components/spi_flash/CMakeLists.txt +++ b/components/spi_flash/CMakeLists.txt @@ -1,10 +1,10 @@ +set(priv_requires bootloader_support soc) if(BOOTLOADER_BUILD) # Bootloader needs SPIUnlock from this file, but doesn't # need other parts of this component - set(COMPONENT_SRCS "spi_flash_rom_patch.c") - set(COMPONENT_PRIV_REQUIRES bootloader_support soc) + set(srcs "spi_flash_rom_patch.c") else() - set(COMPONENT_SRCS "cache_utils.c" + set(srcs "cache_utils.c" "flash_mmap.c" "flash_ops.c" "partition.c" @@ -17,13 +17,13 @@ else() "memspi_host_driver.c" ) if(NOT CONFIG_SPI_FLASH_USE_LEGACY_IMPL) - list(APPEND COMPONENT_SRCS "esp_flash_api.c") + list(APPEND srcs "esp_flash_api.c") endif() - set(COMPONENT_PRIV_REQUIRES bootloader_support app_update soc) + set(priv_requires bootloader_support app_update soc) endif() -set(COMPONENT_ADD_INCLUDEDIRS include) -set(COMPONENT_PRIV_INCLUDEDIRS private_include) -set(COMPONENT_ADD_LDFRAGMENTS linker.lf) - -register_component() +idf_component_register(SRCS "${srcs}" + PRIV_REQUIRES "${priv_requires}" + INCLUDE_DIRS include + PRIV_INCLUDE_DIRS private_include + LDFRAGMENTS linker.lf) diff --git a/components/spi_flash/test/CMakeLists.txt b/components/spi_flash/test/CMakeLists.txt index 559473b821..ae23154f2f 100644 --- a/components/spi_flash/test/CMakeLists.txt +++ b/components/spi_flash/test/CMakeLists.txt @@ -1,6 +1,3 @@ -set(COMPONENT_SRCDIRS ".") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -set(COMPONENT_REQUIRES unity test_utils spi_flash bootloader_support app_update) - -register_component() \ No newline at end of file +idf_component_register(SRC_DIRS "." + INCLUDE_DIRS "." + REQUIRES unity test_utils spi_flash bootloader_support app_update) \ No newline at end of file diff --git a/components/spiffs/CMakeLists.txt b/components/spiffs/CMakeLists.txt index 17e5a5587b..dfe4643c0f 100644 --- a/components/spiffs/CMakeLists.txt +++ b/components/spiffs/CMakeLists.txt @@ -1,15 +1,12 @@ -set(COMPONENT_ADD_INCLUDEDIRS "include") -set(COMPONENT_PRIV_INCLUDEDIRS "." "spiffs/src") -set(COMPONENT_SRCS "esp_spiffs.c" - "spiffs_api.c" - "spiffs/src/spiffs_cache.c" - "spiffs/src/spiffs_check.c" - "spiffs/src/spiffs_gc.c" - "spiffs/src/spiffs_hydrogen.c" - "spiffs/src/spiffs_nucleus.c") - -set(COMPONENT_REQUIRES spi_flash) -set(COMPONENT_PRIV_REQUIRES bootloader_support) - -register_component() +idf_component_register(SRCS "esp_spiffs.c" + "spiffs_api.c" + "spiffs/src/spiffs_cache.c" + "spiffs/src/spiffs_check.c" + "spiffs/src/spiffs_gc.c" + "spiffs/src/spiffs_hydrogen.c" + "spiffs/src/spiffs_nucleus.c" + INCLUDE_DIRS "include" + PRIV_INCLUDE_DIRS "." "spiffs/src" + REQUIRES spi_flash + PRIV_REQUIRES bootloader_support) diff --git a/components/spiffs/test/CMakeLists.txt b/components/spiffs/test/CMakeLists.txt index dc004f479b..20bfd7a7d4 100644 --- a/components/spiffs/test/CMakeLists.txt +++ b/components/spiffs/test/CMakeLists.txt @@ -1,6 +1,3 @@ -set(COMPONENT_SRCDIRS ".") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -set(COMPONENT_REQUIRES unity test_utils spiffs) - -register_component() \ No newline at end of file +idf_component_register(SRC_DIRS "." + INCLUDE_DIRS "." + REQUIRES unity test_utils spiffs) \ No newline at end of file diff --git a/components/tcp_transport/CMakeLists.txt b/components/tcp_transport/CMakeLists.txt index 9c2ef846b8..f1821db60f 100644 --- a/components/tcp_transport/CMakeLists.txt +++ b/components/tcp_transport/CMakeLists.txt @@ -1,12 +1,8 @@ -set(COMPONENT_SRCS "transport.c" - "transport_ssl.c" - "transport_tcp.c" - "transport_ws.c" - "transport_utils.c" - "transport_strcasestr.c") - -set(COMPONENT_ADD_INCLUDEDIRS "include") - -set(COMPONENT_REQUIRES lwip esp-tls) - -register_component() +idf_component_register(SRCS "transport.c" + "transport_ssl.c" + "transport_tcp.c" + "transport_ws.c" + "transport_utils.c" + "transport_strcasestr.c" + INCLUDE_DIRS include + REQUIRES lwip esp-tls) \ No newline at end of file diff --git a/components/tcpip_adapter/CMakeLists.txt b/components/tcpip_adapter/CMakeLists.txt index b728811ef2..8d35ca61df 100644 --- a/components/tcpip_adapter/CMakeLists.txt +++ b/components/tcpip_adapter/CMakeLists.txt @@ -1,7 +1,3 @@ -set(COMPONENT_SRCS "event_handlers.c" - "tcpip_adapter_lwip.c") -set(COMPONENT_ADD_INCLUDEDIRS "include") - -set(COMPONENT_REQUIRES lwip) - -register_component() +idf_component_register(SRCS "event_handlers.c" "tcpip_adapter_lwip.c" + INCLUDE_DIRS include + REQUIRES lwip ethernet) diff --git a/components/ulp/CMakeLists.txt b/components/ulp/CMakeLists.txt index ca8d115c39..6d47331208 100644 --- a/components/ulp/CMakeLists.txt +++ b/components/ulp/CMakeLists.txt @@ -1,4 +1,3 @@ -set(COMPONENT_SRCS "ulp.c" - "ulp_macro.c") -set(COMPONENT_ADD_INCLUDEDIRS "include") -register_component() +idf_component_register(SRCS "ulp.c" + "ulp_macro.c" + INCLUDE_DIRS include) \ No newline at end of file diff --git a/components/ulp/test/CMakeLists.txt b/components/ulp/test/CMakeLists.txt index 296fda0148..27311f9530 100644 --- a/components/ulp/test/CMakeLists.txt +++ b/components/ulp/test/CMakeLists.txt @@ -1,9 +1,6 @@ -set(COMPONENT_SRCDIRS ".") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -set(COMPONENT_REQUIRES unity ulp soc esp_common) - -register_component() +idf_component_register(SRC_DIRS "." + INCLUDE_DIRS "." + REQUIRES unity ulp soc esp_common) set(ulp_app_name ulp_test_app) set(ulp_s_sources "ulp/test_jumps.S") diff --git a/components/unity/CMakeLists.txt b/components/unity/CMakeLists.txt index 53494ae0f0..a82d037c0c 100644 --- a/components/unity/CMakeLists.txt +++ b/components/unity/CMakeLists.txt @@ -1,22 +1,20 @@ -set(COMPONENT_SRCS "unity/src/unity.c" - "unity_port_esp32.c") - -set(COMPONENT_ADD_INCLUDEDIRS "include" - "unity/src") +set(srcs "unity/src/unity.c" + "unity_port_esp32.c") if(CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL) list(APPEND COMPONENT_PRIV_INCLUDEDIRS "include/priv") endif() if(CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER) - list(APPEND COMPONENT_SRCS "unity_runner.c") + list(APPEND srcs "unity_runner.c") endif() if(CONFIG_UNITY_ENABLE_FIXTURE) - list(APPEND COMPONENT_SRCS "unity/extras/fixture/src/unity_fixture.c") + list(APPEND srcs "unity/extras/fixture/src/unity_fixture.c") endif() -register_component() +idf_component_register(SRCS "${srcs}" + INCLUDE_DIRS "include" "unity/src") target_compile_definitions(${COMPONENT_LIB} PUBLIC -DUNITY_INCLUDE_CONFIG_H diff --git a/components/vfs/CMakeLists.txt b/components/vfs/CMakeLists.txt index 0b2f640ca6..d62c9e0393 100644 --- a/components/vfs/CMakeLists.txt +++ b/components/vfs/CMakeLists.txt @@ -1,8 +1,7 @@ -set(COMPONENT_SRCS "vfs.c" - "vfs_uart.c" - "vfs_semihost.c") -set(COMPONENT_ADD_INCLUDEDIRS "include") -register_component() +idf_component_register(SRCS "vfs.c" + "vfs_uart.c" + "vfs_semihost.c" + INCLUDE_DIRS include) # Some newlib syscalls are implemented in vfs.c, make sure these are always # seen by the linker diff --git a/components/vfs/test/CMakeLists.txt b/components/vfs/test/CMakeLists.txt index e67c253d0f..f0a24edd9f 100644 --- a/components/vfs/test/CMakeLists.txt +++ b/components/vfs/test/CMakeLists.txt @@ -1,8 +1,4 @@ -set(COMPONENT_SRCDIRS ".") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -set(COMPONENT_REQUIRES unity test_utils vfs fatfs spiffs) - -set(COMPONENT_ADD_LDFRAGMENTS linker.lf) - -register_component() +idf_component_register(SRC_DIRS "." + INCLUDE_DIRS "." + REQUIRES unity test_utils vfs fatfs spiffs + LDFRAGMENTS linker.lf) \ No newline at end of file diff --git a/components/wear_levelling/CMakeLists.txt b/components/wear_levelling/CMakeLists.txt index 59b81ccfaa..1a0d775975 100644 --- a/components/wear_levelling/CMakeLists.txt +++ b/components/wear_levelling/CMakeLists.txt @@ -1,11 +1,10 @@ -set(COMPONENT_SRCS "Partition.cpp" - "SPI_Flash.cpp" - "WL_Ext_Perf.cpp" - "WL_Ext_Safe.cpp" - "WL_Flash.cpp" - "crc32.cpp" - "wear_levelling.cpp") -set(COMPONENT_ADD_INCLUDEDIRS "include") -set(COMPONENT_PRIV_INCLUDEDIRS private_include) -set(COMPONENT_REQUIRES spi_flash) -register_component() +idf_component_register(SRCS "Partition.cpp" + "SPI_Flash.cpp" + "WL_Ext_Perf.cpp" + "WL_Ext_Safe.cpp" + "WL_Flash.cpp" + "crc32.cpp" + "wear_levelling.cpp" + INCLUDE_DIRS include + PRIV_INCLUDE_DIRS private_include + REQUIRES spi_flash) \ No newline at end of file diff --git a/components/wear_levelling/test/CMakeLists.txt b/components/wear_levelling/test/CMakeLists.txt index 133be91080..a81bf5a8e5 100644 --- a/components/wear_levelling/test/CMakeLists.txt +++ b/components/wear_levelling/test/CMakeLists.txt @@ -1,8 +1,4 @@ -set(COMPONENT_SRCDIRS ".") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -set(COMPONENT_REQUIRES unity test_utils wear_levelling) - -set(COMPONENT_EMBED_FILES test_partition_v1.bin) - -register_component() \ No newline at end of file +idf_component_register(SRC_DIRS "." + INCLUDE_DIRS "." + REQUIRES unity test_utils wear_levelling + EMBED_FILES test_partition_v1.bin) \ No newline at end of file diff --git a/components/wifi_provisioning/CMakeLists.txt b/components/wifi_provisioning/CMakeLists.txt index ec129bce3b..58ddab84f6 100644 --- a/components/wifi_provisioning/CMakeLists.txt +++ b/components/wifi_provisioning/CMakeLists.txt @@ -1,21 +1,20 @@ -set(COMPONENT_ADD_INCLUDEDIRS include) -set(COMPONENT_PRIV_INCLUDEDIRS src proto-c ../protocomm/proto-c) -set(COMPONENT_SRCS "src/wifi_config.c" - "src/manager.c" - "src/handlers.c" - "src/scheme_softap.c" - "src/scheme_console.c" - "proto-c/wifi_config.pb-c.c" - "proto-c/wifi_constants.pb-c.c") - -set(COMPONENT_REQUIRES lwip protocomm) -set(COMPONENT_PRIV_REQUIRES protobuf-c bt mdns json) +set(srcs "src/wifi_config.c" + "src/manager.c" + "src/handlers.c" + "src/scheme_softap.c" + "src/scheme_console.c" + "proto-c/wifi_config.pb-c.c" + "proto-c/wifi_constants.pb-c.c") if(CONFIG_BT_ENABLED) if(CONFIG_BT_BLUEDROID_ENABLED) - list(APPEND COMPONENT_SRCS + list(APPEND srcs "src/scheme_ble.c") endif() endif() -register_component() +idf_component_register(SRCS "${srcs}" + INCLUDE_DIRS include + PRIV_INCLUDE_DIRS src proto-c ../protocomm/proto-c + REQUIRES lwip protocomm + PRIV_REQUIRES protobuf-c bt mdns json) diff --git a/components/wpa_supplicant/CMakeLists.txt b/components/wpa_supplicant/CMakeLists.txt index 35bf0bbdb3..78ce6573aa 100644 --- a/components/wpa_supplicant/CMakeLists.txt +++ b/components/wpa_supplicant/CMakeLists.txt @@ -1,83 +1,81 @@ -set(COMPONENT_SRCS "port/os_xtensa.c" - "src/crypto/aes-cbc.c" - "src/crypto/aes-internal-dec.c" - "src/crypto/aes-internal-enc.c" - "src/crypto/aes-internal.c" - "src/crypto/aes-unwrap.c" - "src/crypto/aes-wrap.c" - "src/crypto/bignum.c" - "src/crypto/crypto_mbedtls.c" - "src/crypto/crypto_internal-cipher.c" - "src/crypto/crypto_internal-modexp.c" - "src/crypto/crypto_internal-rsa.c" - "src/crypto/crypto_internal.c" - "src/crypto/des-internal.c" - "src/crypto/dh_group5.c" - "src/crypto/dh_groups.c" - "src/crypto/md4-internal.c" - "src/crypto/md5-internal.c" - "src/crypto/md5.c" - "src/crypto/ms_funcs.c" - "src/crypto/rc4.c" - "src/crypto/sha1-internal.c" - "src/crypto/sha1-pbkdf2.c" - "src/crypto/sha1.c" - "src/crypto/sha256-internal.c" - "src/crypto/sha256.c" - "src/fast_crypto/fast_aes-cbc.c" - "src/fast_crypto/fast_aes-unwrap.c" - "src/fast_crypto/fast_aes-wrap.c" - "src/fast_crypto/fast_crypto_internal-cipher.c" - "src/fast_crypto/fast_crypto_internal-modexp.c" - "src/fast_crypto/fast_crypto_internal.c" - "src/fast_crypto/fast_sha256-internal.c" - "src/fast_crypto/fast_sha256.c" - "src/wpa2/eap_peer/chap.c" - "src/wpa2/eap_peer/eap.c" - "src/wpa2/eap_peer/eap_common.c" - "src/wpa2/eap_peer/eap_mschapv2.c" - "src/wpa2/eap_peer/eap_peap.c" - "src/wpa2/eap_peer/eap_peap_common.c" - "src/wpa2/eap_peer/eap_tls.c" - "src/wpa2/eap_peer/eap_tls_common.c" - "src/wpa2/eap_peer/eap_ttls.c" - "src/wpa2/eap_peer/mschapv2.c" - "src/wpa2/tls/asn1.c" - "src/wpa2/tls/bignum.c" - "src/wpa2/tls/pkcs1.c" - "src/wpa2/tls/pkcs5.c" - "src/wpa2/tls/pkcs8.c" - "src/wpa2/tls/rsa.c" - "src/wpa2/tls/tls_internal.c" - "src/wpa2/tls/tlsv1_client.c" - "src/wpa2/tls/tlsv1_client_read.c" - "src/wpa2/tls/tlsv1_client_write.c" - "src/wpa2/tls/tlsv1_common.c" - "src/wpa2/tls/tlsv1_cred.c" - "src/wpa2/tls/tlsv1_record.c" - "src/wpa2/tls/tlsv1_server.c" - "src/wpa2/tls/tlsv1_server_read.c" - "src/wpa2/tls/tlsv1_server_write.c" - "src/wpa2/tls/x509v3.c" - "src/wpa2/utils/base64.c" - "src/wpa2/utils/ext_password.c" - "src/wps/eap_common.c" - "src/wps/uuid.c" - "src/wps/wps.c" - "src/wps/wps_attr_build.c" - "src/wps/wps_attr_parse.c" - "src/wps/wps_attr_process.c" - "src/wps/wps_common.c" - "src/wps/wps_dev_attr.c" - "src/wps/wps_enrollee.c" - "src/wps/wps_registrar.c" - "src/wps/wps_validate.c") -set(COMPONENT_ADD_INCLUDEDIRS include port/include) +set(srcs "port/os_xtensa.c" + "src/crypto/aes-cbc.c" + "src/crypto/aes-internal-dec.c" + "src/crypto/aes-internal-enc.c" + "src/crypto/aes-internal.c" + "src/crypto/aes-unwrap.c" + "src/crypto/aes-wrap.c" + "src/crypto/bignum.c" + "src/crypto/crypto_mbedtls.c" + "src/crypto/crypto_internal-cipher.c" + "src/crypto/crypto_internal-modexp.c" + "src/crypto/crypto_internal-rsa.c" + "src/crypto/crypto_internal.c" + "src/crypto/des-internal.c" + "src/crypto/dh_group5.c" + "src/crypto/dh_groups.c" + "src/crypto/md4-internal.c" + "src/crypto/md5-internal.c" + "src/crypto/md5.c" + "src/crypto/ms_funcs.c" + "src/crypto/rc4.c" + "src/crypto/sha1-internal.c" + "src/crypto/sha1-pbkdf2.c" + "src/crypto/sha1.c" + "src/crypto/sha256-internal.c" + "src/crypto/sha256.c" + "src/fast_crypto/fast_aes-cbc.c" + "src/fast_crypto/fast_aes-unwrap.c" + "src/fast_crypto/fast_aes-wrap.c" + "src/fast_crypto/fast_crypto_internal-cipher.c" + "src/fast_crypto/fast_crypto_internal-modexp.c" + "src/fast_crypto/fast_crypto_internal.c" + "src/fast_crypto/fast_sha256-internal.c" + "src/fast_crypto/fast_sha256.c" + "src/wpa2/eap_peer/chap.c" + "src/wpa2/eap_peer/eap.c" + "src/wpa2/eap_peer/eap_common.c" + "src/wpa2/eap_peer/eap_mschapv2.c" + "src/wpa2/eap_peer/eap_peap.c" + "src/wpa2/eap_peer/eap_peap_common.c" + "src/wpa2/eap_peer/eap_tls.c" + "src/wpa2/eap_peer/eap_tls_common.c" + "src/wpa2/eap_peer/eap_ttls.c" + "src/wpa2/eap_peer/mschapv2.c" + "src/wpa2/tls/asn1.c" + "src/wpa2/tls/bignum.c" + "src/wpa2/tls/pkcs1.c" + "src/wpa2/tls/pkcs5.c" + "src/wpa2/tls/pkcs8.c" + "src/wpa2/tls/rsa.c" + "src/wpa2/tls/tls_internal.c" + "src/wpa2/tls/tlsv1_client.c" + "src/wpa2/tls/tlsv1_client_read.c" + "src/wpa2/tls/tlsv1_client_write.c" + "src/wpa2/tls/tlsv1_common.c" + "src/wpa2/tls/tlsv1_cred.c" + "src/wpa2/tls/tlsv1_record.c" + "src/wpa2/tls/tlsv1_server.c" + "src/wpa2/tls/tlsv1_server_read.c" + "src/wpa2/tls/tlsv1_server_write.c" + "src/wpa2/tls/x509v3.c" + "src/wpa2/utils/base64.c" + "src/wpa2/utils/ext_password.c" + "src/wps/eap_common.c" + "src/wps/uuid.c" + "src/wps/wps.c" + "src/wps/wps_attr_build.c" + "src/wps/wps_attr_parse.c" + "src/wps/wps_attr_process.c" + "src/wps/wps_common.c" + "src/wps/wps_dev_attr.c" + "src/wps/wps_enrollee.c" + "src/wps/wps_registrar.c" + "src/wps/wps_validate.c") -set(COMPONENT_REQUIRES "") -set(COMPONENT_PRIV_REQUIRES mbedtls) - -register_component() +idf_component_register(SRCS "${srcs}" + INCLUDE_DIRS include port/include + PRIV_REQUIRES mbedtls) target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-strict-aliasing) target_compile_definitions(${COMPONENT_LIB} PRIVATE diff --git a/components/wpa_supplicant/test/CMakeLists.txt b/components/wpa_supplicant/test/CMakeLists.txt index 28ca784c09..1be503f44f 100644 --- a/components/wpa_supplicant/test/CMakeLists.txt +++ b/components/wpa_supplicant/test/CMakeLists.txt @@ -1,6 +1,3 @@ -set(COMPONENT_SRCDIRS ".") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -set(COMPONENT_REQUIRES unity wpa_supplicant mbedtls) - -register_component() +idf_component_register(SRC_DIRS "." + INCLUDE_DIRS "." + REQUIRES unity wpa_supplicant mbedtls) \ No newline at end of file diff --git a/components/xtensa/CMakeLists.txt b/components/xtensa/CMakeLists.txt index 1bb997d673..08a1ccf64c 100644 --- a/components/xtensa/CMakeLists.txt +++ b/components/xtensa/CMakeLists.txt @@ -1,10 +1,7 @@ -set(COMPONENT_SRCS "eri.c" "trax.c" "debug_helpers.c" "debug_helpers_asm.S") +idf_build_get_property(target IDF_TARGET) +idf_component_register(SRCS "eri.c" "trax.c" "debug_helpers.c" "debug_helpers_asm.S" + INCLUDE_DIRS include ${target}/include + LDFRAGMENTS linker.lf + PRIV_REQUIRES soc) -set(COMPONENT_ADD_INCLUDEDIRS "include" "${IDF_TARGET}/include") - -set(COMPONENT_ADD_LDFRAGMENTS linker.lf) -set(COMPONENT_PRIV_REQUIRES soc) - -register_component() - -target_link_libraries(${COMPONENT_LIB} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/${IDF_TARGET}/libhal.a") +target_link_libraries(${COMPONENT_LIB} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/${target}/libhal.a") From 6771eead80534c51efb2033c04769ef5893b4838 Mon Sep 17 00:00:00 2001 From: Renz Christian Bagaporo Date: Sun, 28 Apr 2019 15:38:46 +0800 Subject: [PATCH 111/486] examples: use new component registration api --- components/esp_websocket_client/CMakeLists.txt | 7 +++---- components/esp_wifi/test/CMakeLists.txt | 2 +- .../a2dp_gatts_coex/main/CMakeLists.txt | 10 ++++------ .../bluetooth/a2dp_sink/main/CMakeLists.txt | 9 ++++----- .../bluetooth/a2dp_source/main/CMakeLists.txt | 8 +++----- examples/bluetooth/ble_adv/main/CMakeLists.txt | 6 ++---- .../ble_compatibility_test/main/CMakeLists.txt | 6 ++---- .../ble_eddystone/main/CMakeLists.txt | 8 +++----- .../ble_hid_device_demo/main/CMakeLists.txt | 12 +++++------- .../bluetooth/ble_ibeacon/main/CMakeLists.txt | 8 +++----- .../ble_spp_client/main/CMakeLists.txt | 6 ++---- .../ble_spp_server/main/CMakeLists.txt | 6 ++---- .../throughput_client/main/CMakeLists.txt | 6 ++---- .../throughput_server/main/CMakeLists.txt | 6 ++---- examples/bluetooth/blufi/main/CMakeLists.txt | 8 +++----- .../bluetooth/bt_discovery/main/CMakeLists.txt | 6 ++---- .../bt_spp_acceptor/main/CMakeLists.txt | 6 ++---- .../bt_spp_initiator/main/CMakeLists.txt | 6 ++---- .../bt_spp_vfs_acceptor/main/CMakeLists.txt | 8 +++----- .../bt_spp_vfs_initiator/main/CMakeLists.txt | 8 +++----- .../controller_hci_uart/main/CMakeLists.txt | 6 ++---- .../bluetooth/gatt_client/main/CMakeLists.txt | 6 ++---- .../gatt_security_client/main/CMakeLists.txt | 6 ++---- .../gatt_security_server/main/CMakeLists.txt | 6 ++---- .../bluetooth/gatt_server/main/CMakeLists.txt | 6 ++---- .../main/CMakeLists.txt | 6 ++---- .../gattc_multi_connect/main/CMakeLists.txt | 6 ++---- .../cmake/import_lib/main/CMakeLists.txt | 9 +++------ .../protocol_examples_common/CMakeLists.txt | 7 ++----- examples/ethernet/eth2ap/main/CMakeLists.txt | 8 ++------ examples/ethernet/ethernet/main/CMakeLists.txt | 6 ++---- examples/ethernet/iperf/main/CMakeLists.txt | 8 +++----- examples/get-started/blink/main/CMakeLists.txt | 6 ++---- .../hello_world/main/CMakeLists.txt | 6 ++---- .../internal_communication/main/CMakeLists.txt | 8 +++----- .../mesh/manual_networking/main/CMakeLists.txt | 8 +++----- examples/peripherals/adc/main/CMakeLists.txt | 6 ++---- examples/peripherals/adc2/main/CMakeLists.txt | 6 ++---- .../can_alert_and_recovery/main/CMakeLists.txt | 6 ++---- .../main/CMakeLists.txt | 6 ++---- .../can_network_master/main/CMakeLists.txt | 6 ++---- .../can_network_slave/main/CMakeLists.txt | 6 ++---- .../can/can_self_test/main/CMakeLists.txt | 6 ++---- examples/peripherals/gpio/main/CMakeLists.txt | 6 ++---- .../i2c/i2c_self_test/main/CMakeLists.txt | 6 ++---- .../i2c/i2c_tools/main/CMakeLists.txt | 8 +++----- examples/peripherals/i2s/main/CMakeLists.txt | 6 ++---- .../i2s_adc_dac/main/CMakeLists.txt | 6 ++---- examples/peripherals/ledc/main/CMakeLists.txt | 6 ++---- .../mcpwm_basic_config/main/CMakeLists.txt | 6 ++---- .../mcpwm_bldc_control/main/CMakeLists.txt | 6 ++---- .../main/CMakeLists.txt | 6 ++---- .../mcpwm_servo_control/main/CMakeLists.txt | 6 ++---- examples/peripherals/pcnt/main/CMakeLists.txt | 6 ++---- .../rmt_nec_tx_rx/main/CMakeLists.txt | 6 ++---- .../peripherals/rmt_tx/main/CMakeLists.txt | 6 ++---- .../host/components/esp_slave/CMakeLists.txt | 9 +++------ .../peripherals/sdio/host/main/CMakeLists.txt | 6 ++---- .../peripherals/sdio/slave/main/CMakeLists.txt | 6 ++---- .../peripherals/sigmadelta/main/CMakeLists.txt | 6 ++---- .../peripherals/spi_master/main/CMakeLists.txt | 14 +++++--------- .../spi_slave/receiver/main/CMakeLists.txt | 6 ++---- .../spi_slave/sender/main/CMakeLists.txt | 6 ++---- .../timer_group/main/CMakeLists.txt | 6 ++---- .../touch_pad_interrupt/main/CMakeLists.txt | 6 ++---- .../touch_pad_read/main/CMakeLists.txt | 6 ++---- .../uart/nmea0183_parser/main/CMakeLists.txt | 8 +++----- .../uart_async_rxtxtasks/main/CMakeLists.txt | 6 ++---- .../uart/uart_echo/main/CMakeLists.txt | 6 ++---- .../uart/uart_echo_rs485/main/CMakeLists.txt | 6 ++---- .../uart/uart_events/main/CMakeLists.txt | 6 ++---- .../uart/uart_select/main/CMakeLists.txt | 6 ++---- .../asio/chat_client/main/CMakeLists.txt | 6 ++---- .../asio/chat_server/main/CMakeLists.txt | 6 ++---- .../asio/tcp_echo_server/main/CMakeLists.txt | 6 ++---- .../asio/udp_echo_server/main/CMakeLists.txt | 6 ++---- .../protocols/coap_client/main/CMakeLists.txt | 6 ++---- .../protocols/coap_server/main/CMakeLists.txt | 6 ++---- .../esp_http_client/main/CMakeLists.txt | 10 +++------- .../components/sh2lib/CMakeLists.txt | 12 ++++-------- .../http2_request/main/CMakeLists.txt | 6 ++---- .../protocols/http_request/main/CMakeLists.txt | 6 ++---- .../advanced_tests/main/CMakeLists.txt | 8 +++----- .../file_serving/main/CMakeLists.txt | 9 +++------ .../persistent_sockets/main/CMakeLists.txt | 6 ++---- .../restful_server/main/CMakeLists.txt | 7 +++---- .../http_server/simple/main/CMakeLists.txt | 6 ++---- .../https_mbedtls/main/CMakeLists.txt | 10 +++------- .../https_request/main/CMakeLists.txt | 10 +++------- .../protocols/https_server/main/CMakeLists.txt | 12 ++++-------- examples/protocols/mdns/main/CMakeLists.txt | 6 ++---- .../modbus_master/main/CMakeLists.txt | 10 ++++------ .../protocols/modbus_slave/main/CMakeLists.txt | 8 +++----- .../mqtt/publish_test/main/CMakeLists.txt | 6 ++---- .../protocols/mqtt/ssl/main/CMakeLists.txt | 6 ++---- .../mqtt/ssl_mutual_auth/main/CMakeLists.txt | 6 ++---- .../protocols/mqtt/tcp/main/CMakeLists.txt | 6 ++---- examples/protocols/mqtt/ws/main/CMakeLists.txt | 6 ++---- .../protocols/mqtt/wss/main/CMakeLists.txt | 6 ++---- .../openssl_client/main/CMakeLists.txt | 6 ++---- .../openssl_server/main/CMakeLists.txt | 10 +++------- .../components/modem/CMakeLists.txt | 18 +++++++----------- .../protocols/pppos_client/main/CMakeLists.txt | 6 ++---- examples/protocols/sntp/main/CMakeLists.txt | 6 ++---- .../sockets/tcp_client/main/CMakeLists.txt | 6 ++---- .../sockets/tcp_server/main/CMakeLists.txt | 6 ++---- .../sockets/udp_client/main/CMakeLists.txt | 6 ++---- .../sockets/udp_multicast/main/CMakeLists.txt | 6 ++---- .../sockets/udp_server/main/CMakeLists.txt | 6 ++---- .../protocols/websocket/main/CMakeLists.txt | 6 ++---- .../provisioning/ble_prov/main/CMakeLists.txt | 10 ++++------ .../console_prov/main/CMakeLists.txt | 10 ++++------ .../custom_provisioning/CMakeLists.txt | 13 +++++-------- .../custom_config/main/CMakeLists.txt | 10 ++++------ .../provisioning/manager/main/CMakeLists.txt | 6 ++---- .../softap_prov/main/CMakeLists.txt | 10 ++++------ .../storage/nvs_rw_blob/main/CMakeLists.txt | 6 ++---- .../storage/nvs_rw_value/main/CMakeLists.txt | 6 ++---- .../partition_find/main/CMakeLists.txt | 9 +++------ .../partition_mmap/main/CMakeLists.txt | 6 ++---- .../partition_ops/main/CMakeLists.txt | 6 ++---- examples/storage/parttool/main/CMakeLists.txt | 6 ++---- examples/storage/sd_card/main/CMakeLists.txt | 6 ++---- .../storage/semihost_vfs/main/CMakeLists.txt | 5 +++-- examples/storage/spiffs/main/CMakeLists.txt | 6 ++---- examples/storage/spiffsgen/main/CMakeLists.txt | 6 ++---- .../storage/wear_levelling/main/CMakeLists.txt | 6 ++---- .../app_trace_to_host/main/CMakeLists.txt | 6 ++---- .../base_mac_address/main/CMakeLists.txt | 6 ++---- .../console/components/cmd_nvs/CMakeLists.txt | 10 +++------- .../components/cmd_system/CMakeLists.txt | 10 +++------- examples/system/console/main/CMakeLists.txt | 8 +++----- .../system/cpp_exceptions/main/CMakeLists.txt | 6 ++---- .../system/cpp_pthread/main/CMakeLists.txt | 6 ++---- examples/system/deep_sleep/main/CMakeLists.txt | 6 ++---- .../default_event_loop/main/CMakeLists.txt | 6 ++---- .../user_event_loops/main/CMakeLists.txt | 6 ++---- examples/system/esp_timer/main/CMakeLists.txt | 6 ++---- .../real_time_stats/main/CMakeLists.txt | 6 ++---- examples/system/gcov/main/CMakeLists.txt | 8 +++----- examples/system/himem/main/CMakeLists.txt | 6 ++---- .../system/light_sleep/main/CMakeLists.txt | 6 ++---- .../system/network_tests/main/CMakeLists.txt | 6 ++---- .../ota/advanced_https_ota/main/CMakeLists.txt | 13 ++++--------- .../ota/native_ota_example/main/CMakeLists.txt | 9 +++------ .../system/ota/otatool/main/CMakeLists.txt | 6 ++---- .../ota/simple_ota_example/main/CMakeLists.txt | 10 +++------- examples/system/select/main/CMakeLists.txt | 6 ++---- .../system/sysview_tracing/main/CMakeLists.txt | 6 ++---- .../main/CMakeLists.txt | 6 ++---- .../system/task_watchdog/main/CMakeLists.txt | 6 ++---- examples/system/ulp/main/CMakeLists.txt | 10 +++------- examples/system/ulp_adc/main/CMakeLists.txt | 12 ++++-------- .../components/testable/CMakeLists.txt | 6 ++---- .../components/testable/test/CMakeLists.txt | 9 +++------ examples/system/unit_test/main/CMakeLists.txt | 6 ++---- .../system/unit_test/test/main/CMakeLists.txt | 6 ++---- examples/wifi/espnow/main/CMakeLists.txt | 6 ++---- .../getting_started/softAP/main/CMakeLists.txt | 6 ++---- .../station/main/CMakeLists.txt | 6 ++---- .../wifi/iperf/components/iperf/CMakeLists.txt | 10 +++------- examples/wifi/iperf/main/CMakeLists.txt | 8 +++----- examples/wifi/power_save/main/CMakeLists.txt | 6 ++---- examples/wifi/scan/main/CMakeLists.txt | 6 ++---- .../components/pcap/CMakeLists.txt | 7 ++----- .../wifi/simple_sniffer/main/CMakeLists.txt | 13 +++---------- examples/wifi/smart_config/main/CMakeLists.txt | 6 ++---- .../wifi/wpa2_enterprise/main/CMakeLists.txt | 10 +++------- examples/wifi/wps/main/CMakeLists.txt | 6 ++---- 169 files changed, 409 insertions(+), 788 deletions(-) diff --git a/components/esp_websocket_client/CMakeLists.txt b/components/esp_websocket_client/CMakeLists.txt index 723199ce0f..f366a278d3 100644 --- a/components/esp_websocket_client/CMakeLists.txt +++ b/components/esp_websocket_client/CMakeLists.txt @@ -1,4 +1,3 @@ -set(COMPONENT_SRCS "esp_websocket_client.c") -set(COMPONENT_ADD_INCLUDEDIRS "include") -set(COMPONENT_REQUIRES lwip esp-tls tcp_transport nghttp) -register_component() +idf_component_register(SRCS "esp_websocket_client.c" + INCLUDE_DIRS "include" + REQUIRES lwip esp-tls tcp_transport nghttp) diff --git a/components/esp_wifi/test/CMakeLists.txt b/components/esp_wifi/test/CMakeLists.txt index a3ff3757c3..cf98dd8137 100644 --- a/components/esp_wifi/test/CMakeLists.txt +++ b/components/esp_wifi/test/CMakeLists.txt @@ -1,4 +1,4 @@ -idf_component_register(SRCDIRS "." +idf_component_register(SRC_DIRS "." INCLUDE_DIRS "." "${CMAKE_CURRENT_BINARY_DIR}" REQUIRES unity test_utils nvs_flash ulp esp_common) diff --git a/examples/bluetooth/a2dp_gatts_coex/main/CMakeLists.txt b/examples/bluetooth/a2dp_gatts_coex/main/CMakeLists.txt index a55538b5fb..55f2d445aa 100644 --- a/examples/bluetooth/a2dp_gatts_coex/main/CMakeLists.txt +++ b/examples/bluetooth/a2dp_gatts_coex/main/CMakeLists.txt @@ -1,6 +1,4 @@ -set(COMPONENT_SRCS "bt_app_av.c" - "bt_app_core.c" - "main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "bt_app_av.c" + "bt_app_core.c" + "main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/bluetooth/a2dp_sink/main/CMakeLists.txt b/examples/bluetooth/a2dp_sink/main/CMakeLists.txt index a55538b5fb..d1d916efbd 100644 --- a/examples/bluetooth/a2dp_sink/main/CMakeLists.txt +++ b/examples/bluetooth/a2dp_sink/main/CMakeLists.txt @@ -1,6 +1,5 @@ -set(COMPONENT_SRCS "bt_app_av.c" - "bt_app_core.c" - "main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") +idf_component_register(SRCS "bt_app_av.c" + "bt_app_core.c" + "main.c" + INCLUDE_DIRS ".") -register_component() diff --git a/examples/bluetooth/a2dp_source/main/CMakeLists.txt b/examples/bluetooth/a2dp_source/main/CMakeLists.txt index 98b05a101b..d0a3885ddb 100644 --- a/examples/bluetooth/a2dp_source/main/CMakeLists.txt +++ b/examples/bluetooth/a2dp_source/main/CMakeLists.txt @@ -1,5 +1,3 @@ -set(COMPONENT_SRCS "bt_app_core.c" - "main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "bt_app_core.c" + "main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/bluetooth/ble_adv/main/CMakeLists.txt b/examples/bluetooth/ble_adv/main/CMakeLists.txt index d005e60123..494d32308b 100644 --- a/examples/bluetooth/ble_adv/main/CMakeLists.txt +++ b/examples/bluetooth/ble_adv/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "app_bt.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "app_bt.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/bluetooth/ble_compatibility_test/main/CMakeLists.txt b/examples/bluetooth/ble_compatibility_test/main/CMakeLists.txt index 3435b4c563..99553fec31 100644 --- a/examples/bluetooth/ble_compatibility_test/main/CMakeLists.txt +++ b/examples/bluetooth/ble_compatibility_test/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "ble_compatibility_test.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "ble_compatibility_test.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/bluetooth/ble_eddystone/main/CMakeLists.txt b/examples/bluetooth/ble_eddystone/main/CMakeLists.txt index 66a4e1aa6c..cdd852c570 100644 --- a/examples/bluetooth/ble_eddystone/main/CMakeLists.txt +++ b/examples/bluetooth/ble_eddystone/main/CMakeLists.txt @@ -1,5 +1,3 @@ -set(COMPONENT_SRCS "esp_eddystone_api.c" - "esp_eddystone_demo.c") -set(COMPONENT_ADD_INCLUDEDIRS "") - -register_component() +idf_component_register(SRCS "esp_eddystone_api.c" + "esp_eddystone_demo.c" + INCLUDE_DIRS "") \ No newline at end of file diff --git a/examples/bluetooth/ble_hid_device_demo/main/CMakeLists.txt b/examples/bluetooth/ble_hid_device_demo/main/CMakeLists.txt index 6445d3c5ef..efba49f6b7 100644 --- a/examples/bluetooth/ble_hid_device_demo/main/CMakeLists.txt +++ b/examples/bluetooth/ble_hid_device_demo/main/CMakeLists.txt @@ -1,10 +1,8 @@ -set(COMPONENT_SRCS "ble_hidd_demo_main.c" - "esp_hidd_prf_api.c" - "hid_dev.c" - "hid_device_le_prf.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "ble_hidd_demo_main.c" + "esp_hidd_prf_api.c" + "hid_dev.c" + "hid_device_le_prf.c" + INCLUDE_DIRS ".") if(GCC_NOT_5_2_0) target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-unused-const-variable) diff --git a/examples/bluetooth/ble_ibeacon/main/CMakeLists.txt b/examples/bluetooth/ble_ibeacon/main/CMakeLists.txt index 1ae1a02e64..50293e2a7b 100644 --- a/examples/bluetooth/ble_ibeacon/main/CMakeLists.txt +++ b/examples/bluetooth/ble_ibeacon/main/CMakeLists.txt @@ -1,5 +1,3 @@ -set(COMPONENT_SRCS "esp_ibeacon_api.c" - "ibeacon_demo.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "esp_ibeacon_api.c" + "ibeacon_demo.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/bluetooth/ble_spp_client/main/CMakeLists.txt b/examples/bluetooth/ble_spp_client/main/CMakeLists.txt index de584f3977..1b83a293fa 100644 --- a/examples/bluetooth/ble_spp_client/main/CMakeLists.txt +++ b/examples/bluetooth/ble_spp_client/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "spp_client_demo.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "spp_client_demo.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/bluetooth/ble_spp_server/main/CMakeLists.txt b/examples/bluetooth/ble_spp_server/main/CMakeLists.txt index 99f35ee072..be4f67ba8d 100644 --- a/examples/bluetooth/ble_spp_server/main/CMakeLists.txt +++ b/examples/bluetooth/ble_spp_server/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "ble_spp_server_demo.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "ble_spp_server_demo.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/bluetooth/ble_throughput/throughput_client/main/CMakeLists.txt b/examples/bluetooth/ble_throughput/throughput_client/main/CMakeLists.txt index 1fe275526d..e4c37ee280 100644 --- a/examples/bluetooth/ble_throughput/throughput_client/main/CMakeLists.txt +++ b/examples/bluetooth/ble_throughput/throughput_client/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "example_ble_client_throughput.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "example_ble_client_throughput.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/bluetooth/ble_throughput/throughput_server/main/CMakeLists.txt b/examples/bluetooth/ble_throughput/throughput_server/main/CMakeLists.txt index 3c1f62ac89..c54e45c928 100644 --- a/examples/bluetooth/ble_throughput/throughput_server/main/CMakeLists.txt +++ b/examples/bluetooth/ble_throughput/throughput_server/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "example_ble_server_throughput.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "example_ble_server_throughput.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/bluetooth/blufi/main/CMakeLists.txt b/examples/bluetooth/blufi/main/CMakeLists.txt index cbb9b24c36..8e40bfbf2f 100644 --- a/examples/bluetooth/blufi/main/CMakeLists.txt +++ b/examples/bluetooth/blufi/main/CMakeLists.txt @@ -1,5 +1,3 @@ -set(COMPONENT_SRCS "blufi_example_main.c" - "blufi_security.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "blufi_example_main.c" + "blufi_security.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/bluetooth/bt_discovery/main/CMakeLists.txt b/examples/bluetooth/bt_discovery/main/CMakeLists.txt index 68ddbfb4c8..4bc23a96f4 100644 --- a/examples/bluetooth/bt_discovery/main/CMakeLists.txt +++ b/examples/bluetooth/bt_discovery/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "bt_discovery.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "bt_discovery.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/bluetooth/bt_spp_acceptor/main/CMakeLists.txt b/examples/bluetooth/bt_spp_acceptor/main/CMakeLists.txt index 0694503f92..a71f85ba7c 100644 --- a/examples/bluetooth/bt_spp_acceptor/main/CMakeLists.txt +++ b/examples/bluetooth/bt_spp_acceptor/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "example_spp_acceptor_demo.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "example_spp_acceptor_demo.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/bluetooth/bt_spp_initiator/main/CMakeLists.txt b/examples/bluetooth/bt_spp_initiator/main/CMakeLists.txt index 1eae1b74c9..09a976ad1c 100644 --- a/examples/bluetooth/bt_spp_initiator/main/CMakeLists.txt +++ b/examples/bluetooth/bt_spp_initiator/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "example_spp_initiator_demo.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "example_spp_initiator_demo.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/bluetooth/bt_spp_vfs_acceptor/main/CMakeLists.txt b/examples/bluetooth/bt_spp_vfs_acceptor/main/CMakeLists.txt index b224db80b6..369e404a15 100644 --- a/examples/bluetooth/bt_spp_vfs_acceptor/main/CMakeLists.txt +++ b/examples/bluetooth/bt_spp_vfs_acceptor/main/CMakeLists.txt @@ -1,5 +1,3 @@ -set(COMPONENT_SRCS "example_spp_vfs_acceptor_demo.c" - "spp_task.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "example_spp_vfs_acceptor_demo.c" + "spp_task.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/bluetooth/bt_spp_vfs_initiator/main/CMakeLists.txt b/examples/bluetooth/bt_spp_vfs_initiator/main/CMakeLists.txt index f3b9111353..03781e4786 100644 --- a/examples/bluetooth/bt_spp_vfs_initiator/main/CMakeLists.txt +++ b/examples/bluetooth/bt_spp_vfs_initiator/main/CMakeLists.txt @@ -1,5 +1,3 @@ -set(COMPONENT_SRCS "example_spp_vfs_initiator_demo.c" - "spp_task.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "example_spp_vfs_initiator_demo.c" + "spp_task.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/bluetooth/controller_hci_uart/main/CMakeLists.txt b/examples/bluetooth/controller_hci_uart/main/CMakeLists.txt index 2f40a64c69..b4eacc768a 100644 --- a/examples/bluetooth/controller_hci_uart/main/CMakeLists.txt +++ b/examples/bluetooth/controller_hci_uart/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "controller_hci_uart_demo.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "controller_hci_uart_demo.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/bluetooth/gatt_client/main/CMakeLists.txt b/examples/bluetooth/gatt_client/main/CMakeLists.txt index 7c79edceaa..82a30b2f71 100644 --- a/examples/bluetooth/gatt_client/main/CMakeLists.txt +++ b/examples/bluetooth/gatt_client/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "gattc_demo.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "gattc_demo.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/bluetooth/gatt_security_client/main/CMakeLists.txt b/examples/bluetooth/gatt_security_client/main/CMakeLists.txt index 32cea7e2e3..691e1edd29 100644 --- a/examples/bluetooth/gatt_security_client/main/CMakeLists.txt +++ b/examples/bluetooth/gatt_security_client/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "example_ble_sec_gattc_demo.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "example_ble_sec_gattc_demo.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/bluetooth/gatt_security_server/main/CMakeLists.txt b/examples/bluetooth/gatt_security_server/main/CMakeLists.txt index 87df03c692..6514591112 100644 --- a/examples/bluetooth/gatt_security_server/main/CMakeLists.txt +++ b/examples/bluetooth/gatt_security_server/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "example_ble_sec_gatts_demo.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "example_ble_sec_gatts_demo.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/bluetooth/gatt_server/main/CMakeLists.txt b/examples/bluetooth/gatt_server/main/CMakeLists.txt index d23e5ffc7d..e950c9ecd0 100644 --- a/examples/bluetooth/gatt_server/main/CMakeLists.txt +++ b/examples/bluetooth/gatt_server/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "gatts_demo.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "gatts_demo.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/bluetooth/gatt_server_service_table/main/CMakeLists.txt b/examples/bluetooth/gatt_server_service_table/main/CMakeLists.txt index 7588b5a7a1..fbe553576b 100644 --- a/examples/bluetooth/gatt_server_service_table/main/CMakeLists.txt +++ b/examples/bluetooth/gatt_server_service_table/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "gatts_table_creat_demo.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "gatts_table_creat_demo.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/bluetooth/gattc_multi_connect/main/CMakeLists.txt b/examples/bluetooth/gattc_multi_connect/main/CMakeLists.txt index 19dcf60d3a..826febe746 100644 --- a/examples/bluetooth/gattc_multi_connect/main/CMakeLists.txt +++ b/examples/bluetooth/gattc_multi_connect/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "gattc_multi_connect.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "gattc_multi_connect.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/build_system/cmake/import_lib/main/CMakeLists.txt b/examples/build_system/cmake/import_lib/main/CMakeLists.txt index fb136b8b97..6c60c8f980 100644 --- a/examples/build_system/cmake/import_lib/main/CMakeLists.txt +++ b/examples/build_system/cmake/import_lib/main/CMakeLists.txt @@ -1,9 +1,6 @@ -set(COMPONENT_SRCS "main.cpp") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -set(COMPONENT_EMBED_TXTFILES "sample.xml") - -register_component() +idf_component_register(SRCS "main.cpp" + INCLUDE_DIRS "." + EMBED_TXTFILES "sample.xml") # Build static library, do not build test executables option(BUILD_SHARED_LIBS OFF) diff --git a/examples/common_components/protocol_examples_common/CMakeLists.txt b/examples/common_components/protocol_examples_common/CMakeLists.txt index 784909da03..0ccb219b4a 100644 --- a/examples/common_components/protocol_examples_common/CMakeLists.txt +++ b/examples/common_components/protocol_examples_common/CMakeLists.txt @@ -1,5 +1,2 @@ -set(COMPONENT_SRCS "connect.c" - "stdin_out.c") -set(COMPONENT_ADD_INCLUDEDIRS "include") - -register_component() +idf_component_register(SRCS "connect.c" "stdin_out.c" + INCLUDE_DIRS "include") diff --git a/examples/ethernet/eth2ap/main/CMakeLists.txt b/examples/ethernet/eth2ap/main/CMakeLists.txt index aaf55bea54..5dbbb14c2d 100644 --- a/examples/ethernet/eth2ap/main/CMakeLists.txt +++ b/examples/ethernet/eth2ap/main/CMakeLists.txt @@ -1,6 +1,2 @@ - -set(COMPONENT_SRCS "eth2ap_example_main.c") - -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "eth2ap_example_main.c" + INCLUDE_DIRS ".") diff --git a/examples/ethernet/ethernet/main/CMakeLists.txt b/examples/ethernet/ethernet/main/CMakeLists.txt index 8c0c9fa0fc..8bc01f1bb4 100644 --- a/examples/ethernet/ethernet/main/CMakeLists.txt +++ b/examples/ethernet/ethernet/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "ethernet_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "ethernet_example_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/ethernet/iperf/main/CMakeLists.txt b/examples/ethernet/iperf/main/CMakeLists.txt index 16ecc3272c..aa66030901 100644 --- a/examples/ethernet/iperf/main/CMakeLists.txt +++ b/examples/ethernet/iperf/main/CMakeLists.txt @@ -1,5 +1,3 @@ -set(COMPONENT_SRCS "cmd_ethernet.c" - "iperf_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "cmd_ethernet.c" + "iperf_example_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/get-started/blink/main/CMakeLists.txt b/examples/get-started/blink/main/CMakeLists.txt index 69f12a3847..413c3b5141 100644 --- a/examples/get-started/blink/main/CMakeLists.txt +++ b/examples/get-started/blink/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "blink.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "blink.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/get-started/hello_world/main/CMakeLists.txt b/examples/get-started/hello_world/main/CMakeLists.txt index b79b831950..c299e03782 100644 --- a/examples/get-started/hello_world/main/CMakeLists.txt +++ b/examples/get-started/hello_world/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "hello_world_main.c") -set(COMPONENT_ADD_INCLUDEDIRS "") - -register_component() \ No newline at end of file +idf_component_register(SRCS "hello_world_main.c" + INCLUDE_DIRS "") \ No newline at end of file diff --git a/examples/mesh/internal_communication/main/CMakeLists.txt b/examples/mesh/internal_communication/main/CMakeLists.txt index 451eeceed6..686ab05acf 100644 --- a/examples/mesh/internal_communication/main/CMakeLists.txt +++ b/examples/mesh/internal_communication/main/CMakeLists.txt @@ -1,5 +1,3 @@ -set(COMPONENT_SRCS "mesh_light.c" - "mesh_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ". include") - -register_component() +idf_component_register(SRCS "mesh_light.c" + "mesh_main.c" + INCLUDE_DIRS "." "include") diff --git a/examples/mesh/manual_networking/main/CMakeLists.txt b/examples/mesh/manual_networking/main/CMakeLists.txt index 1ae57b28ac..686ab05acf 100644 --- a/examples/mesh/manual_networking/main/CMakeLists.txt +++ b/examples/mesh/manual_networking/main/CMakeLists.txt @@ -1,5 +1,3 @@ -set(COMPONENT_SRCS "mesh_light.c" - "mesh_main.c") -set(COMPONENT_ADD_INCLUDEDIRS "." "include") - -register_component() +idf_component_register(SRCS "mesh_light.c" + "mesh_main.c" + INCLUDE_DIRS "." "include") diff --git a/examples/peripherals/adc/main/CMakeLists.txt b/examples/peripherals/adc/main/CMakeLists.txt index 9b5d48fc60..035e5bd270 100644 --- a/examples/peripherals/adc/main/CMakeLists.txt +++ b/examples/peripherals/adc/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "adc1_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "adc1_example_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/peripherals/adc2/main/CMakeLists.txt b/examples/peripherals/adc2/main/CMakeLists.txt index 1d7776ecf0..5a6c87d577 100644 --- a/examples/peripherals/adc2/main/CMakeLists.txt +++ b/examples/peripherals/adc2/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "adc2_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "adc2_example_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/peripherals/can/can_alert_and_recovery/main/CMakeLists.txt b/examples/peripherals/can/can_alert_and_recovery/main/CMakeLists.txt index cf9bf40a99..ab23bf60fb 100644 --- a/examples/peripherals/can/can_alert_and_recovery/main/CMakeLists.txt +++ b/examples/peripherals/can/can_alert_and_recovery/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "can_alert_and_recovery_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "can_alert_and_recovery_example_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/peripherals/can/can_network/can_network_listen_only/main/CMakeLists.txt b/examples/peripherals/can/can_network/can_network_listen_only/main/CMakeLists.txt index 5eab299b00..191642e1d1 100644 --- a/examples/peripherals/can/can_network/can_network_listen_only/main/CMakeLists.txt +++ b/examples/peripherals/can/can_network/can_network_listen_only/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "can_network_example_listen_only_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "can_network_example_listen_only_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/peripherals/can/can_network/can_network_master/main/CMakeLists.txt b/examples/peripherals/can/can_network/can_network_master/main/CMakeLists.txt index f0dc1d6a79..31a67ddcb9 100644 --- a/examples/peripherals/can/can_network/can_network_master/main/CMakeLists.txt +++ b/examples/peripherals/can/can_network/can_network_master/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "can_network_example_master_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "can_network_example_master_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/peripherals/can/can_network/can_network_slave/main/CMakeLists.txt b/examples/peripherals/can/can_network/can_network_slave/main/CMakeLists.txt index 4a002fbfc3..5ade11d5b5 100644 --- a/examples/peripherals/can/can_network/can_network_slave/main/CMakeLists.txt +++ b/examples/peripherals/can/can_network/can_network_slave/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "can_network_example_slave_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "can_network_example_slave_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/peripherals/can/can_self_test/main/CMakeLists.txt b/examples/peripherals/can/can_self_test/main/CMakeLists.txt index 21db765a8f..5c3f85e0a5 100644 --- a/examples/peripherals/can/can_self_test/main/CMakeLists.txt +++ b/examples/peripherals/can/can_self_test/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "can_self_test_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "can_self_test_example_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/peripherals/gpio/main/CMakeLists.txt b/examples/peripherals/gpio/main/CMakeLists.txt index d768cb8252..d33d5e1716 100644 --- a/examples/peripherals/gpio/main/CMakeLists.txt +++ b/examples/peripherals/gpio/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "gpio_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "gpio_example_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/peripherals/i2c/i2c_self_test/main/CMakeLists.txt b/examples/peripherals/i2c/i2c_self_test/main/CMakeLists.txt index 02386f7197..4dc828aacc 100644 --- a/examples/peripherals/i2c/i2c_self_test/main/CMakeLists.txt +++ b/examples/peripherals/i2c/i2c_self_test/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "i2c_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "i2c_example_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/peripherals/i2c/i2c_tools/main/CMakeLists.txt b/examples/peripherals/i2c/i2c_tools/main/CMakeLists.txt index 50cdc09826..fed3a94434 100644 --- a/examples/peripherals/i2c/i2c_tools/main/CMakeLists.txt +++ b/examples/peripherals/i2c/i2c_tools/main/CMakeLists.txt @@ -1,5 +1,3 @@ -set(COMPONENT_SRCS "i2ctools_example_main.c" - "cmd_i2ctools.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "i2ctools_example_main.c" + "cmd_i2ctools.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/peripherals/i2s/main/CMakeLists.txt b/examples/peripherals/i2s/main/CMakeLists.txt index dbb7b172a3..5233689fb2 100644 --- a/examples/peripherals/i2s/main/CMakeLists.txt +++ b/examples/peripherals/i2s/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "i2s_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "i2s_example_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/peripherals/i2s_adc_dac/main/CMakeLists.txt b/examples/peripherals/i2s_adc_dac/main/CMakeLists.txt index 6b03500639..e7392559ee 100644 --- a/examples/peripherals/i2s_adc_dac/main/CMakeLists.txt +++ b/examples/peripherals/i2s_adc_dac/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "app_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "app_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/peripherals/ledc/main/CMakeLists.txt b/examples/peripherals/ledc/main/CMakeLists.txt index 6405eb95ff..f6064011cc 100644 --- a/examples/peripherals/ledc/main/CMakeLists.txt +++ b/examples/peripherals/ledc/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "ledc_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "ledc_example_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/peripherals/mcpwm/mcpwm_basic_config/main/CMakeLists.txt b/examples/peripherals/mcpwm/mcpwm_basic_config/main/CMakeLists.txt index d5fdbf5600..ec236fe844 100644 --- a/examples/peripherals/mcpwm/mcpwm_basic_config/main/CMakeLists.txt +++ b/examples/peripherals/mcpwm/mcpwm_basic_config/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "mcpwm_basic_config_example.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "mcpwm_basic_config_example.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/peripherals/mcpwm/mcpwm_bldc_control/main/CMakeLists.txt b/examples/peripherals/mcpwm/mcpwm_bldc_control/main/CMakeLists.txt index a52ad94513..3f7cedf8d2 100644 --- a/examples/peripherals/mcpwm/mcpwm_bldc_control/main/CMakeLists.txt +++ b/examples/peripherals/mcpwm/mcpwm_bldc_control/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "mcpwm_bldc_control_hall_sensor_example.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "mcpwm_bldc_control_hall_sensor_example.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/peripherals/mcpwm/mcpwm_brushed_dc_control/main/CMakeLists.txt b/examples/peripherals/mcpwm/mcpwm_brushed_dc_control/main/CMakeLists.txt index 1259035cbf..81ecd07a8f 100644 --- a/examples/peripherals/mcpwm/mcpwm_brushed_dc_control/main/CMakeLists.txt +++ b/examples/peripherals/mcpwm/mcpwm_brushed_dc_control/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "mcpwm_brushed_dc_control_example.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "mcpwm_brushed_dc_control_example.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/peripherals/mcpwm/mcpwm_servo_control/main/CMakeLists.txt b/examples/peripherals/mcpwm/mcpwm_servo_control/main/CMakeLists.txt index c8fa3d96b9..7a6efda90a 100644 --- a/examples/peripherals/mcpwm/mcpwm_servo_control/main/CMakeLists.txt +++ b/examples/peripherals/mcpwm/mcpwm_servo_control/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "mcpwm_servo_control_example.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "mcpwm_servo_control_example.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/peripherals/pcnt/main/CMakeLists.txt b/examples/peripherals/pcnt/main/CMakeLists.txt index 2bd2779553..11ace9793f 100644 --- a/examples/peripherals/pcnt/main/CMakeLists.txt +++ b/examples/peripherals/pcnt/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "pcnt_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "pcnt_example_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/peripherals/rmt_nec_tx_rx/main/CMakeLists.txt b/examples/peripherals/rmt_nec_tx_rx/main/CMakeLists.txt index 80c75b2285..907a16c057 100644 --- a/examples/peripherals/rmt_nec_tx_rx/main/CMakeLists.txt +++ b/examples/peripherals/rmt_nec_tx_rx/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "infrared_nec_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "infrared_nec_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/peripherals/rmt_tx/main/CMakeLists.txt b/examples/peripherals/rmt_tx/main/CMakeLists.txt index a0a9147a2a..ad48214028 100644 --- a/examples/peripherals/rmt_tx/main/CMakeLists.txt +++ b/examples/peripherals/rmt_tx/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "rmt_tx_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "rmt_tx_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/peripherals/sdio/host/components/esp_slave/CMakeLists.txt b/examples/peripherals/sdio/host/components/esp_slave/CMakeLists.txt index 1cda8805cb..9f5c38bbaf 100644 --- a/examples/peripherals/sdio/host/components/esp_slave/CMakeLists.txt +++ b/examples/peripherals/sdio/host/components/esp_slave/CMakeLists.txt @@ -1,6 +1,3 @@ -set(COMPONENT_SRCS "esp_slave.c") -set(COMPONENT_ADD_INCLUDEDIRS "include") - -set(COMPONENT_REQUIRES driver sdmmc) - -register_component() +idf_component_register(SRCS "esp_slave.c" + INCLUDE_DIRS "include" + REQUIRES driver sdmmc) \ No newline at end of file diff --git a/examples/peripherals/sdio/host/main/CMakeLists.txt b/examples/peripherals/sdio/host/main/CMakeLists.txt index 6b03500639..e7392559ee 100644 --- a/examples/peripherals/sdio/host/main/CMakeLists.txt +++ b/examples/peripherals/sdio/host/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "app_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "app_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/peripherals/sdio/slave/main/CMakeLists.txt b/examples/peripherals/sdio/slave/main/CMakeLists.txt index 6b03500639..e7392559ee 100644 --- a/examples/peripherals/sdio/slave/main/CMakeLists.txt +++ b/examples/peripherals/sdio/slave/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "app_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "app_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/peripherals/sigmadelta/main/CMakeLists.txt b/examples/peripherals/sigmadelta/main/CMakeLists.txt index 1b0f2299f0..52abe2822d 100644 --- a/examples/peripherals/sigmadelta/main/CMakeLists.txt +++ b/examples/peripherals/sigmadelta/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "sigmadelta_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "sigmadelta_example_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/peripherals/spi_master/main/CMakeLists.txt b/examples/peripherals/spi_master/main/CMakeLists.txt index 806ccae2b6..6d1a795b48 100644 --- a/examples/peripherals/spi_master/main/CMakeLists.txt +++ b/examples/peripherals/spi_master/main/CMakeLists.txt @@ -1,9 +1,5 @@ -set(COMPONENT_SRCS "decode_image.c" - "pretty_effect.c" - "spi_master_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - - -set(COMPONENT_EMBED_FILES image.jpg) - -register_component() +idf_component_register(SRCS "decode_image.c" + "pretty_effect.c" + "spi_master_example_main.c" + INCLUDE_DIRS "." + EMBED_FILES image.jpg) \ No newline at end of file diff --git a/examples/peripherals/spi_slave/receiver/main/CMakeLists.txt b/examples/peripherals/spi_slave/receiver/main/CMakeLists.txt index 6b03500639..e7392559ee 100644 --- a/examples/peripherals/spi_slave/receiver/main/CMakeLists.txt +++ b/examples/peripherals/spi_slave/receiver/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "app_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "app_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/peripherals/spi_slave/sender/main/CMakeLists.txt b/examples/peripherals/spi_slave/sender/main/CMakeLists.txt index 6b03500639..e7392559ee 100644 --- a/examples/peripherals/spi_slave/sender/main/CMakeLists.txt +++ b/examples/peripherals/spi_slave/sender/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "app_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "app_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/peripherals/timer_group/main/CMakeLists.txt b/examples/peripherals/timer_group/main/CMakeLists.txt index 9a96ce9068..9a07f9eb72 100644 --- a/examples/peripherals/timer_group/main/CMakeLists.txt +++ b/examples/peripherals/timer_group/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "timer_group_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "timer_group_example_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/peripherals/touch_pad_interrupt/main/CMakeLists.txt b/examples/peripherals/touch_pad_interrupt/main/CMakeLists.txt index b77bf9c7be..df4240c344 100644 --- a/examples/peripherals/touch_pad_interrupt/main/CMakeLists.txt +++ b/examples/peripherals/touch_pad_interrupt/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "tp_interrupt_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "tp_interrupt_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/peripherals/touch_pad_read/main/CMakeLists.txt b/examples/peripherals/touch_pad_read/main/CMakeLists.txt index b556e8c0ee..dad0c4905c 100644 --- a/examples/peripherals/touch_pad_read/main/CMakeLists.txt +++ b/examples/peripherals/touch_pad_read/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "tp_read_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "tp_read_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/peripherals/uart/nmea0183_parser/main/CMakeLists.txt b/examples/peripherals/uart/nmea0183_parser/main/CMakeLists.txt index a8c42b8a71..b06050f468 100644 --- a/examples/peripherals/uart/nmea0183_parser/main/CMakeLists.txt +++ b/examples/peripherals/uart/nmea0183_parser/main/CMakeLists.txt @@ -1,5 +1,3 @@ -set(COMPONENT_SRCS "nmea_parser_example_main.c" - "nmea_parser.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "nmea_parser_example_main.c" + "nmea_parser.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/peripherals/uart/uart_async_rxtxtasks/main/CMakeLists.txt b/examples/peripherals/uart/uart_async_rxtxtasks/main/CMakeLists.txt index b572323523..7cfd9876ee 100644 --- a/examples/peripherals/uart/uart_async_rxtxtasks/main/CMakeLists.txt +++ b/examples/peripherals/uart/uart_async_rxtxtasks/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "uart_async_rxtxtasks_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "uart_async_rxtxtasks_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/peripherals/uart/uart_echo/main/CMakeLists.txt b/examples/peripherals/uart/uart_echo/main/CMakeLists.txt index 1ca803f131..b3eac5eff9 100644 --- a/examples/peripherals/uart/uart_echo/main/CMakeLists.txt +++ b/examples/peripherals/uart/uart_echo/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "uart_echo_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "uart_echo_example_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/peripherals/uart/uart_echo_rs485/main/CMakeLists.txt b/examples/peripherals/uart/uart_echo_rs485/main/CMakeLists.txt index 8b3393049e..ef96bc63f1 100644 --- a/examples/peripherals/uart/uart_echo_rs485/main/CMakeLists.txt +++ b/examples/peripherals/uart/uart_echo_rs485/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "rs485_example.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "rs485_example.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/peripherals/uart/uart_events/main/CMakeLists.txt b/examples/peripherals/uart/uart_events/main/CMakeLists.txt index 5a408ef1ef..44d8838503 100644 --- a/examples/peripherals/uart/uart_events/main/CMakeLists.txt +++ b/examples/peripherals/uart/uart_events/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "uart_events_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "uart_events_example_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/peripherals/uart/uart_select/main/CMakeLists.txt b/examples/peripherals/uart/uart_select/main/CMakeLists.txt index 4e3ae00def..8f1897f23f 100644 --- a/examples/peripherals/uart/uart_select/main/CMakeLists.txt +++ b/examples/peripherals/uart/uart_select/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "uart_select_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "uart_select_example_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/protocols/asio/chat_client/main/CMakeLists.txt b/examples/protocols/asio/chat_client/main/CMakeLists.txt index 25419de4a8..d92c9a8ae9 100644 --- a/examples/protocols/asio/chat_client/main/CMakeLists.txt +++ b/examples/protocols/asio/chat_client/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "chat_client.cpp") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "chat_client.cpp" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/protocols/asio/chat_server/main/CMakeLists.txt b/examples/protocols/asio/chat_server/main/CMakeLists.txt index 7e44a9039f..9042b223a6 100644 --- a/examples/protocols/asio/chat_server/main/CMakeLists.txt +++ b/examples/protocols/asio/chat_server/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "chat_server.cpp") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "chat_server.cpp" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/protocols/asio/tcp_echo_server/main/CMakeLists.txt b/examples/protocols/asio/tcp_echo_server/main/CMakeLists.txt index 04c3c94d9a..ef518dc723 100644 --- a/examples/protocols/asio/tcp_echo_server/main/CMakeLists.txt +++ b/examples/protocols/asio/tcp_echo_server/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "echo_server.cpp") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "echo_server.cpp" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/protocols/asio/udp_echo_server/main/CMakeLists.txt b/examples/protocols/asio/udp_echo_server/main/CMakeLists.txt index cd24937bef..c7a9e3575a 100644 --- a/examples/protocols/asio/udp_echo_server/main/CMakeLists.txt +++ b/examples/protocols/asio/udp_echo_server/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "udp_echo_server.cpp") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "udp_echo_server.cpp" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/protocols/coap_client/main/CMakeLists.txt b/examples/protocols/coap_client/main/CMakeLists.txt index eb1a1db15d..90a88c9d3b 100644 --- a/examples/protocols/coap_client/main/CMakeLists.txt +++ b/examples/protocols/coap_client/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "coap_client_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "coap_client_example_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/protocols/coap_server/main/CMakeLists.txt b/examples/protocols/coap_server/main/CMakeLists.txt index 8650fb1147..c28a7be75e 100644 --- a/examples/protocols/coap_server/main/CMakeLists.txt +++ b/examples/protocols/coap_server/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "coap_server_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "coap_server_example_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/protocols/esp_http_client/main/CMakeLists.txt b/examples/protocols/esp_http_client/main/CMakeLists.txt index 77a52e233a..8cc92e71e8 100644 --- a/examples/protocols/esp_http_client/main/CMakeLists.txt +++ b/examples/protocols/esp_http_client/main/CMakeLists.txt @@ -1,10 +1,6 @@ -set(COMPONENT_SRCS "esp_http_client_example.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - - # Embed the server root certificate into the final binary # # (If this was a component, we would set COMPONENT_EMBED_TXTFILES here.) -set(COMPONENT_EMBED_TXTFILES howsmyssl_com_root_cert.pem) - -register_component() +idf_component_register(SRCS "esp_http_client_example.c" + INCLUDE_DIRS "." + EMBED_TXTFILES howsmyssl_com_root_cert.pem) \ No newline at end of file diff --git a/examples/protocols/http2_request/components/sh2lib/CMakeLists.txt b/examples/protocols/http2_request/components/sh2lib/CMakeLists.txt index ec1b5a1e12..030f3f14c2 100644 --- a/examples/protocols/http2_request/components/sh2lib/CMakeLists.txt +++ b/examples/protocols/http2_request/components/sh2lib/CMakeLists.txt @@ -1,8 +1,4 @@ -set(COMPONENT_ADD_INCLUDEDIRS .) - -set(COMPONENT_SRCS "sh2lib.c") - -set(COMPONENT_REQUIRES nghttp) -set(COMPONENT_PRIV_REQUIRES lwip esp-tls) - -register_component() +idf_component_register(SRCS "sh2lib.c" + INCLUDE_DIRS . + REQUIRES nghttp + PRIV_REQUIRES lwip esp-tls) diff --git a/examples/protocols/http2_request/main/CMakeLists.txt b/examples/protocols/http2_request/main/CMakeLists.txt index 6139002258..defbb29583 100644 --- a/examples/protocols/http2_request/main/CMakeLists.txt +++ b/examples/protocols/http2_request/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "http2_request_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "http2_request_example_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/protocols/http_request/main/CMakeLists.txt b/examples/protocols/http_request/main/CMakeLists.txt index 8e346e8bb3..b4b91e9b87 100644 --- a/examples/protocols/http_request/main/CMakeLists.txt +++ b/examples/protocols/http_request/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "http_request_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "http_request_example_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/protocols/http_server/advanced_tests/main/CMakeLists.txt b/examples/protocols/http_server/advanced_tests/main/CMakeLists.txt index 9fd69b1d35..53a3e4d8b9 100644 --- a/examples/protocols/http_server/advanced_tests/main/CMakeLists.txt +++ b/examples/protocols/http_server/advanced_tests/main/CMakeLists.txt @@ -1,5 +1,3 @@ -set(COMPONENT_SRCS "main.c" - "tests.c") -set(COMPONENT_ADD_INCLUDEDIRS ". include") - -register_component() +idf_component_register(SRCS "main.c" + "tests.c" + INCLUDE_DIRS "." "include") \ No newline at end of file diff --git a/examples/protocols/http_server/file_serving/main/CMakeLists.txt b/examples/protocols/http_server/file_serving/main/CMakeLists.txt index a9493f54a8..2354c4b1be 100644 --- a/examples/protocols/http_server/file_serving/main/CMakeLists.txt +++ b/examples/protocols/http_server/file_serving/main/CMakeLists.txt @@ -1,6 +1,3 @@ -set(COMPONENT_SRCS "main.c" "file_server.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -set(COMPONENT_EMBED_FILES "favicon.ico" "upload_script.html") - -register_component() +idf_component_register(SRCS "main.c" "file_server.c" + INCLUDE_DIRS "." + EMBED_FILES "favicon.ico" "upload_script.html") \ No newline at end of file diff --git a/examples/protocols/http_server/persistent_sockets/main/CMakeLists.txt b/examples/protocols/http_server/persistent_sockets/main/CMakeLists.txt index 85970762ab..e0287b7b9e 100644 --- a/examples/protocols/http_server/persistent_sockets/main/CMakeLists.txt +++ b/examples/protocols/http_server/persistent_sockets/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/protocols/http_server/restful_server/main/CMakeLists.txt b/examples/protocols/http_server/restful_server/main/CMakeLists.txt index 6df9c7c4da..4e35a40f23 100644 --- a/examples/protocols/http_server/restful_server/main/CMakeLists.txt +++ b/examples/protocols/http_server/restful_server/main/CMakeLists.txt @@ -1,7 +1,6 @@ -set(COMPONENT_SRCS "esp_rest_main.c" "rest_server.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "esp_rest_main.c" + "rest_server.c" + INCLUDE_DIRS ".") if(CONFIG_EXAMPLE_WEB_DEPLOY_SF) set(WEB_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../front/web-demo") diff --git a/examples/protocols/http_server/simple/main/CMakeLists.txt b/examples/protocols/http_server/simple/main/CMakeLists.txt index 85970762ab..e0287b7b9e 100644 --- a/examples/protocols/http_server/simple/main/CMakeLists.txt +++ b/examples/protocols/http_server/simple/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/protocols/https_mbedtls/main/CMakeLists.txt b/examples/protocols/https_mbedtls/main/CMakeLists.txt index c9b097c6b9..99d46aba00 100644 --- a/examples/protocols/https_mbedtls/main/CMakeLists.txt +++ b/examples/protocols/https_mbedtls/main/CMakeLists.txt @@ -1,10 +1,6 @@ -set(COMPONENT_SRCS "https_mbedtls_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - - # Embed the server root certificate into the final binary # # (If this was a component, we would set COMPONENT_EMBED_TXTFILES here.) -set(COMPONENT_EMBED_TXTFILES server_root_cert.pem) - -register_component() +idf_component_register(SRCS "https_mbedtls_example_main.c" + INCLUDE_DIRS "." + EMBED_TXTFILES server_root_cert.pem) \ No newline at end of file diff --git a/examples/protocols/https_request/main/CMakeLists.txt b/examples/protocols/https_request/main/CMakeLists.txt index 60a1a0664c..5501a68205 100644 --- a/examples/protocols/https_request/main/CMakeLists.txt +++ b/examples/protocols/https_request/main/CMakeLists.txt @@ -1,10 +1,6 @@ -set(COMPONENT_SRCS "https_request_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - - # Embed the server root certificate into the final binary # # (If this was a component, we would set COMPONENT_EMBED_TXTFILES here.) -set(COMPONENT_EMBED_TXTFILES server_root_cert.pem) - -register_component() +idf_component_register(SRCS "https_request_example_main.c" + INCLUDE_DIRS "." + EMBED_TXTFILES server_root_cert.pem) \ No newline at end of file diff --git a/examples/protocols/https_server/main/CMakeLists.txt b/examples/protocols/https_server/main/CMakeLists.txt index 9e08fb4d71..feca398b8e 100644 --- a/examples/protocols/https_server/main/CMakeLists.txt +++ b/examples/protocols/https_server/main/CMakeLists.txt @@ -1,8 +1,4 @@ -set(COMPONENT_SRCS "main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -set(COMPONENT_EMBED_TXTFILES - "certs/cacert.pem" - "certs/prvtkey.pem") - -register_component() +idf_component_register(SRCS "main.c" + INCLUDE_DIRS "." + EMBED_TXTFILES "certs/cacert.pem" + "certs/prvtkey.pem") \ No newline at end of file diff --git a/examples/protocols/mdns/main/CMakeLists.txt b/examples/protocols/mdns/main/CMakeLists.txt index 814837ea97..141d1e30dc 100644 --- a/examples/protocols/mdns/main/CMakeLists.txt +++ b/examples/protocols/mdns/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "mdns_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "mdns_example_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/protocols/modbus_master/main/CMakeLists.txt b/examples/protocols/modbus_master/main/CMakeLists.txt index 6db1f90987..2555f99e43 100644 --- a/examples/protocols/modbus_master/main/CMakeLists.txt +++ b/examples/protocols/modbus_master/main/CMakeLists.txt @@ -1,6 +1,4 @@ -set(COMPONENT_SRCS "sense_main.c" - "sense_modbus.c" - "device_params.c") -set(COMPONENT_ADD_INCLUDEDIRS "." "include") - -register_component() \ No newline at end of file +idf_component_register(SRCS "sense_main.c" + "sense_modbus.c" + "device_params.c" + INCLUDE_DIRS "." "include") diff --git a/examples/protocols/modbus_slave/main/CMakeLists.txt b/examples/protocols/modbus_slave/main/CMakeLists.txt index 96b08bdc5b..19967e3169 100644 --- a/examples/protocols/modbus_slave/main/CMakeLists.txt +++ b/examples/protocols/modbus_slave/main/CMakeLists.txt @@ -1,5 +1,3 @@ -set(COMPONENT_SRCS "freemodbus.c" - "deviceparams.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() \ No newline at end of file +idf_component_register(SRCS "freemodbus.c" + "deviceparams.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/protocols/mqtt/publish_test/main/CMakeLists.txt b/examples/protocols/mqtt/publish_test/main/CMakeLists.txt index c3074a7067..67c4c7b5ad 100644 --- a/examples/protocols/mqtt/publish_test/main/CMakeLists.txt +++ b/examples/protocols/mqtt/publish_test/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "publish_test.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "publish_test.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/protocols/mqtt/ssl/main/CMakeLists.txt b/examples/protocols/mqtt/ssl/main/CMakeLists.txt index 6b03500639..e7392559ee 100644 --- a/examples/protocols/mqtt/ssl/main/CMakeLists.txt +++ b/examples/protocols/mqtt/ssl/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "app_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "app_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/protocols/mqtt/ssl_mutual_auth/main/CMakeLists.txt b/examples/protocols/mqtt/ssl_mutual_auth/main/CMakeLists.txt index 6b03500639..e7392559ee 100644 --- a/examples/protocols/mqtt/ssl_mutual_auth/main/CMakeLists.txt +++ b/examples/protocols/mqtt/ssl_mutual_auth/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "app_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "app_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/protocols/mqtt/tcp/main/CMakeLists.txt b/examples/protocols/mqtt/tcp/main/CMakeLists.txt index 6b03500639..e7392559ee 100644 --- a/examples/protocols/mqtt/tcp/main/CMakeLists.txt +++ b/examples/protocols/mqtt/tcp/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "app_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "app_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/protocols/mqtt/ws/main/CMakeLists.txt b/examples/protocols/mqtt/ws/main/CMakeLists.txt index 6b03500639..e7392559ee 100644 --- a/examples/protocols/mqtt/ws/main/CMakeLists.txt +++ b/examples/protocols/mqtt/ws/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "app_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "app_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/protocols/mqtt/wss/main/CMakeLists.txt b/examples/protocols/mqtt/wss/main/CMakeLists.txt index 6b03500639..e7392559ee 100644 --- a/examples/protocols/mqtt/wss/main/CMakeLists.txt +++ b/examples/protocols/mqtt/wss/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "app_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "app_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/protocols/openssl_client/main/CMakeLists.txt b/examples/protocols/openssl_client/main/CMakeLists.txt index d8e15c98cd..c7a8b33d91 100644 --- a/examples/protocols/openssl_client/main/CMakeLists.txt +++ b/examples/protocols/openssl_client/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "openssl_client_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "openssl_client_example_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/protocols/openssl_server/main/CMakeLists.txt b/examples/protocols/openssl_server/main/CMakeLists.txt index 07b57ef293..713b0cddf2 100644 --- a/examples/protocols/openssl_server/main/CMakeLists.txt +++ b/examples/protocols/openssl_server/main/CMakeLists.txt @@ -1,8 +1,4 @@ -set(COMPONENT_SRCS "openssl_server_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - - # Embed the certificate & key data directly in the built binary -set(COMPONENT_EMBED_TXTFILES cacert.pem prvtkey.pem) - -register_component() +idf_component_register(SRCS "openssl_server_example_main.c" + INCLUDE_DIRS "." + EMBED_TXTFILES cacert.pem prvtkey.pem) \ No newline at end of file diff --git a/examples/protocols/pppos_client/components/modem/CMakeLists.txt b/examples/protocols/pppos_client/components/modem/CMakeLists.txt index 1b1272ec2f..e1bc2c0a97 100644 --- a/examples/protocols/pppos_client/components/modem/CMakeLists.txt +++ b/examples/protocols/pppos_client/components/modem/CMakeLists.txt @@ -1,12 +1,8 @@ -set(COMPONENT_ADD_INCLUDEDIRS .) +set(srcs "src/esp_modem.c" + "src/esp_modem_dce_service" + "src/sim800.c" + "src/bg96.c") -set(COMPONENT_SRCS "src/esp_modem.c" - "src/esp_modem_dce_service" - "src/sim800.c" - "src/bg96.c") - -set(COMPONENT_ADD_INCLUDEDIRS "include") - -set(COMPONENT_REQUIRES driver) - -register_component() +idf_component_register(SRCS "${srcs}" + INCLUDE_DIRS include + REQUIRES driver) \ No newline at end of file diff --git a/examples/protocols/pppos_client/main/CMakeLists.txt b/examples/protocols/pppos_client/main/CMakeLists.txt index e4ef1373fa..048a7246cd 100644 --- a/examples/protocols/pppos_client/main/CMakeLists.txt +++ b/examples/protocols/pppos_client/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "pppos_client_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "pppos_client_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/protocols/sntp/main/CMakeLists.txt b/examples/protocols/sntp/main/CMakeLists.txt index c7c035ca03..e06a3359f6 100644 --- a/examples/protocols/sntp/main/CMakeLists.txt +++ b/examples/protocols/sntp/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "sntp_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "sntp_example_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/protocols/sockets/tcp_client/main/CMakeLists.txt b/examples/protocols/sockets/tcp_client/main/CMakeLists.txt index be11f2950f..f1688dcb46 100644 --- a/examples/protocols/sockets/tcp_client/main/CMakeLists.txt +++ b/examples/protocols/sockets/tcp_client/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "tcp_client.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "tcp_client.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/protocols/sockets/tcp_server/main/CMakeLists.txt b/examples/protocols/sockets/tcp_server/main/CMakeLists.txt index 844a71574c..1565c7bc30 100644 --- a/examples/protocols/sockets/tcp_server/main/CMakeLists.txt +++ b/examples/protocols/sockets/tcp_server/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "tcp_server.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "tcp_server.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/protocols/sockets/udp_client/main/CMakeLists.txt b/examples/protocols/sockets/udp_client/main/CMakeLists.txt index aadad8c208..adc109a104 100644 --- a/examples/protocols/sockets/udp_client/main/CMakeLists.txt +++ b/examples/protocols/sockets/udp_client/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "udp_client.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "udp_client.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/protocols/sockets/udp_multicast/main/CMakeLists.txt b/examples/protocols/sockets/udp_multicast/main/CMakeLists.txt index d3caeb6ed7..a8b478a68f 100644 --- a/examples/protocols/sockets/udp_multicast/main/CMakeLists.txt +++ b/examples/protocols/sockets/udp_multicast/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "udp_multicast_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "udp_multicast_example_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/protocols/sockets/udp_server/main/CMakeLists.txt b/examples/protocols/sockets/udp_server/main/CMakeLists.txt index 55a6bebdc2..ceb9d65fa5 100644 --- a/examples/protocols/sockets/udp_server/main/CMakeLists.txt +++ b/examples/protocols/sockets/udp_server/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "udp_server.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "udp_server.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/protocols/websocket/main/CMakeLists.txt b/examples/protocols/websocket/main/CMakeLists.txt index caf642155c..bff26f1088 100644 --- a/examples/protocols/websocket/main/CMakeLists.txt +++ b/examples/protocols/websocket/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "websocket_example.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "websocket_example.c" + INCLUDE_DIRS ".") diff --git a/examples/provisioning/ble_prov/main/CMakeLists.txt b/examples/provisioning/ble_prov/main/CMakeLists.txt index 3c44a1a36a..a483b3871f 100644 --- a/examples/provisioning/ble_prov/main/CMakeLists.txt +++ b/examples/provisioning/ble_prov/main/CMakeLists.txt @@ -1,6 +1,4 @@ -set(COMPONENT_SRCS "app_main.c" - "app_prov.c" - "app_prov_handlers.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "app_main.c" + "app_prov.c" + "app_prov_handlers.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/provisioning/console_prov/main/CMakeLists.txt b/examples/provisioning/console_prov/main/CMakeLists.txt index 3c44a1a36a..a483b3871f 100644 --- a/examples/provisioning/console_prov/main/CMakeLists.txt +++ b/examples/provisioning/console_prov/main/CMakeLists.txt @@ -1,6 +1,4 @@ -set(COMPONENT_SRCS "app_main.c" - "app_prov.c" - "app_prov_handlers.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "app_main.c" + "app_prov.c" + "app_prov_handlers.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/provisioning/custom_config/components/custom_provisioning/CMakeLists.txt b/examples/provisioning/custom_config/components/custom_provisioning/CMakeLists.txt index 4e0f61c125..bcffb88bc6 100644 --- a/examples/provisioning/custom_config/components/custom_provisioning/CMakeLists.txt +++ b/examples/provisioning/custom_config/components/custom_provisioning/CMakeLists.txt @@ -1,8 +1,5 @@ -set(COMPONENT_ADD_INCLUDEDIRS include) -set(COMPONENT_PRIV_INCLUDEDIRS proto-c) -set(COMPONENT_SRCS "src/custom_config.c" - "proto-c/custom_config.pb-c.c") - -set(COMPONENT_PRIV_REQUIRES protobuf-c) - -register_component() +idf_component_register(SRCS "src/custom_config.c" + "proto-c/custom_config.pb-c.c" + INCLUDE_DIRS include + PRIV_INCLUDE_DIRS proto-c + PRIV_REQUIRES protobuf-c) \ No newline at end of file diff --git a/examples/provisioning/custom_config/main/CMakeLists.txt b/examples/provisioning/custom_config/main/CMakeLists.txt index 3c44a1a36a..a483b3871f 100644 --- a/examples/provisioning/custom_config/main/CMakeLists.txt +++ b/examples/provisioning/custom_config/main/CMakeLists.txt @@ -1,6 +1,4 @@ -set(COMPONENT_SRCS "app_main.c" - "app_prov.c" - "app_prov_handlers.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "app_main.c" + "app_prov.c" + "app_prov_handlers.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/provisioning/manager/main/CMakeLists.txt b/examples/provisioning/manager/main/CMakeLists.txt index 6b03500639..61fac40e63 100644 --- a/examples/provisioning/manager/main/CMakeLists.txt +++ b/examples/provisioning/manager/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "app_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "app_main.c" + INCLUDE_DIRS ".") diff --git a/examples/provisioning/softap_prov/main/CMakeLists.txt b/examples/provisioning/softap_prov/main/CMakeLists.txt index 3c44a1a36a..a483b3871f 100644 --- a/examples/provisioning/softap_prov/main/CMakeLists.txt +++ b/examples/provisioning/softap_prov/main/CMakeLists.txt @@ -1,6 +1,4 @@ -set(COMPONENT_SRCS "app_main.c" - "app_prov.c" - "app_prov_handlers.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "app_main.c" + "app_prov.c" + "app_prov_handlers.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/storage/nvs_rw_blob/main/CMakeLists.txt b/examples/storage/nvs_rw_blob/main/CMakeLists.txt index 675c11a0e9..a267ebda53 100644 --- a/examples/storage/nvs_rw_blob/main/CMakeLists.txt +++ b/examples/storage/nvs_rw_blob/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "nvs_blob_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "nvs_blob_example_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/storage/nvs_rw_value/main/CMakeLists.txt b/examples/storage/nvs_rw_value/main/CMakeLists.txt index 1383460018..6690649ca0 100644 --- a/examples/storage/nvs_rw_value/main/CMakeLists.txt +++ b/examples/storage/nvs_rw_value/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "nvs_value_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "nvs_value_example_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/storage/partition_api/partition_find/main/CMakeLists.txt b/examples/storage/partition_api/partition_find/main/CMakeLists.txt index 695e65e746..b2d392b5d4 100644 --- a/examples/storage/partition_api/partition_find/main/CMakeLists.txt +++ b/examples/storage/partition_api/partition_find/main/CMakeLists.txt @@ -1,6 +1,3 @@ -set(COMPONENT_SRCS "main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -set(COMPONENT_EMBED_TXTFILES ../partitions_example.csv) - -register_component() +idf_component_register(SRCS "main.c" + INCLUDE_DIRS "." + EMBED_TXTFILES ../partitions_example.csv) \ No newline at end of file diff --git a/examples/storage/partition_api/partition_mmap/main/CMakeLists.txt b/examples/storage/partition_api/partition_mmap/main/CMakeLists.txt index 85970762ab..e0287b7b9e 100644 --- a/examples/storage/partition_api/partition_mmap/main/CMakeLists.txt +++ b/examples/storage/partition_api/partition_mmap/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/storage/partition_api/partition_ops/main/CMakeLists.txt b/examples/storage/partition_api/partition_ops/main/CMakeLists.txt index 85970762ab..e0287b7b9e 100644 --- a/examples/storage/partition_api/partition_ops/main/CMakeLists.txt +++ b/examples/storage/partition_api/partition_ops/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/storage/parttool/main/CMakeLists.txt b/examples/storage/parttool/main/CMakeLists.txt index a574d5ffe6..6056a42f9f 100644 --- a/examples/storage/parttool/main/CMakeLists.txt +++ b/examples/storage/parttool/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "parttool_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() \ No newline at end of file +idf_component_register(SRCS "parttool_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/storage/sd_card/main/CMakeLists.txt b/examples/storage/sd_card/main/CMakeLists.txt index d0a4b53d93..439aa90025 100644 --- a/examples/storage/sd_card/main/CMakeLists.txt +++ b/examples/storage/sd_card/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "sd_card_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "sd_card_example_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/storage/semihost_vfs/main/CMakeLists.txt b/examples/storage/semihost_vfs/main/CMakeLists.txt index 34d60ad45e..aabc5a454f 100644 --- a/examples/storage/semihost_vfs/main/CMakeLists.txt +++ b/examples/storage/semihost_vfs/main/CMakeLists.txt @@ -1,4 +1,5 @@ -set(COMPONENT_SRCS "semihost_vfs_example_main.c") +set(COMPONENT_SRCS ) set(COMPONENT_ADD_INCLUDEDIRS ".") -register_component() +idf_component_register(SRCS "semihost_vfs_example_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/storage/spiffs/main/CMakeLists.txt b/examples/storage/spiffs/main/CMakeLists.txt index 8d041d56d1..026db13131 100644 --- a/examples/storage/spiffs/main/CMakeLists.txt +++ b/examples/storage/spiffs/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "spiffs_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "spiffs_example_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/storage/spiffsgen/main/CMakeLists.txt b/examples/storage/spiffsgen/main/CMakeLists.txt index fdae02e50f..6153893c3c 100644 --- a/examples/storage/spiffsgen/main/CMakeLists.txt +++ b/examples/storage/spiffsgen/main/CMakeLists.txt @@ -1,7 +1,5 @@ -set(COMPONENT_SRCS "spiffsgen_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "spiffsgen_example_main.c" + INCLUDE_DIRS ".") # Create a SPIFFS image from the contents of the 'spiffs_image' directory # that fits the partition named 'storage'. FLASH_IN_PROJECT indicates that diff --git a/examples/storage/wear_levelling/main/CMakeLists.txt b/examples/storage/wear_levelling/main/CMakeLists.txt index e344e9a767..c92569631f 100644 --- a/examples/storage/wear_levelling/main/CMakeLists.txt +++ b/examples/storage/wear_levelling/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "wear_levelling_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "wear_levelling_example_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/system/app_trace_to_host/main/CMakeLists.txt b/examples/system/app_trace_to_host/main/CMakeLists.txt index 767689da75..b8adc44f73 100644 --- a/examples/system/app_trace_to_host/main/CMakeLists.txt +++ b/examples/system/app_trace_to_host/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "app_trace_to_host_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "app_trace_to_host_example_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/system/base_mac_address/main/CMakeLists.txt b/examples/system/base_mac_address/main/CMakeLists.txt index 364317db42..ab68da3e75 100644 --- a/examples/system/base_mac_address/main/CMakeLists.txt +++ b/examples/system/base_mac_address/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "base_mac_address_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "base_mac_address_example_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/system/console/components/cmd_nvs/CMakeLists.txt b/examples/system/console/components/cmd_nvs/CMakeLists.txt index 7c2d3c7d04..ab8257f9ca 100644 --- a/examples/system/console/components/cmd_nvs/CMakeLists.txt +++ b/examples/system/console/components/cmd_nvs/CMakeLists.txt @@ -1,7 +1,3 @@ -set(COMPONENT_ADD_INCLUDEDIRS .) - -set(COMPONENT_SRCS "cmd_nvs.c") - -set(COMPONENT_REQUIRES console nvs_flash) - -register_component() +idf_component_register(SRCS "cmd_nvs.c" + INCLUDE_DIRS . + REQUIRES console nvs_flash) \ No newline at end of file diff --git a/examples/system/console/components/cmd_system/CMakeLists.txt b/examples/system/console/components/cmd_system/CMakeLists.txt index b45a40c31b..ff4612b959 100644 --- a/examples/system/console/components/cmd_system/CMakeLists.txt +++ b/examples/system/console/components/cmd_system/CMakeLists.txt @@ -1,7 +1,3 @@ -set(COMPONENT_ADD_INCLUDEDIRS .) - -set(COMPONENT_SRCS "cmd_system.c") - -set(COMPONENT_REQUIRES console spi_flash) - -register_component() +idf_component_register(SRCS "cmd_system.c" + INCLUDE_DIRS . + REQUIRES console spi_flash) \ No newline at end of file diff --git a/examples/system/console/main/CMakeLists.txt b/examples/system/console/main/CMakeLists.txt index b285886390..5afdb190ad 100644 --- a/examples/system/console/main/CMakeLists.txt +++ b/examples/system/console/main/CMakeLists.txt @@ -1,5 +1,3 @@ -set(COMPONENT_SRCS "cmd_wifi.c" - "console_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "cmd_wifi.c" + "console_example_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/system/cpp_exceptions/main/CMakeLists.txt b/examples/system/cpp_exceptions/main/CMakeLists.txt index f735cc5a64..6668d4f291 100644 --- a/examples/system/cpp_exceptions/main/CMakeLists.txt +++ b/examples/system/cpp_exceptions/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "exception_example_main.cpp") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "exception_example_main.cpp" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/system/cpp_pthread/main/CMakeLists.txt b/examples/system/cpp_pthread/main/CMakeLists.txt index f95c5997c7..f4c4d218e0 100644 --- a/examples/system/cpp_pthread/main/CMakeLists.txt +++ b/examples/system/cpp_pthread/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "cpp_pthread.cpp") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "cpp_pthread.cpp" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/system/deep_sleep/main/CMakeLists.txt b/examples/system/deep_sleep/main/CMakeLists.txt index e99ca4de9d..c64fc7ac48 100644 --- a/examples/system/deep_sleep/main/CMakeLists.txt +++ b/examples/system/deep_sleep/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "deep_sleep_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "deep_sleep_example_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/system/esp_event/default_event_loop/main/CMakeLists.txt b/examples/system/esp_event/default_event_loop/main/CMakeLists.txt index 85970762ab..e0287b7b9e 100644 --- a/examples/system/esp_event/default_event_loop/main/CMakeLists.txt +++ b/examples/system/esp_event/default_event_loop/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/system/esp_event/user_event_loops/main/CMakeLists.txt b/examples/system/esp_event/user_event_loops/main/CMakeLists.txt index 85970762ab..e0287b7b9e 100644 --- a/examples/system/esp_event/user_event_loops/main/CMakeLists.txt +++ b/examples/system/esp_event/user_event_loops/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/system/esp_timer/main/CMakeLists.txt b/examples/system/esp_timer/main/CMakeLists.txt index 09db9ec296..eefc6db70d 100644 --- a/examples/system/esp_timer/main/CMakeLists.txt +++ b/examples/system/esp_timer/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "esp_timer_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "esp_timer_example_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/system/freertos/real_time_stats/main/CMakeLists.txt b/examples/system/freertos/real_time_stats/main/CMakeLists.txt index 85970762ab..e0287b7b9e 100644 --- a/examples/system/freertos/real_time_stats/main/CMakeLists.txt +++ b/examples/system/freertos/real_time_stats/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/system/gcov/main/CMakeLists.txt b/examples/system/gcov/main/CMakeLists.txt index 8c9cbd3eaf..0da58c5e1e 100644 --- a/examples/system/gcov/main/CMakeLists.txt +++ b/examples/system/gcov/main/CMakeLists.txt @@ -1,8 +1,6 @@ -set(COMPONENT_SRCS "gcov_example.c" - "gcov_example_func.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "gcov_example.c" + "gcov_example_func.c" + INCLUDE_DIRS ".") set_source_files_properties(gcov_example.c PROPERTIES COMPILE_FLAGS diff --git a/examples/system/himem/main/CMakeLists.txt b/examples/system/himem/main/CMakeLists.txt index a875dec50c..780e49a35c 100644 --- a/examples/system/himem/main/CMakeLists.txt +++ b/examples/system/himem/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "himem_test_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "himem_test_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/system/light_sleep/main/CMakeLists.txt b/examples/system/light_sleep/main/CMakeLists.txt index fc813fb9ea..36b6c93cc1 100644 --- a/examples/system/light_sleep/main/CMakeLists.txt +++ b/examples/system/light_sleep/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "light_sleep_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "light_sleep_example_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/system/network_tests/main/CMakeLists.txt b/examples/system/network_tests/main/CMakeLists.txt index c16f42adb9..f379bf03a2 100644 --- a/examples/system/network_tests/main/CMakeLists.txt +++ b/examples/system/network_tests/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "net_suite.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "net_suite.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/system/ota/advanced_https_ota/main/CMakeLists.txt b/examples/system/ota/advanced_https_ota/main/CMakeLists.txt index 19abec788c..f24c785feb 100644 --- a/examples/system/ota/advanced_https_ota/main/CMakeLists.txt +++ b/examples/system/ota/advanced_https_ota/main/CMakeLists.txt @@ -1,9 +1,4 @@ -set(COMPONENT_SRCS "advanced_https_ota_example.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - - -# Embed the server root certificate into the final binary -idf_build_get_property(project_dir PROJECT_DIR) -set(COMPONENT_EMBED_TXTFILES ${project_dir}/server_certs/ca_cert.pem) - -register_component() +idf_component_register(SRCS "advanced_https_ota_example.c" + INCLUDE_DIRS "." + # Embed the server root certificate into the final binary + EMBED_TXTFILES ${project_dir}/server_certs/ca_cert.pem) diff --git a/examples/system/ota/native_ota_example/main/CMakeLists.txt b/examples/system/ota/native_ota_example/main/CMakeLists.txt index 1e5d1ed0f5..c09881022c 100644 --- a/examples/system/ota/native_ota_example/main/CMakeLists.txt +++ b/examples/system/ota/native_ota_example/main/CMakeLists.txt @@ -1,8 +1,5 @@ -set(COMPONENT_SRCS "native_ota_example.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - # Embed the server root certificate into the final binary idf_build_get_property(project_dir PROJECT_DIR) -set(COMPONENT_EMBED_TXTFILES ${project_dir}/server_certs/ca_cert.pem) - -register_component() +idf_component_register(SRCS "native_ota_example.c" + INCLUDE_DIRS "." + EMBED_TXTFILES ${project_dir}/server_certs/ca_cert.pem) \ No newline at end of file diff --git a/examples/system/ota/otatool/main/CMakeLists.txt b/examples/system/ota/otatool/main/CMakeLists.txt index 2dc1cb53a0..4c341608ea 100644 --- a/examples/system/ota/otatool/main/CMakeLists.txt +++ b/examples/system/ota/otatool/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "otatool_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "otatool_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/system/ota/simple_ota_example/main/CMakeLists.txt b/examples/system/ota/simple_ota_example/main/CMakeLists.txt index c5db485eff..f8c5c183cf 100644 --- a/examples/system/ota/simple_ota_example/main/CMakeLists.txt +++ b/examples/system/ota/simple_ota_example/main/CMakeLists.txt @@ -1,9 +1,5 @@ -set(COMPONENT_SRCS "simple_ota_example.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - - # Embed the server root certificate into the final binary idf_build_get_property(project_dir PROJECT_DIR) -set(COMPONENT_EMBED_TXTFILES ${project_dir}/server_certs/ca_cert.pem) - -register_component() +idf_component_register(SRCS "simple_ota_example.c" + INCLUDE_DIRS "." + EMBED_TXTFILES ${project_dir}/server_certs/ca_cert.pem) diff --git a/examples/system/select/main/CMakeLists.txt b/examples/system/select/main/CMakeLists.txt index bf59f84c44..3dd427e874 100644 --- a/examples/system/select/main/CMakeLists.txt +++ b/examples/system/select/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "select_example.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "select_example.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/system/sysview_tracing/main/CMakeLists.txt b/examples/system/sysview_tracing/main/CMakeLists.txt index dfee658c9a..a23c5937bc 100644 --- a/examples/system/sysview_tracing/main/CMakeLists.txt +++ b/examples/system/sysview_tracing/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "sysview_tracing.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "sysview_tracing.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/system/sysview_tracing_heap_log/main/CMakeLists.txt b/examples/system/sysview_tracing_heap_log/main/CMakeLists.txt index 58d592c8b2..689bafd3ba 100644 --- a/examples/system/sysview_tracing_heap_log/main/CMakeLists.txt +++ b/examples/system/sysview_tracing_heap_log/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "sysview_heap_log.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "sysview_heap_log.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/system/task_watchdog/main/CMakeLists.txt b/examples/system/task_watchdog/main/CMakeLists.txt index ef06f3cc55..e805c40ac1 100644 --- a/examples/system/task_watchdog/main/CMakeLists.txt +++ b/examples/system/task_watchdog/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "task_watchdog_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "task_watchdog_example_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/system/ulp/main/CMakeLists.txt b/examples/system/ulp/main/CMakeLists.txt index d426857dc6..af7cb3f765 100644 --- a/examples/system/ulp/main/CMakeLists.txt +++ b/examples/system/ulp/main/CMakeLists.txt @@ -1,10 +1,6 @@ -# Set usual component variables -set(COMPONENT_SRCS "ulp_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS "") -set(COMPONENT_REQUIRES soc nvs_flash ulp) - -register_component() - +idf_component_register(SRCS "ulp_example_main.c" + INCLUDE_DIRS "" + REQUIRES soc nvs_flash ulp) # # ULP support additions to component CMakeLists.txt. # diff --git a/examples/system/ulp_adc/main/CMakeLists.txt b/examples/system/ulp_adc/main/CMakeLists.txt index 5f64763179..ef85f9b2fd 100644 --- a/examples/system/ulp_adc/main/CMakeLists.txt +++ b/examples/system/ulp_adc/main/CMakeLists.txt @@ -1,10 +1,6 @@ -# Set usual component variables -set(COMPONENT_SRCS "ulp_adc_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS "") -set(COMPONENT_REQUIRES soc nvs_flash ulp driver) - -register_component() - +idf_component_register(SRCS "ulp_adc_example_main.c" + INCLUDE_DIRS "" + REQUIRES soc nvs_flash ulp driver) # # ULP support additions to component CMakeLists.txt. # @@ -22,4 +18,4 @@ set(ulp_exp_dep_srcs "ulp_adc_example_main.c") # # 4. Call function to build ULP binary and embed in project using the argument # values above. -ulp_embed_binary(${ulp_app_name} ${ulp_s_sources} ${ulp_exp_dep_srcs}) \ No newline at end of file +ulp_embed_binary(${ulp_app_name} ${ulp_s_sources} ${ulp_exp_dep_srcs}) diff --git a/examples/system/unit_test/components/testable/CMakeLists.txt b/examples/system/unit_test/components/testable/CMakeLists.txt index 1df93d6d88..59adfac9d6 100644 --- a/examples/system/unit_test/components/testable/CMakeLists.txt +++ b/examples/system/unit_test/components/testable/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "mean.c") -set(COMPONENT_ADD_INCLUDEDIRS "include") - -register_component() +idf_component_register(SRCS "mean.c" + INCLUDE_DIRS "include") \ No newline at end of file diff --git a/examples/system/unit_test/components/testable/test/CMakeLists.txt b/examples/system/unit_test/components/testable/test/CMakeLists.txt index bbb5b276da..33dded71ae 100644 --- a/examples/system/unit_test/components/testable/test/CMakeLists.txt +++ b/examples/system/unit_test/components/testable/test/CMakeLists.txt @@ -1,6 +1,3 @@ -set(COMPONENT_SRCDIRS ".") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -set(COMPONENT_REQUIRES unity testable) - -register_component() +idf_component_register(SRC_DIRS "." + INCLUDE_DIRS "." + REQUIRES unity testable) \ No newline at end of file diff --git a/examples/system/unit_test/main/CMakeLists.txt b/examples/system/unit_test/main/CMakeLists.txt index f726378150..0631174b7c 100644 --- a/examples/system/unit_test/main/CMakeLists.txt +++ b/examples/system/unit_test/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "example_unit_test_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "example_unit_test_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/system/unit_test/test/main/CMakeLists.txt b/examples/system/unit_test/test/main/CMakeLists.txt index b849174c77..8a4d168eac 100644 --- a/examples/system/unit_test/test/main/CMakeLists.txt +++ b/examples/system/unit_test/test/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "example_unit_test_test.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "example_unit_test_test.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/wifi/espnow/main/CMakeLists.txt b/examples/wifi/espnow/main/CMakeLists.txt index 151eab724b..c752a271b6 100644 --- a/examples/wifi/espnow/main/CMakeLists.txt +++ b/examples/wifi/espnow/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "espnow_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "espnow_example_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/wifi/getting_started/softAP/main/CMakeLists.txt b/examples/wifi/getting_started/softAP/main/CMakeLists.txt index 6724060715..a89388299c 100644 --- a/examples/wifi/getting_started/softAP/main/CMakeLists.txt +++ b/examples/wifi/getting_started/softAP/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "softap_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "softap_example_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/wifi/getting_started/station/main/CMakeLists.txt b/examples/wifi/getting_started/station/main/CMakeLists.txt index 3cd1031283..444b0f59f1 100644 --- a/examples/wifi/getting_started/station/main/CMakeLists.txt +++ b/examples/wifi/getting_started/station/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "station_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "station_example_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/wifi/iperf/components/iperf/CMakeLists.txt b/examples/wifi/iperf/components/iperf/CMakeLists.txt index 11a8f571e2..29cca1e3ec 100644 --- a/examples/wifi/iperf/components/iperf/CMakeLists.txt +++ b/examples/wifi/iperf/components/iperf/CMakeLists.txt @@ -1,7 +1,3 @@ -set(COMPONENT_ADD_INCLUDEDIRS .) - -set(COMPONENT_SRCS "iperf.c") - -set(COMPONENT_REQUIRES lwip) - -register_component() +idf_component_register(SRCS "iperf.c" + INCLUDE_DIRS . + REQUIRES lwip) \ No newline at end of file diff --git a/examples/wifi/iperf/main/CMakeLists.txt b/examples/wifi/iperf/main/CMakeLists.txt index e94ff94b07..978cc99e0e 100644 --- a/examples/wifi/iperf/main/CMakeLists.txt +++ b/examples/wifi/iperf/main/CMakeLists.txt @@ -1,5 +1,3 @@ -set(COMPONENT_SRCS "cmd_wifi.c" - "iperf_example_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "cmd_wifi.c" + "iperf_example_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/wifi/power_save/main/CMakeLists.txt b/examples/wifi/power_save/main/CMakeLists.txt index f7fc34d94a..e02dc88c90 100644 --- a/examples/wifi/power_save/main/CMakeLists.txt +++ b/examples/wifi/power_save/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "power_save.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "power_save.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/wifi/scan/main/CMakeLists.txt b/examples/wifi/scan/main/CMakeLists.txt index 0b789863af..9719433b43 100644 --- a/examples/wifi/scan/main/CMakeLists.txt +++ b/examples/wifi/scan/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "scan.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "scan.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/wifi/simple_sniffer/components/pcap/CMakeLists.txt b/examples/wifi/simple_sniffer/components/pcap/CMakeLists.txt index 15f5b5da63..ca59591e53 100644 --- a/examples/wifi/simple_sniffer/components/pcap/CMakeLists.txt +++ b/examples/wifi/simple_sniffer/components/pcap/CMakeLists.txt @@ -1,5 +1,2 @@ -set(COMPONENT_ADD_INCLUDEDIRS .) - -set(COMPONENT_SRCS "pcap.c") - -register_component() +idf_component_register(SRCS "pcap.c" + INCLUDE_DIRS .) \ No newline at end of file diff --git a/examples/wifi/simple_sniffer/main/CMakeLists.txt b/examples/wifi/simple_sniffer/main/CMakeLists.txt index 8e95d42fc3..9e14ddef0d 100644 --- a/examples/wifi/simple_sniffer/main/CMakeLists.txt +++ b/examples/wifi/simple_sniffer/main/CMakeLists.txt @@ -1,10 +1,3 @@ - -# The following lines of boilerplate have to be in your project's CMakeLists -# in this exact order for cmake to work correctly -cmake_minimum_required(VERSION 3.5) - -set(COMPONENT_SRCS "simple_sniffer_example_main.c" - "cmd_sniffer.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "simple_sniffer_example_main.c" + "cmd_sniffer.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/wifi/smart_config/main/CMakeLists.txt b/examples/wifi/smart_config/main/CMakeLists.txt index a716681e47..5f956d6660 100644 --- a/examples/wifi/smart_config/main/CMakeLists.txt +++ b/examples/wifi/smart_config/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "smartconfig_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "smartconfig_main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/wifi/wpa2_enterprise/main/CMakeLists.txt b/examples/wifi/wpa2_enterprise/main/CMakeLists.txt index 10640a32db..1c847a1f62 100644 --- a/examples/wifi/wpa2_enterprise/main/CMakeLists.txt +++ b/examples/wifi/wpa2_enterprise/main/CMakeLists.txt @@ -1,8 +1,4 @@ -set(COMPONENT_SRCS "wpa2_enterprise_main.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - - # Embed CA, certificate & key directly into binary -set(COMPONENT_EMBED_TXTFILES wpa2_ca.pem wpa2_client.crt wpa2_client.key) - -register_component() +idf_component_register(SRCS "wpa2_enterprise_main.c" + INCLUDE_DIRS "." + EMBED_TXTFILES wpa2_ca.pem wpa2_client.crt wpa2_client.key) \ No newline at end of file diff --git a/examples/wifi/wps/main/CMakeLists.txt b/examples/wifi/wps/main/CMakeLists.txt index c0a1063f66..5ca4a9211f 100644 --- a/examples/wifi/wps/main/CMakeLists.txt +++ b/examples/wifi/wps/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "wps.c") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -register_component() +idf_component_register(SRCS "wps.c" + INCLUDE_DIRS ".") \ No newline at end of file From e9bc46db71ce4d85bd8b8413284df2b8fbea57ae Mon Sep 17 00:00:00 2001 From: Renz Christian Bagaporo Date: Sun, 28 Apr 2019 15:39:02 +0800 Subject: [PATCH 112/486] tools: use new component registration api for unit test app --- .../components/test_utils/CMakeLists.txt | 13 +++++-------- tools/unit-test-app/main/CMakeLists.txt | 6 ++---- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/tools/unit-test-app/components/test_utils/CMakeLists.txt b/tools/unit-test-app/components/test_utils/CMakeLists.txt index 50efbdbbb7..7932a10a68 100644 --- a/tools/unit-test-app/components/test_utils/CMakeLists.txt +++ b/tools/unit-test-app/components/test_utils/CMakeLists.txt @@ -1,9 +1,6 @@ -set(COMPONENT_SRCS "ref_clock.c" - "test_runner.c" - "test_utils.c") -set(COMPONENT_ADD_INCLUDEDIRS include) - -set(COMPONENT_REQUIRES spi_flash idf_test unity) - -register_component() +idf_component_register(SRCS "ref_clock.c" + "test_runner.c" + "test_utils.c" + INCLUDE_DIRS include + REQUIRES spi_flash idf_test unity) diff --git a/tools/unit-test-app/main/CMakeLists.txt b/tools/unit-test-app/main/CMakeLists.txt index 47f681d36a..95f9935aa9 100644 --- a/tools/unit-test-app/main/CMakeLists.txt +++ b/tools/unit-test-app/main/CMakeLists.txt @@ -1,4 +1,2 @@ -set(COMPONENT_SRCS "app_main.c") -set(COMPONENT_ADD_INCLUDEDIRS "") - -register_component() +idf_component_register(SRCS "app_main.c" + INCLUDE_DIRS "") \ No newline at end of file From 047cf71c01189115766cb9eba79762a53f36f6d8 Mon Sep 17 00:00:00 2001 From: Renz Christian Bagaporo Date: Thu, 30 May 2019 14:17:10 +0800 Subject: [PATCH 113/486] tools: update make converter to use new component registration api --- tools/cmake/convert_to_cmake.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/tools/cmake/convert_to_cmake.py b/tools/cmake/convert_to_cmake.py index 80d9296d62..0ba7fa62f5 100755 --- a/tools/cmake/convert_to_cmake.py +++ b/tools/cmake/convert_to_cmake.py @@ -161,17 +161,14 @@ def convert_component(project_path, component_path): cflags = v.get("CFLAGS", None) with open(cmakelists_path, "w") as f: - f.write("set(COMPONENT_ADD_INCLUDEDIRS %s)\n\n" % component_add_includedirs) - - f.write("# Edit following two lines to set component requirements (see docs)\n") - f.write("set(COMPONENT_REQUIRES "")\n") - f.write("set(COMPONENT_PRIV_REQUIRES "")\n\n") - if component_srcs is not None: - f.write("set(COMPONENT_SRCS %s)\n\n" % component_srcs) - f.write("register_component()\n") + f.write("idf_component_register(SRCS %s)\n" % component_srcs) + f.write(" INCLUDE_DIRS %s" % component_add_includedirs) + f.write(" # Edit following two lines to set component requirements (see docs)\n") + f.write(" REQUIRES "")\n") + f.write(" PRIV_REQUIRES "")\n\n") else: - f.write("register_config_only_component()\n") + f.write("idf_component_register()\n") if cflags is not None: f.write("target_compile_options(${COMPONENT_LIB} PRIVATE %s)\n" % cflags) From 9b350f9eccae7c4d56008317f1b8982f9f492eea Mon Sep 17 00:00:00 2001 From: Renz Christian Bagaporo Date: Fri, 21 Jun 2019 14:29:32 +0800 Subject: [PATCH 114/486] cmake: some formatting fixes Do not include bootloader in flash target when secure boot is enabled. Emit signing warning on all cases where signed apps are enabled (secure boot and signed images) Follow convention of capital letters for SECURE_BOOT_SIGNING_KEY variable, since it is relevant to other components, not just bootloader. Pass signing key and verification key via config, not requiring bootloader to know parent app dir. Misc. variables name corrections --- components/app_trace/CMakeLists.txt | 23 +- components/app_update/CMakeLists.txt | 3 +- components/bootloader_support/CMakeLists.txt | 33 +-- components/coap/CMakeLists.txt | 43 ++-- components/console/CMakeLists.txt | 10 +- components/driver/CMakeLists.txt | 3 +- components/efuse/CMakeLists.txt | 7 +- components/esp32/CMakeLists.txt | 49 ++-- components/esp_http_server/CMakeLists.txt | 4 +- components/esp_wifi/CMakeLists.txt | 19 +- components/espcoredump/CMakeLists.txt | 8 +- components/fatfs/CMakeLists.txt | 22 +- components/freemodbus/CMakeLists.txt | 3 +- components/freertos/CMakeLists.txt | 34 +-- components/heap/CMakeLists.txt | 7 +- components/libsodium/CMakeLists.txt | 237 ++++++++++--------- components/lwip/CMakeLists.txt | 182 +++++++------- components/newlib/CMakeLists.txt | 3 +- components/nghttp/CMakeLists.txt | 47 ++-- components/protocomm/CMakeLists.txt | 19 +- components/soc/CMakeLists.txt | 19 +- components/spi_flash/CMakeLists.txt | 24 +- components/unity/CMakeLists.txt | 5 +- components/wpa_supplicant/CMakeLists.txt | 149 ++++++------ components/xtensa/CMakeLists.txt | 5 +- 25 files changed, 491 insertions(+), 467 deletions(-) diff --git a/components/app_trace/CMakeLists.txt b/components/app_trace/CMakeLists.txt index 5276fa01ed..b9bcdddccc 100644 --- a/components/app_trace/CMakeLists.txt +++ b/components/app_trace/CMakeLists.txt @@ -1,7 +1,9 @@ -set(srcs "app_trace.c" - "app_trace_util.c" - "host_file_io.c" - "gcov/gcov_rtio.c") +set(srcs + "app_trace.c" + "app_trace_util.c" + "host_file_io.c" + "gcov/gcov_rtio.c") + set(include_dirs "include") if(CONFIG_SYSVIEW_ENABLE) @@ -10,12 +12,13 @@ if(CONFIG_SYSVIEW_ENABLE) sys_view/SEGGER sys_view/Sample/OS) - list(APPEND srcs "sys_view/SEGGER/SEGGER_SYSVIEW.c" - "sys_view/Sample/Config/SEGGER_SYSVIEW_Config_FreeRTOS.c" - "sys_view/Sample/OS/SEGGER_SYSVIEW_FreeRTOS.c" - "sys_view/esp32/SEGGER_RTT_esp32.c" - "sys_view/ext/heap_trace_module.c" - "sys_view/ext/logging.c") + list(APPEND srcs + "sys_view/SEGGER/SEGGER_SYSVIEW.c" + "sys_view/Sample/Config/SEGGER_SYSVIEW_Config_FreeRTOS.c" + "sys_view/Sample/OS/SEGGER_SYSVIEW_FreeRTOS.c" + "sys_view/esp32/SEGGER_RTT_esp32.c" + "sys_view/ext/heap_trace_module.c" + "sys_view/ext/logging.c") endif() if(CONFIG_HEAP_TRACING_TOHOST) diff --git a/components/app_update/CMakeLists.txt b/components/app_update/CMakeLists.txt index acb02accb2..af07c24042 100644 --- a/components/app_update/CMakeLists.txt +++ b/components/app_update/CMakeLists.txt @@ -1,4 +1,5 @@ -idf_component_register(SRCS "esp_ota_ops.c" "esp_app_desc.c" +idf_component_register(SRCS "esp_ota_ops.c" + "esp_app_desc.c" INCLUDE_DIRS "include" REQUIRES spi_flash partition_table bootloader_support) diff --git a/components/bootloader_support/CMakeLists.txt b/components/bootloader_support/CMakeLists.txt index a8db5c4873..d40b8d1866 100644 --- a/components/bootloader_support/CMakeLists.txt +++ b/components/bootloader_support/CMakeLists.txt @@ -1,21 +1,23 @@ -set(srcs "src/bootloader_clock.c" - "src/bootloader_common.c" - "src/bootloader_flash.c" - "src/bootloader_random.c" - "src/bootloader_utility.c" - "src/esp_image_format.c" - "src/flash_partitions.c" - "src/flash_qio_mode.c") +set(srcs + "src/bootloader_clock.c" + "src/bootloader_common.c" + "src/bootloader_flash.c" + "src/bootloader_random.c" + "src/bootloader_utility.c" + "src/esp_image_format.c" + "src/flash_partitions.c" + "src/flash_qio_mode.c") if(BOOTLOADER_BUILD) set(include_dirs "include" "include_bootloader") set(requires soc) #unfortunately the header directly uses SOC registers set(priv_requires micro-ecc spi_flash efuse) - list(APPEND srcs "src/bootloader_init.c" - "src/${IDF_TARGET}/bootloader_sha.c" - "src/${IDF_TARGET}/flash_encrypt.c" - "src/${IDF_TARGET}/secure_boot_signatures.c" - "src/${IDF_TARGET}/secure_boot.c") + list(APPEND srcs + "src/bootloader_init.c" + "src/${IDF_TARGET}/bootloader_sha.c" + "src/${IDF_TARGET}/flash_encrypt.c" + "src/${IDF_TARGET}/secure_boot_signatures.c" + "src/${IDF_TARGET}/secure_boot.c") if(CONFIG_SECURE_SIGNED_APPS) get_filename_component(secure_boot_verification_key @@ -51,8 +53,9 @@ if(BOOTLOADER_BUILD) "${secure_boot_verification_key}") endif() else() - list(APPEND srcs "src/idf/bootloader_sha.c" - "src/idf/secure_boot_signatures.c") + list(APPEND srcs + "src/idf/bootloader_sha.c" + "src/idf/secure_boot_signatures.c") set(include_dirs "include") set(priv_include_dirs "include_bootloader") set(requires soc) #unfortunately the header directly uses SOC registers diff --git a/components/coap/CMakeLists.txt b/components/coap/CMakeLists.txt index 78015bb056..aef9d31682 100644 --- a/components/coap/CMakeLists.txt +++ b/components/coap/CMakeLists.txt @@ -1,30 +1,31 @@ set(include_dirs port/include port/include/coap libcoap/include libcoap/include/coap2) -set(srcs "libcoap/src/address.c" - "libcoap/src/async.c" - "libcoap/src/block.c" - "libcoap/src/coap_event.c" - "libcoap/src/coap_hashkey.c" - "libcoap/src/coap_session.c" - "libcoap/src/coap_time.c" - "libcoap/src/coap_debug.c" - "libcoap/src/encode.c" - "libcoap/src/mem.c" - "libcoap/src/net.c" - "libcoap/src/option.c" - "libcoap/src/pdu.c" - "libcoap/src/resource.c" - "libcoap/src/str.c" - "libcoap/src/subscribe.c" - "libcoap/src/uri.c" - "libcoap/src/coap_notls.c" - "port/coap_io.c") +set(srcs + "libcoap/src/address.c" + "libcoap/src/async.c" + "libcoap/src/block.c" + "libcoap/src/coap_event.c" + "libcoap/src/coap_hashkey.c" + "libcoap/src/coap_session.c" + "libcoap/src/coap_time.c" + "libcoap/src/coap_debug.c" + "libcoap/src/encode.c" + "libcoap/src/mem.c" + "libcoap/src/net.c" + "libcoap/src/option.c" + "libcoap/src/pdu.c" + "libcoap/src/resource.c" + "libcoap/src/str.c" + "libcoap/src/subscribe.c" + "libcoap/src/uri.c" + "libcoap/src/coap_notls.c" + "port/coap_io.c") set(COMPONENT_REQUIRES lwip) idf_component_register(SRCS "${srcs}" - INCLUDE_DIRS "${include_dirs}" - REQUIRES lwip) + INCLUDE_DIRS "${include_dirs}" + REQUIRES lwip) # Silence format truncation warning, until it is fixed upstream set_source_files_properties(libcoap/src/coap_debug.c PROPERTIES COMPILE_FLAGS -Wno-format-truncation) diff --git a/components/console/CMakeLists.txt b/components/console/CMakeLists.txt index 244834c224..cb20200cd6 100644 --- a/components/console/CMakeLists.txt +++ b/components/console/CMakeLists.txt @@ -1,6 +1,6 @@ idf_component_register(SRCS "commands.c" - "split_argv.c" - "argtable3/argtable3.c" - "linenoise/linenoise.c" - INCLUDE_DIRS "." - REQUIRES vfs) + "split_argv.c" + "argtable3/argtable3.c" + "linenoise/linenoise.c" + INCLUDE_DIRS "." + REQUIRES vfs) diff --git a/components/driver/CMakeLists.txt b/components/driver/CMakeLists.txt index ddeb98d108..f0509fcf19 100644 --- a/components/driver/CMakeLists.txt +++ b/components/driver/CMakeLists.txt @@ -1,4 +1,5 @@ -set(srcs "can.c" +set(srcs + "can.c" "gpio.c" "i2c.c" "i2s.c" diff --git a/components/efuse/CMakeLists.txt b/components/efuse/CMakeLists.txt index f43a2e6030..9a05e7cc14 100644 --- a/components/efuse/CMakeLists.txt +++ b/components/efuse/CMakeLists.txt @@ -7,9 +7,10 @@ if(EXISTS "${COMPONENT_DIR}/${soc_name}") set(include_dirs include ${soc_name}/include) endif() -list(APPEND srcs "src/esp_efuse_api.c" - "src/esp_efuse_fields.c" - "src/esp_efuse_utility.c") +list(APPEND srcs + "src/esp_efuse_api.c" + "src/esp_efuse_fields.c" + "src/esp_efuse_utility.c") idf_component_register(SRCS "${srcs}" PRIV_REQUIRES bootloader_support soc diff --git a/components/esp32/CMakeLists.txt b/components/esp32/CMakeLists.txt index b69f3747ed..916113b533 100644 --- a/components/esp32/CMakeLists.txt +++ b/components/esp32/CMakeLists.txt @@ -4,30 +4,31 @@ if(BOOTLOADER_BUILD) target_linker_script(${COMPONENT_LIB} INTERFACE "ld/esp32.peripherals.ld") else() # Regular app build - set(srcs "brownout.c" - "cache_err_int.c" - "cache_sram_mmu.c" - "clk.c" - "cpu_start.c" - "crosscore_int.c" - "dport_access.c" - "dport_panic_highint_hdl.S" - "esp_adapter.c" - "esp_timer_esp32.c" - "esp_himem.c" - "gdbstub.c" - "hw_random.c" - "int_wdt.c" - "intr_alloc.c" - "panic.c" - "pm_esp32.c" - "pm_trace.c" - "reset_reason.c" - "sleep_modes.c" - "spiram.c" - "spiram_psram.c" - "system_api.c" - "task_wdt.c") + set(srcs + "brownout.c" + "cache_err_int.c" + "cache_sram_mmu.c" + "clk.c" + "cpu_start.c" + "crosscore_int.c" + "dport_access.c" + "dport_panic_highint_hdl.S" + "esp_adapter.c" + "esp_timer_esp32.c" + "esp_himem.c" + "gdbstub.c" + "hw_random.c" + "int_wdt.c" + "intr_alloc.c" + "panic.c" + "pm_esp32.c" + "pm_trace.c" + "reset_reason.c" + "sleep_modes.c" + "spiram.c" + "spiram_psram.c" + "system_api.c" + "task_wdt.c") set(include_dirs "include") set(requires driver esp_event efuse soc) #unfortunately rom/uart uses SOC registers directly diff --git a/components/esp_http_server/CMakeLists.txt b/components/esp_http_server/CMakeLists.txt index 4bf15aa866..744e6b207f 100644 --- a/components/esp_http_server/CMakeLists.txt +++ b/components/esp_http_server/CMakeLists.txt @@ -6,5 +6,5 @@ idf_component_register(SRCS "src/httpd_main.c" "src/util/ctrl_sock.c" INCLUDE_DIRS "include" PRIV_INCLUDE_DIRS "src/port/esp32" "src/util" - REQUIRES nghttp - PRIV_REQUIRES lwip) # for http_parser.h + REQUIRES nghttp # for http_parser.h + PRIV_REQUIRES lwip) diff --git a/components/esp_wifi/CMakeLists.txt b/components/esp_wifi/CMakeLists.txt index 4cf4723b4a..9c7f29759b 100644 --- a/components/esp_wifi/CMakeLists.txt +++ b/components/esp_wifi/CMakeLists.txt @@ -1,24 +1,21 @@ idf_build_get_property(idf_target IDF_TARGET) -idf_build_get_property(build_dir BUILD_DIR) - -set(srcs - "src/coexist.c" - "src/fast_crypto_ops.c" - "src/lib_printf.c" - "src/mesh_event.c" - "src/phy_init.c" - "src/restore.c" - "src/wifi_init.c") if(NOT CONFIG_ESP32_NO_BLOBS) set(ldfragments "linker.lf") endif() -idf_component_register(SRCS "${srcs}" +idf_component_register(SRCS "src/coexist.c" + "src/fast_crypto_ops.c" + "src/lib_printf.c" + "src/mesh_event.c" + "src/phy_init.c" + "src/restore.c" + "src/wifi_init.c" INCLUDE_DIRS "include" "${idf_target}/include" PRIV_REQUIRES wpa_supplicant nvs_flash LDFRAGMENTS "${ldfragments}") +idf_build_get_property(build_dir BUILD_DIR) target_link_libraries(${COMPONENT_LIB} PUBLIC "-L ${CMAKE_CURRENT_SOURCE_DIR}/lib_${idf_target}") if(NOT CONFIG_ESP32_NO_BLOBS) diff --git a/components/espcoredump/CMakeLists.txt b/components/espcoredump/CMakeLists.txt index f925f93db2..aa3b32eeac 100644 --- a/components/espcoredump/CMakeLists.txt +++ b/components/espcoredump/CMakeLists.txt @@ -2,7 +2,7 @@ idf_component_register(SRCS "src/core_dump_common.c" "src/core_dump_flash.c" "src/core_dump_port.c" "src/core_dump_uart.c" - INCLUDE_DIRS "include" - PRIV_INCLUDE_DIRS "include_core_dump" - LDFRAGMENTS linker.lf - PRIV_REQUIRES spi_flash soc) + INCLUDE_DIRS "include" + PRIV_INCLUDE_DIRS "include_core_dump" + LDFRAGMENTS linker.lf + PRIV_REQUIRES spi_flash soc) diff --git a/components/fatfs/CMakeLists.txt b/components/fatfs/CMakeLists.txt index d46f077252..d12bd175d2 100644 --- a/components/fatfs/CMakeLists.txt +++ b/components/fatfs/CMakeLists.txt @@ -1,14 +1,12 @@ -set(srcs "src/diskio.c" - "src/diskio_rawflash.c" - "src/diskio_sdmmc.c" - "src/diskio_wl.c" - "src/ff.c" - "src/ffsystem.c" - "src/ffunicode.c" - "src/vfs_fat.c" - "src/vfs_fat_sdmmc.c" - "src/vfs_fat_spiflash.c") - -idf_component_register(SRCS "${srcs}" +idf_component_register(SRCS "src/diskio.c" + "src/diskio_rawflash.c" + "src/diskio_sdmmc.c" + "src/diskio_wl.c" + "src/ff.c" + "src/ffsystem.c" + "src/ffunicode.c" + "src/vfs_fat.c" + "src/vfs_fat_sdmmc.c" + "src/vfs_fat_spiflash.c" INCLUDE_DIRS src REQUIRES wear_levelling sdmmc) diff --git a/components/freemodbus/CMakeLists.txt b/components/freemodbus/CMakeLists.txt index 0bf6b6af07..97112a53be 100644 --- a/components/freemodbus/CMakeLists.txt +++ b/components/freemodbus/CMakeLists.txt @@ -1,6 +1,7 @@ # The following five lines of boilerplate have to be in your project's # CMakeLists in this exact order for cmake to work correctly -set(srcs "common/esp_modbus_master.c" +set(srcs + "common/esp_modbus_master.c" "common/esp_modbus_slave.c" "modbus/mb.c" "modbus/mb_m.c" diff --git a/components/freertos/CMakeLists.txt b/components/freertos/CMakeLists.txt index 0efd886e0d..ee6b560392 100644 --- a/components/freertos/CMakeLists.txt +++ b/components/freertos/CMakeLists.txt @@ -1,19 +1,21 @@ -set(srcs "croutine.c" - "event_groups.c" - "FreeRTOS-openocd.c" - "list.c" - "port.c" - "portasm.S" - "queue.c" - "tasks.c" - "timers.c" - "xtensa_context.S" - "xtensa_init.c" - "xtensa_intr.c" - "xtensa_intr_asm.S" - "xtensa_overlay_os_hook.c" - "xtensa_vector_defaults.S" - "xtensa_vectors.S") +set(srcs + "croutine.c" + "event_groups.c" + "FreeRTOS-openocd.c" + "list.c" + "port.c" + "portasm.S" + "queue.c" + "tasks.c" + "timers.c" + "xtensa_context.S" + "xtensa_init.c" + "xtensa_intr.c" + "xtensa_intr_asm.S" + "xtensa_overlay_os_hook.c" + "xtensa_vector_defaults.S" + "xtensa_vectors.S") + # app_trace is required by FreeRTOS headers only when CONFIG_SYSVIEW_ENABLE=y, # but requirements can't depend on config options, so always require it. idf_component_register(SRCS "${srcs}" diff --git a/components/heap/CMakeLists.txt b/components/heap/CMakeLists.txt index 40fac3710b..0bacaf74f9 100644 --- a/components/heap/CMakeLists.txt +++ b/components/heap/CMakeLists.txt @@ -1,6 +1,7 @@ -set(srcs "heap_caps.c" - "heap_caps_init.c" - "multi_heap.c") +set(srcs + "heap_caps.c" + "heap_caps_init.c" + "multi_heap.c") if(NOT CONFIG_HEAP_POISONING_DISABLED) list(APPEND srcs "multi_heap_poisoning.c") diff --git a/components/libsodium/CMakeLists.txt b/components/libsodium/CMakeLists.txt index 358a840d25..103e296612 100644 --- a/components/libsodium/CMakeLists.txt +++ b/components/libsodium/CMakeLists.txt @@ -1,126 +1,129 @@ set(SRC libsodium/src/libsodium) # Derived from libsodium/src/libsodium/Makefile.am # (ignoring the !MINIMAL set) -set(srcs "${SRC}/crypto_aead/chacha20poly1305/sodium/aead_chacha20poly1305.c" - "${SRC}/crypto_aead/xchacha20poly1305/sodium/aead_xchacha20poly1305.c" - "${SRC}/crypto_auth/crypto_auth.c" - "${SRC}/crypto_auth/hmacsha256/auth_hmacsha256.c" - "${SRC}/crypto_auth/hmacsha512/auth_hmacsha512.c" - "${SRC}/crypto_auth/hmacsha512256/auth_hmacsha512256.c" - "${SRC}/crypto_box/crypto_box.c" - "${SRC}/crypto_box/crypto_box_easy.c" - "${SRC}/crypto_box/crypto_box_seal.c" - "${SRC}/crypto_box/curve25519xchacha20poly1305/box_curve25519xchacha20poly1305.c" - "${SRC}/crypto_box/curve25519xsalsa20poly1305/box_curve25519xsalsa20poly1305.c" - "${SRC}/crypto_core/curve25519/ref10/curve25519_ref10.c" - "${SRC}/crypto_core/hchacha20/core_hchacha20.c" - "${SRC}/crypto_core/hsalsa20/core_hsalsa20.c" - "${SRC}/crypto_core/hsalsa20/ref2/core_hsalsa20_ref2.c" - "${SRC}/crypto_core/salsa/ref/core_salsa_ref.c" - "${SRC}/crypto_generichash/crypto_generichash.c" - "${SRC}/crypto_generichash/blake2b/generichash_blake2.c" - "${SRC}/crypto_generichash/blake2b/ref/blake2b-compress-avx2.c" - "${SRC}/crypto_generichash/blake2b/ref/blake2b-compress-ref.c" - "${SRC}/crypto_generichash/blake2b/ref/blake2b-compress-sse41.c" - "${SRC}/crypto_generichash/blake2b/ref/blake2b-compress-ssse3.c" - "${SRC}/crypto_generichash/blake2b/ref/blake2b-ref.c" - "${SRC}/crypto_generichash/blake2b/ref/generichash_blake2b.c" - "${SRC}/crypto_hash/crypto_hash.c" - "${SRC}/crypto_hash/sha256/hash_sha256.c" - "${SRC}/crypto_hash/sha256/cp/hash_sha256_cp.c" - "${SRC}/crypto_hash/sha512/hash_sha512.c" - "${SRC}/crypto_hash/sha512/cp/hash_sha512_cp.c" - "${SRC}/crypto_kdf/crypto_kdf.c" - "${SRC}/crypto_kdf/blake2b/kdf_blake2b.c" - "${SRC}/crypto_kx/crypto_kx.c" - "${SRC}/crypto_onetimeauth/crypto_onetimeauth.c" - "${SRC}/crypto_onetimeauth/poly1305/onetimeauth_poly1305.c" - "${SRC}/crypto_onetimeauth/poly1305/donna/poly1305_donna.c" - "${SRC}/crypto_onetimeauth/poly1305/sse2/poly1305_sse2.c" - "${SRC}/crypto_pwhash/crypto_pwhash.c" - "${SRC}/crypto_pwhash/argon2/argon2-core.c" - "${SRC}/crypto_pwhash/argon2/argon2-encoding.c" - "${SRC}/crypto_pwhash/argon2/argon2-fill-block-ref.c" - "${SRC}/crypto_pwhash/argon2/argon2-fill-block-ssse3.c" - "${SRC}/crypto_pwhash/argon2/argon2.c" - "${SRC}/crypto_pwhash/argon2/blake2b-long.c" - "${SRC}/crypto_pwhash/argon2/pwhash_argon2i.c" - "${SRC}/crypto_pwhash/scryptsalsa208sha256/crypto_scrypt-common.c" - "${SRC}/crypto_pwhash/scryptsalsa208sha256/pbkdf2-sha256.c" - "${SRC}/crypto_pwhash/scryptsalsa208sha256/pwhash_scryptsalsa208sha256.c" - "${SRC}/crypto_pwhash/scryptsalsa208sha256/scrypt_platform.c" - "${SRC}/crypto_pwhash/scryptsalsa208sha256/nosse/pwhash_scryptsalsa208sha256_nosse.c" - "${SRC}/crypto_pwhash/scryptsalsa208sha256/sse/pwhash_scryptsalsa208sha256_sse.c" - "${SRC}/crypto_scalarmult/crypto_scalarmult.c" - "${SRC}/crypto_scalarmult/curve25519/scalarmult_curve25519.c" - "${SRC}/crypto_scalarmult/curve25519/donna_c64/curve25519_donna_c64.c" - "${SRC}/crypto_scalarmult/curve25519/ref10/x25519_ref10.c" - "${SRC}/crypto_scalarmult/curve25519/sandy2x/consts.S" - "${SRC}/crypto_scalarmult/curve25519/sandy2x/curve25519_sandy2x.c" - "${SRC}/crypto_scalarmult/curve25519/sandy2x/fe51_invert.c" - "${SRC}/crypto_scalarmult/curve25519/sandy2x/fe51_mul.S" - "${SRC}/crypto_scalarmult/curve25519/sandy2x/fe51_nsquare.S" - "${SRC}/crypto_scalarmult/curve25519/sandy2x/fe51_pack.S" - "${SRC}/crypto_scalarmult/curve25519/sandy2x/fe_frombytes_sandy2x.c" - "${SRC}/crypto_scalarmult/curve25519/sandy2x/ladder.S" - "${SRC}/crypto_scalarmult/curve25519/sandy2x/ladder_base.S" - "${SRC}/crypto_scalarmult/curve25519/sandy2x/sandy2x.S" - "${SRC}/crypto_secretbox/crypto_secretbox.c" - "${SRC}/crypto_secretbox/crypto_secretbox_easy.c" - "${SRC}/crypto_secretbox/xchacha20poly1305/secretbox_xchacha20poly1305.c" - "${SRC}/crypto_secretbox/xsalsa20poly1305/secretbox_xsalsa20poly1305.c" - "${SRC}/crypto_shorthash/crypto_shorthash.c" - "${SRC}/crypto_shorthash/siphash24/shorthash_siphash24.c" - "${SRC}/crypto_shorthash/siphash24/shorthash_siphashx24.c" - "${SRC}/crypto_shorthash/siphash24/ref/shorthash_siphash24_ref.c" - "${SRC}/crypto_shorthash/siphash24/ref/shorthash_siphashx24_ref.c" - "${SRC}/crypto_sign/crypto_sign.c" - "${SRC}/crypto_sign/ed25519/sign_ed25519.c" - "${SRC}/crypto_sign/ed25519/ref10/keypair.c" - "${SRC}/crypto_sign/ed25519/ref10/obsolete.c" - "${SRC}/crypto_sign/ed25519/ref10/open.c" - "${SRC}/crypto_sign/ed25519/ref10/sign.c" - "${SRC}/crypto_stream/crypto_stream.c" - "${SRC}/crypto_stream/aes128ctr/stream_aes128ctr.c" - "${SRC}/crypto_stream/aes128ctr/nacl/afternm_aes128ctr.c" - "${SRC}/crypto_stream/aes128ctr/nacl/beforenm_aes128ctr.c" - "${SRC}/crypto_stream/aes128ctr/nacl/consts_aes128ctr.c" - "${SRC}/crypto_stream/aes128ctr/nacl/int128_aes128ctr.c" - "${SRC}/crypto_stream/aes128ctr/nacl/stream_aes128ctr_nacl.c" - "${SRC}/crypto_stream/aes128ctr/nacl/xor_afternm_aes128ctr.c" - "${SRC}/crypto_stream/chacha20/stream_chacha20.c" - "${SRC}/crypto_stream/chacha20/dolbeau/chacha20_dolbeau-avx2.c" - "${SRC}/crypto_stream/chacha20/dolbeau/chacha20_dolbeau-ssse3.c" - "${SRC}/crypto_stream/chacha20/ref/chacha20_ref.c" - "${SRC}/crypto_stream/salsa20/stream_salsa20.c" - "${SRC}/crypto_stream/salsa20/ref/salsa20_ref.c" - "${SRC}/crypto_stream/salsa20/xmm6/salsa20_xmm6-asm.S" - "${SRC}/crypto_stream/salsa20/xmm6/salsa20_xmm6.c" - "${SRC}/crypto_stream/salsa20/xmm6int/salsa20_xmm6int-avx2.c" - "${SRC}/crypto_stream/salsa20/xmm6int/salsa20_xmm6int-sse2.c" - "${SRC}/crypto_stream/salsa2012/stream_salsa2012.c" - "${SRC}/crypto_stream/salsa2012/ref/stream_salsa2012_ref.c" - "${SRC}/crypto_stream/salsa208/stream_salsa208.c" - "${SRC}/crypto_stream/salsa208/ref/stream_salsa208_ref.c" - "${SRC}/crypto_stream/xchacha20/stream_xchacha20.c" - "${SRC}/crypto_stream/xsalsa20/stream_xsalsa20.c" - "${SRC}/crypto_verify/sodium/verify.c" - "${SRC}/randombytes/randombytes.c" - "${SRC}/randombytes/nativeclient/randombytes_nativeclient.c" - "${SRC}/randombytes/salsa20/randombytes_salsa20_random.c" - "${SRC}/randombytes/sysrandom/randombytes_sysrandom.c" - "${SRC}/sodium/core.c" - "${SRC}/sodium/runtime.c" - "${SRC}/sodium/utils.c" - "${SRC}/sodium/version.c" - "port/randombytes_esp32.c") +set(srcs + "${SRC}/crypto_aead/chacha20poly1305/sodium/aead_chacha20poly1305.c" + "${SRC}/crypto_aead/xchacha20poly1305/sodium/aead_xchacha20poly1305.c" + "${SRC}/crypto_auth/crypto_auth.c" + "${SRC}/crypto_auth/hmacsha256/auth_hmacsha256.c" + "${SRC}/crypto_auth/hmacsha512/auth_hmacsha512.c" + "${SRC}/crypto_auth/hmacsha512256/auth_hmacsha512256.c" + "${SRC}/crypto_box/crypto_box.c" + "${SRC}/crypto_box/crypto_box_easy.c" + "${SRC}/crypto_box/crypto_box_seal.c" + "${SRC}/crypto_box/curve25519xchacha20poly1305/box_curve25519xchacha20poly1305.c" + "${SRC}/crypto_box/curve25519xsalsa20poly1305/box_curve25519xsalsa20poly1305.c" + "${SRC}/crypto_core/curve25519/ref10/curve25519_ref10.c" + "${SRC}/crypto_core/hchacha20/core_hchacha20.c" + "${SRC}/crypto_core/hsalsa20/core_hsalsa20.c" + "${SRC}/crypto_core/hsalsa20/ref2/core_hsalsa20_ref2.c" + "${SRC}/crypto_core/salsa/ref/core_salsa_ref.c" + "${SRC}/crypto_generichash/crypto_generichash.c" + "${SRC}/crypto_generichash/blake2b/generichash_blake2.c" + "${SRC}/crypto_generichash/blake2b/ref/blake2b-compress-avx2.c" + "${SRC}/crypto_generichash/blake2b/ref/blake2b-compress-ref.c" + "${SRC}/crypto_generichash/blake2b/ref/blake2b-compress-sse41.c" + "${SRC}/crypto_generichash/blake2b/ref/blake2b-compress-ssse3.c" + "${SRC}/crypto_generichash/blake2b/ref/blake2b-ref.c" + "${SRC}/crypto_generichash/blake2b/ref/generichash_blake2b.c" + "${SRC}/crypto_hash/crypto_hash.c" + "${SRC}/crypto_hash/sha256/hash_sha256.c" + "${SRC}/crypto_hash/sha256/cp/hash_sha256_cp.c" + "${SRC}/crypto_hash/sha512/hash_sha512.c" + "${SRC}/crypto_hash/sha512/cp/hash_sha512_cp.c" + "${SRC}/crypto_kdf/crypto_kdf.c" + "${SRC}/crypto_kdf/blake2b/kdf_blake2b.c" + "${SRC}/crypto_kx/crypto_kx.c" + "${SRC}/crypto_onetimeauth/crypto_onetimeauth.c" + "${SRC}/crypto_onetimeauth/poly1305/onetimeauth_poly1305.c" + "${SRC}/crypto_onetimeauth/poly1305/donna/poly1305_donna.c" + "${SRC}/crypto_onetimeauth/poly1305/sse2/poly1305_sse2.c" + "${SRC}/crypto_pwhash/crypto_pwhash.c" + "${SRC}/crypto_pwhash/argon2/argon2-core.c" + "${SRC}/crypto_pwhash/argon2/argon2-encoding.c" + "${SRC}/crypto_pwhash/argon2/argon2-fill-block-ref.c" + "${SRC}/crypto_pwhash/argon2/argon2-fill-block-ssse3.c" + "${SRC}/crypto_pwhash/argon2/argon2.c" + "${SRC}/crypto_pwhash/argon2/blake2b-long.c" + "${SRC}/crypto_pwhash/argon2/pwhash_argon2i.c" + "${SRC}/crypto_pwhash/scryptsalsa208sha256/crypto_scrypt-common.c" + "${SRC}/crypto_pwhash/scryptsalsa208sha256/pbkdf2-sha256.c" + "${SRC}/crypto_pwhash/scryptsalsa208sha256/pwhash_scryptsalsa208sha256.c" + "${SRC}/crypto_pwhash/scryptsalsa208sha256/scrypt_platform.c" + "${SRC}/crypto_pwhash/scryptsalsa208sha256/nosse/pwhash_scryptsalsa208sha256_nosse.c" + "${SRC}/crypto_pwhash/scryptsalsa208sha256/sse/pwhash_scryptsalsa208sha256_sse.c" + "${SRC}/crypto_scalarmult/crypto_scalarmult.c" + "${SRC}/crypto_scalarmult/curve25519/scalarmult_curve25519.c" + "${SRC}/crypto_scalarmult/curve25519/donna_c64/curve25519_donna_c64.c" + "${SRC}/crypto_scalarmult/curve25519/ref10/x25519_ref10.c" + "${SRC}/crypto_scalarmult/curve25519/sandy2x/consts.S" + "${SRC}/crypto_scalarmult/curve25519/sandy2x/curve25519_sandy2x.c" + "${SRC}/crypto_scalarmult/curve25519/sandy2x/fe51_invert.c" + "${SRC}/crypto_scalarmult/curve25519/sandy2x/fe51_mul.S" + "${SRC}/crypto_scalarmult/curve25519/sandy2x/fe51_nsquare.S" + "${SRC}/crypto_scalarmult/curve25519/sandy2x/fe51_pack.S" + "${SRC}/crypto_scalarmult/curve25519/sandy2x/fe_frombytes_sandy2x.c" + "${SRC}/crypto_scalarmult/curve25519/sandy2x/ladder.S" + "${SRC}/crypto_scalarmult/curve25519/sandy2x/ladder_base.S" + "${SRC}/crypto_scalarmult/curve25519/sandy2x/sandy2x.S" + "${SRC}/crypto_secretbox/crypto_secretbox.c" + "${SRC}/crypto_secretbox/crypto_secretbox_easy.c" + "${SRC}/crypto_secretbox/xchacha20poly1305/secretbox_xchacha20poly1305.c" + "${SRC}/crypto_secretbox/xsalsa20poly1305/secretbox_xsalsa20poly1305.c" + "${SRC}/crypto_shorthash/crypto_shorthash.c" + "${SRC}/crypto_shorthash/siphash24/shorthash_siphash24.c" + "${SRC}/crypto_shorthash/siphash24/shorthash_siphashx24.c" + "${SRC}/crypto_shorthash/siphash24/ref/shorthash_siphash24_ref.c" + "${SRC}/crypto_shorthash/siphash24/ref/shorthash_siphashx24_ref.c" + "${SRC}/crypto_sign/crypto_sign.c" + "${SRC}/crypto_sign/ed25519/sign_ed25519.c" + "${SRC}/crypto_sign/ed25519/ref10/keypair.c" + "${SRC}/crypto_sign/ed25519/ref10/obsolete.c" + "${SRC}/crypto_sign/ed25519/ref10/open.c" + "${SRC}/crypto_sign/ed25519/ref10/sign.c" + "${SRC}/crypto_stream/crypto_stream.c" + "${SRC}/crypto_stream/aes128ctr/stream_aes128ctr.c" + "${SRC}/crypto_stream/aes128ctr/nacl/afternm_aes128ctr.c" + "${SRC}/crypto_stream/aes128ctr/nacl/beforenm_aes128ctr.c" + "${SRC}/crypto_stream/aes128ctr/nacl/consts_aes128ctr.c" + "${SRC}/crypto_stream/aes128ctr/nacl/int128_aes128ctr.c" + "${SRC}/crypto_stream/aes128ctr/nacl/stream_aes128ctr_nacl.c" + "${SRC}/crypto_stream/aes128ctr/nacl/xor_afternm_aes128ctr.c" + "${SRC}/crypto_stream/chacha20/stream_chacha20.c" + "${SRC}/crypto_stream/chacha20/dolbeau/chacha20_dolbeau-avx2.c" + "${SRC}/crypto_stream/chacha20/dolbeau/chacha20_dolbeau-ssse3.c" + "${SRC}/crypto_stream/chacha20/ref/chacha20_ref.c" + "${SRC}/crypto_stream/salsa20/stream_salsa20.c" + "${SRC}/crypto_stream/salsa20/ref/salsa20_ref.c" + "${SRC}/crypto_stream/salsa20/xmm6/salsa20_xmm6-asm.S" + "${SRC}/crypto_stream/salsa20/xmm6/salsa20_xmm6.c" + "${SRC}/crypto_stream/salsa20/xmm6int/salsa20_xmm6int-avx2.c" + "${SRC}/crypto_stream/salsa20/xmm6int/salsa20_xmm6int-sse2.c" + "${SRC}/crypto_stream/salsa2012/stream_salsa2012.c" + "${SRC}/crypto_stream/salsa2012/ref/stream_salsa2012_ref.c" + "${SRC}/crypto_stream/salsa208/stream_salsa208.c" + "${SRC}/crypto_stream/salsa208/ref/stream_salsa208_ref.c" + "${SRC}/crypto_stream/xchacha20/stream_xchacha20.c" + "${SRC}/crypto_stream/xsalsa20/stream_xsalsa20.c" + "${SRC}/crypto_verify/sodium/verify.c" + "${SRC}/randombytes/randombytes.c" + "${SRC}/randombytes/nativeclient/randombytes_nativeclient.c" + "${SRC}/randombytes/salsa20/randombytes_salsa20_random.c" + "${SRC}/randombytes/sysrandom/randombytes_sysrandom.c" + "${SRC}/sodium/core.c" + "${SRC}/sodium/runtime.c" + "${SRC}/sodium/utils.c" + "${SRC}/sodium/version.c" + "port/randombytes_esp32.c") if(CONFIG_LIBSODIUM_USE_MBEDTLS_SHA) - list(APPEND srcs "port/crypto_hash_mbedtls/crypto_hash_sha256_mbedtls.c" - "port/crypto_hash_mbedtls/crypto_hash_sha512_mbedtls.c") + list(APPEND srcs + "port/crypto_hash_mbedtls/crypto_hash_sha256_mbedtls.c" + "port/crypto_hash_mbedtls/crypto_hash_sha512_mbedtls.c") else() - list(APPEND srcs "${SRC}/crypto_hash/sha256/cp/hash_sha256_cp.c" - "${SRC}/crypto_hash/sha512/cp/hash_sha512_cp.c") + list(APPEND srcs + "${SRC}/crypto_hash/sha256/cp/hash_sha256_cp.c" + "${SRC}/crypto_hash/sha512/cp/hash_sha512_cp.c") endif() set(include_dirs ${SRC}/include port_include) diff --git a/components/lwip/CMakeLists.txt b/components/lwip/CMakeLists.txt index 6eda8db37e..cb6a659530 100644 --- a/components/lwip/CMakeLists.txt +++ b/components/lwip/CMakeLists.txt @@ -6,57 +6,93 @@ set(include_dirs port/esp32/include/arch ) -set(srcs "apps/dhcpserver/dhcpserver.c" - "apps/ping/esp_ping.c" - "apps/ping/ping.c" - "apps/sntp/sntp.c" - "lwip/src/api/api_lib.c" - "lwip/src/api/api_msg.c" - "lwip/src/api/err.c" - "lwip/src/api/netbuf.c" - "lwip/src/api/netdb.c" - "lwip/src/api/netifapi.c" - "lwip/src/api/sockets.c" - "lwip/src/api/tcpip.c" - "lwip/src/apps/sntp/sntp.c" - "lwip/src/core/def.c" - "lwip/src/core/dns.c" - "lwip/src/core/inet_chksum.c" - "lwip/src/core/init.c" - "lwip/src/core/ip.c" - "lwip/src/core/mem.c" - "lwip/src/core/memp.c" - "lwip/src/core/netif.c" - "lwip/src/core/pbuf.c" - "lwip/src/core/raw.c" - "lwip/src/core/stats.c" - "lwip/src/core/sys.c" - "lwip/src/core/tcp.c" - "lwip/src/core/tcp_in.c" - "lwip/src/core/tcp_out.c" - "lwip/src/core/timeouts.c" - "lwip/src/core/udp.c" - "lwip/src/core/ipv4/autoip.c" - "lwip/src/core/ipv4/dhcp.c" - "lwip/src/core/ipv4/etharp.c" - "lwip/src/core/ipv4/icmp.c" - "lwip/src/core/ipv4/igmp.c" - "lwip/src/core/ipv4/ip4.c" - "lwip/src/core/ipv4/ip4_addr.c" - "lwip/src/core/ipv4/ip4_frag.c" - "lwip/src/core/ipv6/dhcp6.c" - "lwip/src/core/ipv6/ethip6.c" - "lwip/src/core/ipv6/icmp6.c" - "lwip/src/core/ipv6/inet6.c" - "lwip/src/core/ipv6/ip6.c" - "lwip/src/core/ipv6/ip6_addr.c" - "lwip/src/core/ipv6/ip6_frag.c" - "lwip/src/core/ipv6/mld6.c" - "lwip/src/core/ipv6/nd6.c" - "lwip/src/netif/ethernet.c" - "lwip/src/netif/ethernetif.c" - "lwip/src/netif/lowpan6.c" - "lwip/src/netif/slipif.c" +set(srcs + "apps/dhcpserver/dhcpserver.c" + "apps/ping/esp_ping.c" + "apps/ping/ping.c" + "apps/sntp/sntp.c" + "lwip/src/api/api_lib.c" + "lwip/src/api/api_msg.c" + "lwip/src/api/err.c" + "lwip/src/api/netbuf.c" + "lwip/src/api/netdb.c" + "lwip/src/api/netifapi.c" + "lwip/src/api/sockets.c" + "lwip/src/api/tcpip.c" + "lwip/src/apps/sntp/sntp.c" + "lwip/src/core/def.c" + "lwip/src/core/dns.c" + "lwip/src/core/inet_chksum.c" + "lwip/src/core/init.c" + "lwip/src/core/ip.c" + "lwip/src/core/mem.c" + "lwip/src/core/memp.c" + "lwip/src/core/netif.c" + "lwip/src/core/pbuf.c" + "lwip/src/core/raw.c" + "lwip/src/core/stats.c" + "lwip/src/core/sys.c" + "lwip/src/core/tcp.c" + "lwip/src/core/tcp_in.c" + "lwip/src/core/tcp_out.c" + "lwip/src/core/timeouts.c" + "lwip/src/core/udp.c" + "lwip/src/core/ipv4/autoip.c" + "lwip/src/core/ipv4/dhcp.c" + "lwip/src/core/ipv4/etharp.c" + "lwip/src/core/ipv4/icmp.c" + "lwip/src/core/ipv4/igmp.c" + "lwip/src/core/ipv4/ip4.c" + "lwip/src/core/ipv4/ip4_addr.c" + "lwip/src/core/ipv4/ip4_frag.c" + "lwip/src/core/ipv6/dhcp6.c" + "lwip/src/core/ipv6/ethip6.c" + "lwip/src/core/ipv6/icmp6.c" + "lwip/src/core/ipv6/inet6.c" + "lwip/src/core/ipv6/ip6.c" + "lwip/src/core/ipv6/ip6_addr.c" + "lwip/src/core/ipv6/ip6_frag.c" + "lwip/src/core/ipv6/mld6.c" + "lwip/src/core/ipv6/nd6.c" + "lwip/src/netif/ethernet.c" + "lwip/src/netif/ethernetif.c" + "lwip/src/netif/lowpan6.c" + "lwip/src/netif/slipif.c" + "lwip/src/netif/ppp/auth.c" + "lwip/src/netif/ppp/ccp.c" + "lwip/src/netif/ppp/chap-md5.c" + "lwip/src/netif/ppp/chap-new.c" + "lwip/src/netif/ppp/chap_ms.c" + "lwip/src/netif/ppp/demand.c" + "lwip/src/netif/ppp/eap.c" + "lwip/src/netif/ppp/ecp.c" + "lwip/src/netif/ppp/eui64.c" + "lwip/src/netif/ppp/fsm.c" + "lwip/src/netif/ppp/ipcp.c" + "lwip/src/netif/ppp/ipv6cp.c" + "lwip/src/netif/ppp/lcp.c" + "lwip/src/netif/ppp/magic.c" + "lwip/src/netif/ppp/mppe.c" + "lwip/src/netif/ppp/multilink.c" + "lwip/src/netif/ppp/ppp.c" + "lwip/src/netif/ppp/pppapi.c" + "lwip/src/netif/ppp/pppcrypt.c" + "lwip/src/netif/ppp/pppoe.c" + "lwip/src/netif/ppp/pppol2tp.c" + "lwip/src/netif/ppp/pppos.c" + "lwip/src/netif/ppp/upap.c" + "lwip/src/netif/ppp/utils.c" + "lwip/src/netif/ppp/vj.c" + "port/esp32/vfs_lwip.c" + "port/esp32/debug/lwip_debug.c" + "port/esp32/freertos/sys_arch.c" + "port/esp32/netif/dhcp_state.c" + "port/esp32/netif/ethernetif.c" + "port/esp32/netif/nettestif.c" + "port/esp32/netif/wlanif.c") + +if(CONFIG_LWIP_PPP_SUPPORT) + list(APPEND srcs "lwip/src/netif/ppp/auth.c" "lwip/src/netif/ppp/ccp.c" "lwip/src/netif/ppp/chap-md5.c" @@ -82,45 +118,11 @@ set(srcs "apps/dhcpserver/dhcpserver.c" "lwip/src/netif/ppp/upap.c" "lwip/src/netif/ppp/utils.c" "lwip/src/netif/ppp/vj.c" - "port/esp32/vfs_lwip.c" - "port/esp32/debug/lwip_debug.c" - "port/esp32/freertos/sys_arch.c" - "port/esp32/netif/dhcp_state.c" - "port/esp32/netif/ethernetif.c" - "port/esp32/netif/nettestif.c" - "port/esp32/netif/wlanif.c") - -if(CONFIG_LWIP_PPP_SUPPORT) - list(APPEND srcs "lwip/src/netif/ppp/auth.c" - "lwip/src/netif/ppp/ccp.c" - "lwip/src/netif/ppp/chap-md5.c" - "lwip/src/netif/ppp/chap-new.c" - "lwip/src/netif/ppp/chap_ms.c" - "lwip/src/netif/ppp/demand.c" - "lwip/src/netif/ppp/eap.c" - "lwip/src/netif/ppp/ecp.c" - "lwip/src/netif/ppp/eui64.c" - "lwip/src/netif/ppp/fsm.c" - "lwip/src/netif/ppp/ipcp.c" - "lwip/src/netif/ppp/ipv6cp.c" - "lwip/src/netif/ppp/lcp.c" - "lwip/src/netif/ppp/magic.c" - "lwip/src/netif/ppp/mppe.c" - "lwip/src/netif/ppp/multilink.c" - "lwip/src/netif/ppp/ppp.c" - "lwip/src/netif/ppp/pppapi.c" - "lwip/src/netif/ppp/pppcrypt.c" - "lwip/src/netif/ppp/pppoe.c" - "lwip/src/netif/ppp/pppol2tp.c" - "lwip/src/netif/ppp/pppos.c" - "lwip/src/netif/ppp/upap.c" - "lwip/src/netif/ppp/utils.c" - "lwip/src/netif/ppp/vj.c" - "lwip/src/netif/ppp/polarssl/arc4.c" - "lwip/src/netif/ppp/polarssl/des.c" - "lwip/src/netif/ppp/polarssl/md4.c" - "lwip/src/netif/ppp/polarssl/md5.c" - "lwip/src/netif/ppp/polarssl/sha1.c") + "lwip/src/netif/ppp/polarssl/arc4.c" + "lwip/src/netif/ppp/polarssl/des.c" + "lwip/src/netif/ppp/polarssl/md4.c" + "lwip/src/netif/ppp/polarssl/md5.c" + "lwip/src/netif/ppp/polarssl/sha1.c") endif() idf_component_register(SRCS "${srcs}" diff --git a/components/newlib/CMakeLists.txt b/components/newlib/CMakeLists.txt index 97a6d773b7..47b7c368b3 100644 --- a/components/newlib/CMakeLists.txt +++ b/components/newlib/CMakeLists.txt @@ -1,4 +1,5 @@ -set(srcs "heap.c" +set(srcs + "heap.c" "locks.c" "poll.c" "pthread.c" diff --git a/components/nghttp/CMakeLists.txt b/components/nghttp/CMakeLists.txt index 32bd379eda..1c9ba17a23 100644 --- a/components/nghttp/CMakeLists.txt +++ b/components/nghttp/CMakeLists.txt @@ -1,26 +1,27 @@ -set(srcs "nghttp2/lib/nghttp2_buf.c" - "nghttp2/lib/nghttp2_callbacks.c" - "nghttp2/lib/nghttp2_debug.c" - "nghttp2/lib/nghttp2_frame.c" - "nghttp2/lib/nghttp2_hd.c" - "nghttp2/lib/nghttp2_hd_huffman.c" - "nghttp2/lib/nghttp2_hd_huffman_data.c" - "nghttp2/lib/nghttp2_helper.c" - "nghttp2/lib/nghttp2_http.c" - "nghttp2/lib/nghttp2_map.c" - "nghttp2/lib/nghttp2_mem.c" - "nghttp2/lib/nghttp2_npn.c" - "nghttp2/lib/nghttp2_option.c" - "nghttp2/lib/nghttp2_outbound_item.c" - "nghttp2/lib/nghttp2_pq.c" - "nghttp2/lib/nghttp2_priority_spec.c" - "nghttp2/lib/nghttp2_queue.c" - "nghttp2/lib/nghttp2_rcbuf.c" - "nghttp2/lib/nghttp2_session.c" - "nghttp2/lib/nghttp2_stream.c" - "nghttp2/lib/nghttp2_submit.c" - "nghttp2/lib/nghttp2_version.c" - "port/http_parser.c") +set(srcs + "nghttp2/lib/nghttp2_buf.c" + "nghttp2/lib/nghttp2_callbacks.c" + "nghttp2/lib/nghttp2_debug.c" + "nghttp2/lib/nghttp2_frame.c" + "nghttp2/lib/nghttp2_hd.c" + "nghttp2/lib/nghttp2_hd_huffman.c" + "nghttp2/lib/nghttp2_hd_huffman_data.c" + "nghttp2/lib/nghttp2_helper.c" + "nghttp2/lib/nghttp2_http.c" + "nghttp2/lib/nghttp2_map.c" + "nghttp2/lib/nghttp2_mem.c" + "nghttp2/lib/nghttp2_npn.c" + "nghttp2/lib/nghttp2_option.c" + "nghttp2/lib/nghttp2_outbound_item.c" + "nghttp2/lib/nghttp2_pq.c" + "nghttp2/lib/nghttp2_priority_spec.c" + "nghttp2/lib/nghttp2_queue.c" + "nghttp2/lib/nghttp2_rcbuf.c" + "nghttp2/lib/nghttp2_session.c" + "nghttp2/lib/nghttp2_stream.c" + "nghttp2/lib/nghttp2_submit.c" + "nghttp2/lib/nghttp2_version.c" + "port/http_parser.c") idf_component_register(SRCS "${srcs}" INCLUDE_DIRS port/include nghttp2/lib/includes) diff --git a/components/protocomm/CMakeLists.txt b/components/protocomm/CMakeLists.txt index 22bb08254c..334e279145 100644 --- a/components/protocomm/CMakeLists.txt +++ b/components/protocomm/CMakeLists.txt @@ -2,15 +2,16 @@ set(include_dirs include/common include/security include/transports) set(priv_include_dirs proto-c src/common src/simple_ble) -set(srcs "src/common/protocomm.c" - "src/security/security0.c" - "src/security/security1.c" - "proto-c/constants.pb-c.c" - "proto-c/sec0.pb-c.c" - "proto-c/sec1.pb-c.c" - "proto-c/session.pb-c.c" - "src/transports/protocomm_console.c" - "src/transports/protocomm_httpd.c") +set(srcs + "src/common/protocomm.c" + "src/security/security0.c" + "src/security/security1.c" + "proto-c/constants.pb-c.c" + "proto-c/sec0.pb-c.c" + "proto-c/sec1.pb-c.c" + "proto-c/session.pb-c.c" + "src/transports/protocomm_console.c" + "src/transports/protocomm_httpd.c") if(CONFIG_BT_ENABLED) if(CONFIG_BT_BLUEDROID_ENABLED) diff --git a/components/soc/CMakeLists.txt b/components/soc/CMakeLists.txt index 0a2b29b00e..11bbbd0d4c 100644 --- a/components/soc/CMakeLists.txt +++ b/components/soc/CMakeLists.txt @@ -9,15 +9,16 @@ if(EXISTS "${COMPONENT_DIR}/${soc_name}") endif() list(APPEND include_dirs include) -list(APPEND srcs "src/memory_layout_utils.c" - "src/lldesc.c" - "src/hal/spi_hal.c" - "src/hal/spi_hal_iram.c" - "src/hal/spi_slave_hal.c" - "src/hal/spi_slave_hal_iram.c" - "src/soc_include_legacy_warn.c" - "src/hal/spi_flash_hal.c" - "src/hal/spi_flash_hal_iram.c" +list(APPEND srcs + "src/memory_layout_utils.c" + "src/lldesc.c" + "src/hal/spi_hal.c" + "src/hal/spi_hal_iram.c" + "src/hal/spi_slave_hal.c" + "src/hal/spi_slave_hal_iram.c" + "src/soc_include_legacy_warn.c" + "src/hal/spi_flash_hal.c" + "src/hal/spi_flash_hal_iram.c" ) idf_component_register(SRCS "${srcs}" diff --git a/components/spi_flash/CMakeLists.txt b/components/spi_flash/CMakeLists.txt index 4a0dd4ecc4..51bec183a7 100644 --- a/components/spi_flash/CMakeLists.txt +++ b/components/spi_flash/CMakeLists.txt @@ -4,18 +4,18 @@ if(BOOTLOADER_BUILD) # need other parts of this component set(srcs "spi_flash_rom_patch.c") else() - set(srcs "cache_utils.c" - "flash_mmap.c" - "flash_ops.c" - "partition.c" - "spi_flash_rom_patch.c" - "spi_flash_chip_drivers.c" - "spi_flash_chip_generic.c" - "spi_flash_chip_issi.c" - "spi_flash_os_func_app.c" - "spi_flash_os_func_noos.c" - "memspi_host_driver.c" - ) + set(srcs + "cache_utils.c" + "flash_mmap.c" + "flash_ops.c" + "partition.c" + "spi_flash_rom_patch.c" + "spi_flash_chip_drivers.c" + "spi_flash_chip_generic.c" + "spi_flash_chip_issi.c" + "spi_flash_os_func_app.c" + "spi_flash_os_func_noos.c" + "memspi_host_driver.c") if(NOT CONFIG_SPI_FLASH_USE_LEGACY_IMPL) list(APPEND srcs "esp_flash_api.c") endif() diff --git a/components/unity/CMakeLists.txt b/components/unity/CMakeLists.txt index a82d037c0c..d98b05d8df 100644 --- a/components/unity/CMakeLists.txt +++ b/components/unity/CMakeLists.txt @@ -1,5 +1,6 @@ -set(srcs "unity/src/unity.c" - "unity_port_esp32.c") +set(srcs + "unity/src/unity.c" + "unity_port_esp32.c") if(CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL) list(APPEND COMPONENT_PRIV_INCLUDEDIRS "include/priv") diff --git a/components/wpa_supplicant/CMakeLists.txt b/components/wpa_supplicant/CMakeLists.txt index 78ce6573aa..7389e1da6c 100644 --- a/components/wpa_supplicant/CMakeLists.txt +++ b/components/wpa_supplicant/CMakeLists.txt @@ -1,77 +1,78 @@ -set(srcs "port/os_xtensa.c" - "src/crypto/aes-cbc.c" - "src/crypto/aes-internal-dec.c" - "src/crypto/aes-internal-enc.c" - "src/crypto/aes-internal.c" - "src/crypto/aes-unwrap.c" - "src/crypto/aes-wrap.c" - "src/crypto/bignum.c" - "src/crypto/crypto_mbedtls.c" - "src/crypto/crypto_internal-cipher.c" - "src/crypto/crypto_internal-modexp.c" - "src/crypto/crypto_internal-rsa.c" - "src/crypto/crypto_internal.c" - "src/crypto/des-internal.c" - "src/crypto/dh_group5.c" - "src/crypto/dh_groups.c" - "src/crypto/md4-internal.c" - "src/crypto/md5-internal.c" - "src/crypto/md5.c" - "src/crypto/ms_funcs.c" - "src/crypto/rc4.c" - "src/crypto/sha1-internal.c" - "src/crypto/sha1-pbkdf2.c" - "src/crypto/sha1.c" - "src/crypto/sha256-internal.c" - "src/crypto/sha256.c" - "src/fast_crypto/fast_aes-cbc.c" - "src/fast_crypto/fast_aes-unwrap.c" - "src/fast_crypto/fast_aes-wrap.c" - "src/fast_crypto/fast_crypto_internal-cipher.c" - "src/fast_crypto/fast_crypto_internal-modexp.c" - "src/fast_crypto/fast_crypto_internal.c" - "src/fast_crypto/fast_sha256-internal.c" - "src/fast_crypto/fast_sha256.c" - "src/wpa2/eap_peer/chap.c" - "src/wpa2/eap_peer/eap.c" - "src/wpa2/eap_peer/eap_common.c" - "src/wpa2/eap_peer/eap_mschapv2.c" - "src/wpa2/eap_peer/eap_peap.c" - "src/wpa2/eap_peer/eap_peap_common.c" - "src/wpa2/eap_peer/eap_tls.c" - "src/wpa2/eap_peer/eap_tls_common.c" - "src/wpa2/eap_peer/eap_ttls.c" - "src/wpa2/eap_peer/mschapv2.c" - "src/wpa2/tls/asn1.c" - "src/wpa2/tls/bignum.c" - "src/wpa2/tls/pkcs1.c" - "src/wpa2/tls/pkcs5.c" - "src/wpa2/tls/pkcs8.c" - "src/wpa2/tls/rsa.c" - "src/wpa2/tls/tls_internal.c" - "src/wpa2/tls/tlsv1_client.c" - "src/wpa2/tls/tlsv1_client_read.c" - "src/wpa2/tls/tlsv1_client_write.c" - "src/wpa2/tls/tlsv1_common.c" - "src/wpa2/tls/tlsv1_cred.c" - "src/wpa2/tls/tlsv1_record.c" - "src/wpa2/tls/tlsv1_server.c" - "src/wpa2/tls/tlsv1_server_read.c" - "src/wpa2/tls/tlsv1_server_write.c" - "src/wpa2/tls/x509v3.c" - "src/wpa2/utils/base64.c" - "src/wpa2/utils/ext_password.c" - "src/wps/eap_common.c" - "src/wps/uuid.c" - "src/wps/wps.c" - "src/wps/wps_attr_build.c" - "src/wps/wps_attr_parse.c" - "src/wps/wps_attr_process.c" - "src/wps/wps_common.c" - "src/wps/wps_dev_attr.c" - "src/wps/wps_enrollee.c" - "src/wps/wps_registrar.c" - "src/wps/wps_validate.c") +set(srcs + "port/os_xtensa.c" + "src/crypto/aes-cbc.c" + "src/crypto/aes-internal-dec.c" + "src/crypto/aes-internal-enc.c" + "src/crypto/aes-internal.c" + "src/crypto/aes-unwrap.c" + "src/crypto/aes-wrap.c" + "src/crypto/bignum.c" + "src/crypto/crypto_mbedtls.c" + "src/crypto/crypto_internal-cipher.c" + "src/crypto/crypto_internal-modexp.c" + "src/crypto/crypto_internal-rsa.c" + "src/crypto/crypto_internal.c" + "src/crypto/des-internal.c" + "src/crypto/dh_group5.c" + "src/crypto/dh_groups.c" + "src/crypto/md4-internal.c" + "src/crypto/md5-internal.c" + "src/crypto/md5.c" + "src/crypto/ms_funcs.c" + "src/crypto/rc4.c" + "src/crypto/sha1-internal.c" + "src/crypto/sha1-pbkdf2.c" + "src/crypto/sha1.c" + "src/crypto/sha256-internal.c" + "src/crypto/sha256.c" + "src/fast_crypto/fast_aes-cbc.c" + "src/fast_crypto/fast_aes-unwrap.c" + "src/fast_crypto/fast_aes-wrap.c" + "src/fast_crypto/fast_crypto_internal-cipher.c" + "src/fast_crypto/fast_crypto_internal-modexp.c" + "src/fast_crypto/fast_crypto_internal.c" + "src/fast_crypto/fast_sha256-internal.c" + "src/fast_crypto/fast_sha256.c" + "src/wpa2/eap_peer/chap.c" + "src/wpa2/eap_peer/eap.c" + "src/wpa2/eap_peer/eap_common.c" + "src/wpa2/eap_peer/eap_mschapv2.c" + "src/wpa2/eap_peer/eap_peap.c" + "src/wpa2/eap_peer/eap_peap_common.c" + "src/wpa2/eap_peer/eap_tls.c" + "src/wpa2/eap_peer/eap_tls_common.c" + "src/wpa2/eap_peer/eap_ttls.c" + "src/wpa2/eap_peer/mschapv2.c" + "src/wpa2/tls/asn1.c" + "src/wpa2/tls/bignum.c" + "src/wpa2/tls/pkcs1.c" + "src/wpa2/tls/pkcs5.c" + "src/wpa2/tls/pkcs8.c" + "src/wpa2/tls/rsa.c" + "src/wpa2/tls/tls_internal.c" + "src/wpa2/tls/tlsv1_client.c" + "src/wpa2/tls/tlsv1_client_read.c" + "src/wpa2/tls/tlsv1_client_write.c" + "src/wpa2/tls/tlsv1_common.c" + "src/wpa2/tls/tlsv1_cred.c" + "src/wpa2/tls/tlsv1_record.c" + "src/wpa2/tls/tlsv1_server.c" + "src/wpa2/tls/tlsv1_server_read.c" + "src/wpa2/tls/tlsv1_server_write.c" + "src/wpa2/tls/x509v3.c" + "src/wpa2/utils/base64.c" + "src/wpa2/utils/ext_password.c" + "src/wps/eap_common.c" + "src/wps/uuid.c" + "src/wps/wps.c" + "src/wps/wps_attr_build.c" + "src/wps/wps_attr_parse.c" + "src/wps/wps_attr_process.c" + "src/wps/wps_common.c" + "src/wps/wps_dev_attr.c" + "src/wps/wps_enrollee.c" + "src/wps/wps_registrar.c" + "src/wps/wps_validate.c") idf_component_register(SRCS "${srcs}" INCLUDE_DIRS include port/include diff --git a/components/xtensa/CMakeLists.txt b/components/xtensa/CMakeLists.txt index 08a1ccf64c..0ab1dd018d 100644 --- a/components/xtensa/CMakeLists.txt +++ b/components/xtensa/CMakeLists.txt @@ -1,5 +1,8 @@ idf_build_get_property(target IDF_TARGET) -idf_component_register(SRCS "eri.c" "trax.c" "debug_helpers.c" "debug_helpers_asm.S" +idf_component_register(SRCS "debug_helpers.c" + "debug_helpers_asm.S" + "eri.c" + "trax.c" INCLUDE_DIRS include ${target}/include LDFRAGMENTS linker.lf PRIV_REQUIRES soc) From 1096ee4b57a8ac0f41241120374119631649a3f7 Mon Sep 17 00:00:00 2001 From: Kirill Chalov Date: Fri, 21 Jun 2019 20:43:17 +0800 Subject: [PATCH 115/486] Review the file api-reference/peripherals/touch_pad.rst --- .../api-reference/peripherals/touch_pad.rst | 74 ++++++++++--------- 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/docs/en/api-reference/peripherals/touch_pad.rst b/docs/en/api-reference/peripherals/touch_pad.rst index daf15c46f3..6d4c2f8ac6 100644 --- a/docs/en/api-reference/peripherals/touch_pad.rst +++ b/docs/en/api-reference/peripherals/touch_pad.rst @@ -4,19 +4,19 @@ Touch Sensor Introduction ------------ -A touch-sensor system is built on a substrate which carries electrodes and relevant connections under a protective flat surface. When a user touches the surface, the capacitance variation is triggered and a binary signal is generated to indicate whether the touch is valid. +A touch sensor system is built on a substrate which carries electrodes and relevant connections under a protective flat surface. When a user touches the surface, the capacitance variation is used to evaluate if the touch was valid. -ESP32 can provide up to 10 capacitive touch pads / GPIOs. The sensing pads can be arranged in different combinations (e.g. matrix, slider), so that a larger area or more points can be detected. The touch pad sensing process is under the control of a hardware-implemented finite-state machine (FSM) which is initiated by software or a dedicated hardware timer. +ESP32 can handle up to 10 capacitive touch pads / GPIOs. The sensing pads can be arranged in different combinations (e.g., matrix, slider), so that a larger area or more points can be detected. The touch pad sensing process is under the control of a hardware-implemented finite-state machine (FSM) which is initiated by software or a dedicated hardware timer. -Design, operation and control registers of touch sensor are discussed in `ESP32 Technical Reference Manual `_ (PDF). Please refer to it for additional details how this subsystem works. +Design, operation, and control registers of a touch sensor are discussed in `ESP32 Technical Reference Manual `_ (PDF). Please refer to this manual for additional details on how this subsystem works. -In depth details of design of touch sensors and firmware development guidelines for the ESP32 are available in `Touch Sensor Application Note `_. If you would like to test touch sensors in various configurations without building them on your own, check `Guide for ESP32-Sense Development Kit `_. +In-depth design details of touch sensors and firmware development guidelines for ESP32 are available in `Touch Sensor Application Note `_. If you want to test touch sensors in various configurations without building them on your own, check the `Guide for ESP32-Sense Development Kit `_. Functionality Overview ---------------------- -Description of API is broken down into groups of functions to provide quick overview of features like: +Description of API is broken down into groups of functions to provide a quick overview of the following features: - Initialization of touch pad driver - Configuration of touch pad GPIO pins @@ -25,118 +25,120 @@ Description of API is broken down into groups of functions to provide quick over - Filtering measurements - Touch detection methods - Setting up interrupts to report touch detection -- Waking up from sleep mode on interrupt +- Waking up from Sleep mode on interrupt -For detailed description of particular function please go to section :ref:`touch_pad-api-reference`. Practical implementation of this API is covered in section :ref:`touch_pad-api-examples`. +For detailed description of a particular function, please go to Section :ref:`touch_pad-api-reference`. Practical implementation of this API is covered in Section :ref:`touch_pad-api-examples`. Initialization ^^^^^^^^^^^^^^ -Touch pad driver should be initialized before use by calling function :cpp:func:`touch_pad_init`. This function sets several ``.._DEFAULT`` driver parameters listed in :ref:`touch_pad-api-reference` under "Macros". It also clears information what pads have been touched before (if any) and disables interrupts. +Before using a touch pad, you need to initialize the touch pad driver by calling the function :cpp:func:`touch_pad_init`. This function sets several ``.._DEFAULT`` driver parameters listed in :ref:`touch_pad-api-reference` under *Macros*. It also removes the information about which pads have been touched before, if any, and disables interrupts. -If not required anymore, driver can be disabled by calling :cpp:func:`touch_pad_deinit`. +If the driver is not required anymore, deinitialize it by calling :cpp:func:`touch_pad_deinit`. Configuration ^^^^^^^^^^^^^ -Enabling of touch sensor functionality for particular GPIO is done with :cpp:func:`touch_pad_config`. +Enabling the touch sensor functionality for a particular GPIO is done with :cpp:func:`touch_pad_config`. -The function :cpp:func:`touch_pad_set_fsm_mode` is used to select whether touch pad measurement (operated by FSM) is started automatically by hardware timer, or by software. If software mode is selected, then use :cpp:func:`touch_pad_sw_start` to start of the FSM. +Use the function :cpp:func:`touch_pad_set_fsm_mode` to select if touch pad measurement (operated by FSM) should be started automatically by a hardware timer, or by software. If software mode is selected, use :cpp:func:`touch_pad_sw_start` to start the FSM. Touch State Measurements ^^^^^^^^^^^^^^^^^^^^^^^^ -The following two functions come handy to read raw or filtered measurements from the sensor: +The following two functions come in handy to read raw or filtered measurements from the sensor: * :cpp:func:`touch_pad_read` * :cpp:func:`touch_pad_read_filtered` -They may be used to characterize particular touch pad design by checking the range of sensor readings when a pad is touched or released. This information can be then used to establish the touch threshold. +They can also be used, for example, to evaluate a particular touch pad design by checking the range of sensor readings when a pad is touched or released. This information can be then used to establish a touch threshold. .. note:: - Start and configure filter before using :cpp:func:`touch_pad_read_filtered` by calling specific filter functions described down below. + Before using :cpp:func:`touch_pad_read_filtered`, you need to initialize and configure the filter by calling specific filter functions described in Section `Filtering of Measurements`_. -To see how to use both read functions check :example:`peripherals/touch_pad_read` application example. +For the demonstration of how to use both read functions, check the application example :example:`peripherals/touch_pad_read`. Optimization of Measurements ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Touch sensor has several configurable parameters to match characteristics of particular touch pad design. For instance, to sense smaller capacity changes, it is possible to narrow the reference voltage range within which the touch pads are charged / discharged. The high and low reference voltages are set using function :cpp:func:`touch_pad_set_voltage`. A positive side effect, besides ability to discern smaller capacity changes, will be reduction of power consumption for low power applications. A likely negative effect will be increase of measurement noise. If dynamic rage of obtained readings is still satisfactory, then further reduction of power consumption may be done by lowering the measurement time with :cpp:func:`touch_pad_set_meas_time`. +A touch sensor has several configurable parameters to match the characteristics of a particular touch pad design. For instance, to sense smaller capacity changes, it is possible to narrow down the reference voltage range within which the touch pads are charged / discharged. The high and low reference voltages are set using the function :cpp:func:`touch_pad_set_voltage`. -The following summarizes available measurement parameters and corresponding 'set' functions: +Besides the ability to discern smaller capacity changes, a positive side effect is reduction of power consumption for low power applications. A likely negative effect is an increase in measurement noise. If the dynamic range of obtained readings is still satisfactory, then further reduction of power consumption might be done by reducing the measurement time with :cpp:func:`touch_pad_set_meas_time`. + +The following list summarizes available measurement parameters and corresponding 'set' functions: * Touch pad charge / discharge parameters: * voltage range: :cpp:func:`touch_pad_set_voltage` * speed (slope): :cpp:func:`touch_pad_set_cnt_mode` -* Measure time: :cpp:func:`touch_pad_set_meas_time` +* Measurement time: :cpp:func:`touch_pad_set_meas_time` -Relationship between voltage range (high / low reference voltages), speed (slope) and measure time is shown on figure below. +Relationship between the voltage range (high / low reference voltages), speed (slope), and measurement time is shown in the figure below. .. figure:: ../../../_static/touch_pad-measurement-parameters.jpg :align: center :alt: Touch Pad - relationship between measurement parameters :figclass: align-center - Touch Pad - relationship between measurement parameters + Touch pad - relationship between measurement parameters -The last chart "Output" represents the touch sensor reading, i.e. the count of pulses collected within measure time. +The last chart *Output* represents the touch sensor reading, i.e., the count of pulses collected within the measurement time. -All functions are provided in pairs to 'set' specific parameter and to 'get' the current parameter's value, e.g. :cpp:func:`touch_pad_set_voltage` and :cpp:func:`touch_pad_get_voltage`. +All functions are provided in pairs to *set* a specific parameter and to *get* the current parameter's value, e.g., :cpp:func:`touch_pad_set_voltage` and :cpp:func:`touch_pad_get_voltage`. .. _touch_pad-api-filtering-of-measurements: Filtering of Measurements ^^^^^^^^^^^^^^^^^^^^^^^^^ -If measurements are noisy, you may filter them with provided API. The filter should be started before first use by calling :cpp:func:`touch_pad_filter_start`. +If measurements are noisy, you can filter them with provided API functions. Before using the filter, please start it by calling :cpp:func:`touch_pad_filter_start`. -The filter type is IIR (Infinite Impulse Response) and it has configurable period that can be set with function :cpp:func:`touch_pad_set_filter_period`. +The filter type is IIR (infinite impulse response), and it has a configurable period that can be set with the function :cpp:func:`touch_pad_set_filter_period`. -You can stop the filter with :cpp:func:`touch_pad_filter_stop`. If not required anymore, the filter may be deleted by invoking :cpp:func:`touch_pad_filter_delete`. +You can stop the filter with :cpp:func:`touch_pad_filter_stop`. If not required anymore, the filter can be deleted by invoking :cpp:func:`touch_pad_filter_delete`. Touch Detection ^^^^^^^^^^^^^^^ -Touch detection is implemented in ESP32's hardware basing on user configured threshold and raw measurements executed by FSM. Use function :cpp:func:`touch_pad_get_status` to check what pads have been touched and :cpp:func:`touch_pad_clear_status` to clear the touch status information. +Touch detection is implemented in ESP32's hardware based on the user-configured threshold and raw measurements executed by FSM. Use the functions :cpp:func:`touch_pad_get_status` to check which pads have been touched and :cpp:func:`touch_pad_clear_status` to clear the touch status information. -Hardware touch detection may be also wired to interrupts and this is described in next section. +Hardware touch detection can also be wired to interrupts. This is described in the next section. -If measurements are noisy and capacity changes small, then hardware touch detection may be not reliable. To resolve this issue, instead of using hardware detection / provided interrupts, implement measurement filtering and perform touch detection in your own application. See :example:`peripherals/touch_pad_interrupt` for sample implementation of both methods of touch detection. +If measurements are noisy and capacity changes are small, hardware touch detection might be unreliable. To resolve this issue, instead of using hardware detection / provided interrupts, implement measurement filtering and perform touch detection in your own application. For sample implementation of both methods of touch detection, see :example:`peripherals/touch_pad_interrupt`. Touch Triggered Interrupts ^^^^^^^^^^^^^^^^^^^^^^^^^^ -Before enabling an interrupt on touch detection, user should establish touch detection threshold. Use functions described above to read and display sensor measurements when pad is touched and released. Apply a filter when measurements are noisy and relative changes are small. Depending on your application and environmental conditions, test the influence of temperature and power supply voltage changes on measured values. +Before enabling an interrupt on a touch detection, you should establish a touch detection threshold. Use the functions described in `Touch State Measurements`_ to read and display sensor measurements when a pad is touched and released. Apply a filter if measurements are noisy and relative capacity changes are small. Depending on your application and environment conditions, test the influence of temperature and power supply voltage changes on measured values. -Once detection threshold is established, it may be set on initialization with :cpp:func:`touch_pad_config` or at the runtime with :cpp:func:`touch_pad_set_thresh`. +Once a detection threshold is established, it can be set during initialization with :cpp:func:`touch_pad_config` or at the runtime with :cpp:func:`touch_pad_set_thresh`. -In next step configure how interrupts are triggered. They may be triggered below or above threshold and this is set with function :cpp:func:`touch_pad_set_trigger_mode`. +In the next step, configure how interrupts are triggered. They can be triggered below or above the threshold, which is set with the function :cpp:func:`touch_pad_set_trigger_mode`. -Finally configure and manage interrupt calls using the following functions: +Finally, configure and manage interrupt calls using the following functions: * :cpp:func:`touch_pad_isr_register` / :cpp:func:`touch_pad_isr_deregister` * :cpp:func:`touch_pad_intr_enable` / :cpp:func:`touch_pad_intr_disable` -When interrupts are operational, you can obtain information what particular pad triggered interrupt by invoking :cpp:func:`touch_pad_get_status` and clear pad status with :cpp:func:`touch_pad_clear_status`. +When interrupts are operational, you can obtain the information from which particular pad an interrupt came by invoking :cpp:func:`touch_pad_get_status` and clear the pad status with :cpp:func:`touch_pad_clear_status`. .. note:: - Interrupts on touch detection operate on raw / unfiltered measurements checked against user established threshold and are implemented in hardware. Enabling software filtering API (see :ref:`touch_pad-api-filtering-of-measurements`) does not affect this process. + Interrupts on touch detection operate on raw / unfiltered measurements checked against user established threshold and are implemented in hardware. Enabling the software filtering API (see :ref:`touch_pad-api-filtering-of-measurements`) does not affect this process. Wakeup from Sleep Mode ^^^^^^^^^^^^^^^^^^^^^^ -If touch pad interrupts are used to wakeup the chip from a sleep mode, then user can select certain configuration of pads (SET1 or both SET1 and SET2), that should be touched to trigger the interrupt and cause subsequent wakeup. To do so, use function :cpp:func:`touch_pad_set_trigger_source`. +If touch pad interrupts are used to wake up the chip from a sleep mode, you can select a certain configuration of pads (SET1 or both SET1 and SET2) that should be touched to trigger the interrupt and cause the subsequent wakeup. To do so, use the function :cpp:func:`touch_pad_set_trigger_source`. Configuration of required bit patterns of pads may be managed for each 'SET' with: @@ -162,7 +164,7 @@ API Reference GPIO Lookup Macros ^^^^^^^^^^^^^^^^^^ -Some useful macros can be used to specified the GPIO number of a touchpad channel, or vice versa. +Some useful macros can be used to specified the GPIO number of a touch pad channel, or vice versa. e.g. 1. ``TOUCH_PAD_NUM5_GPIO_NUM`` is the GPIO number of channel 5 (12); From 752c7c32d460a92903adb50a60b33db729bfc28a Mon Sep 17 00:00:00 2001 From: Kirill Chalov Date: Mon, 24 Jun 2019 08:22:43 +0800 Subject: [PATCH 116/486] Review the file hw-reference/modules-and-boards.rst --- docs/en/hw-reference/modules-and-boards.rst | 182 +++++++++++++------- 1 file changed, 120 insertions(+), 62 deletions(-) diff --git a/docs/en/hw-reference/modules-and-boards.rst b/docs/en/hw-reference/modules-and-boards.rst index ecc9666c14..ddc41a7353 100644 --- a/docs/en/hw-reference/modules-and-boards.rst +++ b/docs/en/hw-reference/modules-and-boards.rst @@ -4,42 +4,44 @@ ESP32 Modules and Boards ************************ -Espressif designed and manufactured several development modules and boards to help users evaluate functionality of the ESP32 family of chips. Development boards, depending on intended functionality, have exposed GPIO pins headers, provide USB programming interface, JTAG interface as well as peripherals like touch pads, LCD screen, SD card slot, camera module header, etc. +Espressif designs and manufactures different modules and development boards to help users evaluate the potential of the ESP32 family of chips. -For details please refer to documentation below, provided together with description of particular boards. +This document provides description of modules and development boards currently available from Espressif. .. note:: - This section describes the latest versions of boards. Previous versions of boards, including these not produced anymore, are described in section :ref:`esp-modules-and-boards-previous`. + For description of previous versions of modules and development boards as well as for description of discontinued ones, please go to Section :ref:`esp-modules-and-boards-previous`. .. _esp-wroom-solo-wrover-modules: -WROOM, SOLO and WROVER Modules -============================== +WROOM, SOLO, WROVER, and PICO Modules +===================================== -A family of small modules that contain ESP32 chip on board together with some key components including a crystal oscillator and an antenna matching circuit. This makes it easier to provide an ESP32 based solution ready to integrate into final products. Such modules can be also used for evaluation after adding a few extra components like a programming interface, bootstrapping resistors and break out headers. The key characteristics of these modules are summarized in the following table. Some additional details are covered in the following chapters. +This is a family of ESP32-based modules with some integrated key components, including a crystal oscillator and an antenna matching circuit. The modules constitute ready-made solutions for integration into final products. If combined with a few extra components, such as a programming interface, bootstrapping resistors, and pin headers, these modules can also be used for evaluation of ESP32's functionality. -=============== ============ ===== ====== ==== --- Key Components ---------------- --------------------------------- -Module Chip Flash PSRAM Ant. -=============== ============ ===== ====== ==== -ESP32-WROOM-32 ESP32-D0WDQ6 4MB -- MIFA -ESP32-WROOM-32D ESP32-D0WD 4MB -- MIFA -ESP32-WROOM-32U ESP32-D0WD 4MB -- U.FL -ESP32-SOLO-1 ESP32-S0WD 4MB -- MIFA -ESP32-WROVER ESP32-D0WDQ6 4MB 8MB MIFA -ESP32-WROVER-I ESP32-D0WDQ6 4MB 8MB U.FL -ESP32-WROVER-B ESP32-D0WD 4MB 8MB MIFA -ESP32-WROVER-IB ESP32-D0WD 4MB 8MB U.FL -=============== ============ ===== ====== ==== +The key characteristics of these modules are summarized in the table below. Some additional details are covered in the following sections. -* ESP32-**D**.. denotes dual core, ESP32-**S**.. denotes single core chip -* MIFA - Meandered Inverted-F Antenna +=================== ============ =========== ========= ==== =============== + Key Components +------------------- ------------------------------------------ --------------- +Module Chip Flash, MB PSRAM, MB Ant. Dimensions, mm +=================== ============ =========== ========= ==== =============== +ESP32-WROOM-32 ESP32-D0WDQ6 4 -- MIFA 18 × 25.5 × 3.1 +ESP32-WROOM-32D ESP32-D0WD 4, 8, or 16 -- MIFA 18 × 25.5 × 3.1 +ESP32-WROOM-32U ESP32-D0WD 4, 8, or 16 -- U.FL 18 × 19.2 × 3.1 +ESP32-SOLO-1 ESP32-S0WD 4 -- MIFA 18 × 25.5 × 3.1 +ESP32-WROVER (PCB) ESP32-D0WDQ6 4 8 MIFA 18 × 31.4 × 3.3 +ESP32-WROVER (IPEX) ESP32-D0WDQ6 4 8 U.FL 18 × 31.4 × 3.3 +ESP32-WROVER-B ESP32-D0WD 4, 8, or 16 8 MIFA 18 × 31.4 × 3.3 +ESP32-WROVER-IB ESP32-D0WD 4, 8, or 16 8 U.FL 18 × 31.4 × 3.3 +=================== ============ =========== ========= ==== =============== + +* ESP32-**D**.. identifies a dual-core chip, ESP32-**S**.. identifies a single-core chip +* MIFA - Meandered Inverted-F Antenna * U.FL - U.FL / IPEX antenna connector -* ESP32-WROOM-x and ESP32-WROVER-x modules are also available with custom flash sizes of 8MB or 16MB, see `Espressif Products Ordering Information`_ (PDF) +* ESP32-WROOM-32x, ESP32-WROVER-B and ESP32-WROVER-IB modules come with 4 MB flash by default but also available with custom flash sizes of 8 MB and 16 MB, see `Espressif Products Ordering Information`_ (PDF) * `ESP32 Chip Datasheet `__ (PDF) -* Initial release of ESP32-WROVER module had 4MB of PSRAM +* Initial release of the ESP32-WROVER module had 4 MB of PSRAM * *ESP32-WROOM-32* was previously called *ESP-WROOM-32* @@ -48,7 +50,10 @@ ESP32-WROVER-IB ESP32-D0WD 4MB 8MB U.FL ESP32-WROOM-32 -------------- -A basic and commonly adopted ESP32 module with ESP32-D0WDQ6 chip on board. The first one of the WROOM / WROVER family released to the market. By default the module has 4MB flash and may be also ordered with custom flash size of 8 or 16MB, see `Espressif Products Ordering Information`_. +This is a basic and commonly adopted ESP32 module with the ESP32-D0WDQ6 chip on board. It was the first module of the WROOM / WROVER family released to the market. + +For key characteristics, see the table in Section :ref:`esp-wroom-solo-wrover-modules`, `Espressif Products Ordering Information`_. + .. figure:: https://dl.espressif.com/dl/schematics/pictures/esp32-wroom-32-front-back.jpg :align: center @@ -61,7 +66,7 @@ Documentation ^^^^^^^^^^^^^ * `ESP32-WROOM-32 Datasheet `__ (PDF) -* `ESP32-WROOM-32 Reference Design `_ containing OrCAD schematic, PCB layout, gerbers and BOM +* `ESP32-WROOM-32 Reference Design `_ containing OrCAD schematic, PCB layout, gerber and BOM files .. _esp-modules-and-boards-esp32-wroom-32d-and-u: @@ -69,7 +74,11 @@ Documentation ESP32-WROOM-32D / ESP32-WROOM-32U --------------------------------- -Both modules have ESP32-D0WD chip on board of a smaller footprint than ESP32-D0WDQ6 installed in :ref:`esp-modules-and-boards-esp32-wroom-32`. By default the module has 4MB flash and may be also ordered with custom flash size of 8 or 16MB, see `Espressif Products Ordering Information`_. Version "D" has a MIFA antenna. Version "U" has just an U.FL / IPEX antenna connector. That makes it 6.3 mm shorter comparing to "D", and also the smallest representative of the whole WROOM / WROVER family of modules. +Both modules integrate the ESP32-D0WD chip which has a smaller footprint than the chip ESP32-D0WDQ6 installed in :ref:`esp-modules-and-boards-esp32-wroom-32`. + +For key characteristics, see the table in Section :ref:`esp-wroom-solo-wrover-modules` and `Espressif Products Ordering Information`_. + +ESP32-WROOM-32U is the smallest representative of the whole WROOM / WROVER family of modules. .. figure:: https://dl.espressif.com/dl/schematics/pictures/esp32-wroom-32d-front-back.jpg :align: center @@ -96,7 +105,9 @@ Documentation ESP32-SOLO-1 ------------ -Simplified version of ESP32-WROOM-32D module. It contains a single core ESP32 chip that supports clock frequency of up to 160 MHz. +This is a simplified version of the ESP32-WROOM-32D module. It contains a single-core ESP32 chip that supports a clock frequency of up to 160 MHz. + +For key characteristics, see the table in Section :ref:`esp-wroom-solo-wrover-modules` and `Espressif Products Ordering Information`_. .. figure:: https://dl.espressif.com/dl/schematics/pictures/esp32-solo-1-front-back.jpg :align: center @@ -114,19 +125,17 @@ Documentation .. _esp-modules-and-boards-esp32-wrover: -ESP32-WROVER ------------- +ESP32-WROVER series +------------------- -A step upgrade of ESP32-WROOM-32x modules with an additional 8MB SPI PSRAM (Pseudo static RAM). +This series consists of a few modifications of ESP32-WROOM-32x modules, which among other upgrades include additional 8 MB SPI PSRAM (pseudo static RAM). -The module comes in couple of versions listed in section :ref:`esp-wroom-solo-wrover-modules`: +For details, see the table in Section :ref:`esp-wroom-solo-wrover-modules` and `Espressif Products Ordering Information`_. -* **ESP32-WROVER** and **ESP32-WROVER-I** have PSRAM that operates at 1.8V and can support up to 144 MHz clock rate. -* **ESP32-WROVER-B** and **ESP32-WROVER-IB** have PSRAM that operates at 3.3V and can support up to 133 MHz clock rate. +* **ESP32-WROVER (PCB)** and **ESP32-WROVER (IPEX)** have PSRAM that operates at 1.8 V and supports up to 144 MHz clock rate. +* **ESP32-WROVER-B** and **ESP32-WROVER-IB** have PSRAM that operates at 3.3 V and can supports up to 133 MHz clock rate. -By default the module has 4MB flash and may be also ordered with custom flash size of 8 or 16MB, see `Espressif Products Ordering Information`_. - -Depending on version the module has PCB antenna (shown below) or an U.FL / IPEX antenna connector. Because of additional components inside, this module is 5.9 mm longer than :ref:`esp-modules-and-boards-esp32-wroom-32`. +The picture below shows an ESP32-WROVER module with a PCB antenna. .. figure:: https://dl.espressif.com/dl/schematics/pictures/esp32-wrover.jpg :align: center @@ -139,16 +148,47 @@ Documentation ^^^^^^^^^^^^^ * `ESP32-WROVER Datasheet `__ (PDF) +* `ESP32-WROVER-B Datasheet `__ (PDF) * `ESP-PSRAM64 & ESP-PSRAM64H Datasheet `__ (PDF) -* `ESP32-WROVER Reference Design `_ containing OrCAD schematic, PCB layout, gerbers and BOM +* `ESP32-WROVER Reference Design `_ containing OrCAD schematic, PCB layout, gerber and BOM files +ESP32-PICO-D4 +------------- + +ESP32-PICO-D4 is a System-in-Package (SiP) module, integrating all peripheral components seamlessly, including the following: + +- 4 MB flash memory +- crystal oscillator +- filter capacitors +- RF matching circuit + +For key characteristics, see `Espressif Products Ordering Information`_. + + +Documentation +^^^^^^^^^^^^^ + +* `ESP32-PICO-D4 Datasheet `__ (PDF) + + +Development Boards +================== + +Depending on the intended functionality, different development boards feature: + +- Access to different ESP32 GPIO pins. +- Different interfaces: USB, JTAG. +- Different peripherals: touchpads, LCD screens, SD card slots, headers for camera modules, etc. + .. _esp-modules-and-boards-esp32-pico-kit: ESP32-PICO-KIT V4.1 -=================== +------------------- -The smallest ESP32 development board with all the components required to connect it directly to a PC USB port, and pin headers to plug into a mini breadboard. It is equipped with ESP32-PICO-D4 module that integrates 4 MB flash memory, a crystal oscillator, filter capacitors and RF matching circuit in one single package. As result, the fully functional development board requires only a few external components that can easy fit on a 20 x 52 mm PCB including antenna, LDO, USB-UART bridge and two buttons to reset it and put into download mode. +This is the smallest available ESP32-based development board. It features all the components for direct connection to a computer's USB port as well as pin headers for plugging into a mini breadboard. + +The board is equipped with the `ESP32-PICO-D4`_ module. With such a module, the creation of a fully functional development board required only a few external components that fit on a PCB as small as 20 x 52 mm. The external components include antenna, LDO, USB-UART bridge, and two buttons for reset and activation of Firmware Download mode. .. figure:: https://dl.espressif.com/dl/schematics/pictures/esp32-pico-kit-v4.1.jpg :align: center @@ -157,18 +197,18 @@ The smallest ESP32 development board with all the components required to connect ESP32-PICO-KIT V4.1 board -Comparing to ESP32-PICO-KIT V4, this version contains a more capable CP2102N USB-UART bridge that provides up to 3 Mbps transfers rates. +Comparing to ESP32-PICO-KIT V4, this version features the CP2102N USB-UART bridge that provides faster transfer rates of up to 3 Mbps. Documentation -------------- +^^^^^^^^^^^^^ -* :doc:`../get-started/get-started-pico-kit` +* :doc:`../get-started-cmake/get-started-pico-kit` * `ESP32-PICO-KIT V4.1 Schematic `_ (PDF) -* `ESP32-PICO-KIT Reference Design `_ containing OrCAD schematic, PCB layout, gerbers and BOM +* `ESP32-PICO-KIT Reference Design `_ containing OrCAD schematic, PCB layout, gerber and BOM files * `ESP32-PICO-D4 Datasheet `_ (PDF) Previous Versions ------------------ +^^^^^^^^^^^^^^^^^ * :ref:`esp-modules-and-boards-esp32-pico-kit-v4` * :ref:`esp-modules-and-boards-esp32-pico-kit-v3` @@ -177,9 +217,17 @@ Previous Versions .. _esp-modules-and-boards-esp32-devkitc: ESP32 DevKitC V4 -================ +---------------- -Small and convenient development board with :ref:`esp-modules-and-boards-esp32-wroom-32` module installed, break out pin headers and minimum additional components. Includes USB to serial programming interface, that also provides power supply for the board. Has pushbuttons to reset the board and put it in upload mode. Comparing to the previous :ref:`esp-modules-and-boards-esp32-devkitc-v2`, instead of ESP32-WROOM-32 it can accommodate :ref:`esp-modules-and-boards-esp32-wrover` module and has CP2102N chip that supports faster baud rates. +This is a small and convenient development board that features: + +- :ref:`esp-modules-and-boards-esp32-wroom-32` module +- USB-to-serial programming interface that also provides power supply for the board +- pin headers +- pushbuttons for reset and activation of Firmware Download mode +- a few other components + +Comparing to the previous :ref:`esp-modules-and-boards-esp32-devkitc-v2`, this version can integrate :ref:`esp-modules-and-boards-esp32-wrover` module instead of ESP32-WROOM-32 and has the CP2102N chip that supports faster baud rates. .. figure:: https://dl.espressif.com/dl/schematics/pictures/esp32-devkitc-v4-front.jpg :align: center @@ -189,15 +237,15 @@ Small and convenient development board with :ref:`esp-modules-and-boards-esp32-w ESP32 DevKitC V4 board Documentation -------------- +^^^^^^^^^^^^^ -* :doc:`../get-started/get-started-devkitc` +* :doc:`../get-started-cmake/get-started-devkitc` * `ESP32-DevKitC schematic `_ (PDF) -* `ESP32-DevKitC Reference Design `_ containing OrCAD schematic, PCB layout, gerbers and BOM +* `ESP32-DevKitC Reference Design `_ containing OrCAD schematic, PCB layout, gerber and BOM files * `CP210x USB to UART Bridge VCP Drivers `_ Previous Versions ------------------ +^^^^^^^^^^^^^^^^^ * :ref:`esp-modules-and-boards-esp32-devkitc-v2` @@ -205,18 +253,28 @@ Previous Versions .. _esp-modules-and-boards-esp-wrover-kit: ESP-WROVER-KIT V4.1 -=================== +------------------- -The ESP-WROVER-KIT V4.1 development board has dual port USB to serial converter for programming and JTAG interface for debugging. Power supply is provided by USB interface or from standard 5 mm power supply jack. Power supply selection is done with a jumper and may be put on/off with a separate switch. This board has MicroSD card slot, 3.2” SPI LCD screen and dedicated header to connect a camera. It provides RGB diode for diagnostics. Includes 32.768 kHz XTAL for internal RTC to operate it in low power modes. +This board features: -This version of ESP-WROVER-KIT board has ESP-WROVER-B module installed that integrates 64-MBit PSRAM for flexible extended storage and data processing capabilities. The board can accommodate other versions of ESP modules described under :ref:`esp-wroom-solo-wrover-modules`. +- dual port USB-to-serial converter for programming +- JTAG interface for debugging +- MicroSD card slot +- 3.2” SPI LCD screen +- header for a camera module +- RGB diode for diagnostics +- 32.768 kHz XTAL for internal RTC to operate it in low power modes + +Power can be supplied either via USB or via a standard 5 mm power supply jack. A power source can be selected with a jumper and can be turned on/off with a separate switch. + +This version of the ESP-WROVER-KIT board integrates the ESP-WROVER-B module that has 8 MB PSRAM for flexible extended storage and data processing capabilities. The board can accommodate other versions of ESP modules described in :ref:`esp-wroom-solo-wrover-modules`. Comparing to :ref:`esp-modules-and-boards-esp-wrover-kit-v3`, this board has the following design changes: - * JP8, JP11 and JP13 have been combined into a single JP2 - * USB connector has been changed to DIP type and moved to the lower right corner of the board - * R61 has been changed to 0R - * Some other components, e.g. EN and Boot buttons, have been replaced with functional equivalents basing on test results and sourcing options +- JP8, JP11, and JP13 have been combined into a single JP2. +- USB connector has been changed to DIP type and moved to the lower right corner of the board. +- R61 has been changed to a Zero-ohm resistor. +- Some components have been replaced with functional equivalents based on test results and sourcing options, e.g., the EN and Boot buttons. .. figure:: https://dl.espressif.com/dl/schematics/pictures/esp-wrover-kit-v4.1-front.jpg :align: center @@ -225,18 +283,18 @@ Comparing to :ref:`esp-modules-and-boards-esp-wrover-kit-v3`, this board has the ESP-WROVER-KIT V4.1 board -The board on picture above has ESP32-WROVER-B module is installed. +The board in the picture above integrates the ESP32-WROVER-B module. Documentation -------------- +^^^^^^^^^^^^^ -* :doc:`../get-started/get-started-wrover-kit` +* :doc:`../get-started-cmake/get-started-wrover-kit` * `ESP-WROVER-KIT V4.1 Schematic `__ (PDF) * :doc:`../api-guides/jtag-debugging/index` * `FTDI Virtual COM Port Drivers`_ Previous Versions ------------------ +^^^^^^^^^^^^^^^^^ * :ref:`esp-modules-and-boards-esp-wrover-kit-v3` * :ref:`esp-modules-and-boards-esp-wrover-kit-v2` From 32c3730c33bc74fafe5b28d9382bc39083d7da96 Mon Sep 17 00:00:00 2001 From: Kirill Chalov Date: Mon, 24 Jun 2019 08:24:44 +0800 Subject: [PATCH 117/486] Review the file api-guides/external-ram.rst --- docs/en/api-guides/external-ram.rst | 109 +++++++++++++--------------- 1 file changed, 52 insertions(+), 57 deletions(-) diff --git a/docs/en/api-guides/external-ram.rst b/docs/en/api-guides/external-ram.rst index f7c509abcd..2d8d694923 100644 --- a/docs/en/api-guides/external-ram.rst +++ b/docs/en/api-guides/external-ram.rst @@ -7,41 +7,38 @@ Support for external RAM Introduction ============ -The ESP32 has a few hundred KiB of internal RAM, residing on the same die as the rest of the ESP32. For some purposes, this is insufficient, -and therefore the ESP32 incorporates the ability to also use up to 4MiB of external SPI RAM memory as memory. The external memory is incorporated -in the memory map and is, within certain restrictions, usable in the same way internal data RAM is. +ESP32 has a few hundred kilobytes of internal RAM, residing on the same die as the rest of the chip components. It can be insufficient for some purposes, so ESP32 has the ability to also use up to 4 MB of external SPI RAM memory. The external memory is incorporated in the memory map and, with certain restrictions, is usable in the same way as internal data RAM. + Hardware ======== -The ESP32 supports SPI (P)SRAM connected in parallel with the SPI flash chip. While the ESP32 is capable of supporting several types -of RAM chips, the ESP32 SDK at the moment only supports the ESP-PSRAM32 chip. +ESP32 supports SPI PSRAM connected in parallel with the SPI flash chip. While ESP32 is capable of supporting several types of RAM chips, the ESP32 SDK only supports the ESP-PSRAM32 chip at the moment. -The ESP-PSRAM32 chip is an 1.8V device, and can only be used in parallel with an 1.8V flash part. Make sure to either set the MTDI -pin to a high signal level on bootup, or program the fuses in the ESP32 to always use a VDD_SIO level of 1.8V. Not doing this risks -damaging the PSRAM and/or flash chip. +The ESP-PSRAM32 chip is a 1.8 V device which can only be used in parallel with a 1.8 V flash component. Make sure to either set the MTDI pin to a high signal level on bootup, or program ESP32 eFuses to always use the VDD_SIO level of 1.8 V. Not doing this can damage the PSRAM and/or flash chip. -To connect the ESP-PSRAM chip to the ESP32D0W*, connect the following signals: - * PSRAM /CE (pin 1) - ESP32 GPIO 16 - * PSRAM SO (pin 2) - flash DO - * PSRAM SIO[2] (pin 3) - flash WP - * PSRAM SI (pin 5) - flash DI - * PSRAM SCLK (pin 6) - ESP32 GPIO 17 - * PSRAM SIO[3] (pin 7) - flash HOLD - * PSRAM Vcc (pin 8) - ESP32 VCC_SDIO +To connect the ESP-PSRAM32 chip to ESP32D0W*, connect the following signals: -Connections for the ESP32D2W* chips are TBD. + * PSRAM /CE (pin 1) > ESP32 GPIO 16 + * PSRAM SO (pin 2) > flash DO + * PSRAM SIO[2] (pin 3) > flash WP + * PSRAM SI (pin 5) > flash DI + * PSRAM SCLK (pin 6) > ESP32 GPIO 17 + * PSRAM SIO[3] (pin 7) > flash HOLD + * PSRAM Vcc (pin 8) > ESP32 VCC_SDIO + +Connections for ESP32D2W* chips are TBD. .. NOTE:: - Espressif sells an ESP-WROVER module which contains an ESP32, 1.8V flash and the ESP-PSRAM32 integrated in a module, ready for inclusion - on an end product PCB. + Espressif produces the line of ESP32-WROVER modules which contain ESP32, 1.8 V flash, and ESP-PSRAM32. These modules are ready to be mounted on an end product PCB. .. _external_ram_config: + Configuring External RAM ======================== -ESP-IDF fully supports using external memory in applications. ESP-IDF can be configured to handle external RAM in several ways after it is initialized at startup: +ESP-IDF fully supports the use of external memory in applications. Once the external RAM is initialized at startup, ESP-IDF can be configured to handle it in several ways: * :ref:`external_ram_config_memory_map` * :ref:`external_ram_config_capability_allocator` @@ -50,36 +47,39 @@ ESP-IDF fully supports using external memory in applications. ESP-IDF can be con .. _external_ram_config_memory_map: -Integrate RAM into ESP32 memory map ------------------------------------ + +Integrate RAM into the ESP32 memory map +--------------------------------------- Select this option by choosing "Integrate RAM into ESP32 memory map" from :ref:`CONFIG_SPIRAM_USE`. -This is the most basic option for external SPIRAM integration. Most users will want one of the other, more advanced, options. +This is the most basic option for external SPI RAM integration. Most likely, you will need another, more advanced option. -During ESP-IDF startup, external RAM is mapped into the data address space starting at at address 0x3F800000 (byte-accessible). The length of this region is the same as the SPIRAM size (up to the limit of 4MiB). +During the ESP-IDF startup, external RAM is mapped into the data address space, starting at address 0x3F800000 (byte-accessible). The length of this region is the same as the SPI RAM size (up to the limit of 4 MB). -The application can manually place data in external memory by creating pointers to this region. The application is responsible for all management of the external SPIRAM: coordinating buffer usage, preventing corruption, etc. +Applications can manually place data in external memory by creating pointers to this region. So if an application uses external memory, it is responsible for all management of the external SPI RAM: coordinating buffer usage, preventing corruption, etc. .. _external_ram_config_capability_allocator: + Add external RAM to the capability allocator -------------------------------------------- Select this option by choosing "Make RAM allocatable using heap_caps_malloc(..., MALLOC_CAP_SPIRAM)" from :ref:`CONFIG_SPIRAM_USE`. -When enabled, memory is mapped to address 0x3F800000 but also added to the :doc:`capabilities-based heap memory allocator ` using ``MALLOC_CAP_SPIRAM``. +When enabled, memory is mapped to address 0x3F800000 and also added to the :doc:`capabilities-based heap memory allocator ` using ``MALLOC_CAP_SPIRAM``. To allocate memory from external RAM, a program should call ``heap_caps_malloc(size, MALLOC_CAP_SPIRAM)``. After use, this memory can be freed by calling the normal ``free()`` function. .. _external_ram_config_malloc: + Provide external RAM via malloc() --------------------------------- -Select this option by choosing "Make RAM allocatable using malloc() as well" from :ref:`CONFIG_SPIRAM_USE`. This is the default selection. +Select this option by choosing "Make RAM allocatable using malloc() as well" from :ref:`CONFIG_SPIRAM_USE`. This is the default option. -Using this option, memory is added to the capability allocator as described for the previous option. However it is also added to the pool of RAM that can be returned by standard ``malloc()``. +In this case, memory is added to the capability allocator as described for the previous option. However, it is also added to the pool of RAM that can be returned by the standard ``malloc()`` function. This allows any application to use the external RAM without having to rewrite the code to use ``heap_caps_malloc(..., MALLOC_CAP_SPIRAM)``. @@ -88,65 +88,60 @@ An additional configuration item, :ref:`CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL`, ca - When allocating a size less than the threshold, the allocator will try internal memory first. - When allocating a size equal to or larger than the threshold, the allocator will try external memory first. -If a suitable block of preferred internal/external memory is not available, allocation will try the other type of memory. +If a suitable block of preferred internal/external memory is not available, the allocator will try the other type of memory. Because some buffers can only be allocated in internal memory, a second configuration item :ref:`CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL` defines a pool of internal memory which is reserved for *only* explicitly internal allocations (such as memory for DMA use). Regular ``malloc()`` will not allocate from this pool. The :ref:`MALLOC_CAP_DMA ` and ``MALLOC_CAP_INTERNAL`` flags can be used to allocate memory from this pool. .. _external_ram_config_bss: + Allow .bss segment placed in external memory -------------------------------------------- -Enable this option by setting :ref:`CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY`. This configuration setting is independent of the other three. +Enable this option by checking :ref:`CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY`. This configuration setting is independent of the other three. -If enabled, a region of the address space starting from 0x3F800000 will be to used to store zero initialized data (BSS segment) from the lwip, net80211, libpp and bluedroid ESP-IDF libraries. +If enabled, a region of the address space starting from 0x3F800000 will be used to store zero-initialized data (BSS segment) from the lwIP, net80211, libpp, and bluedroid ESP-IDF libraries. -Additional data can be moved from the internal BSS segment to external RAM by applying the ``EXT_RAM_ATTR`` macro to any static declaration (which is not initialized to a non-zero value). +Additional data can be moved from the internal BSS segment to external RAM by applying the macro ``EXT_RAM_ATTR`` to any static declaration (which is not initialized to a non-zero value). This option reduces the internal static memory used by the BSS segment. -Remaining external RAM can also be added to the capability heap allocator, by the method shown above. +Remaining external RAM can also be added to the capability heap allocator using the method shown above. + Restrictions ============ External RAM use has the following restrictions: - * When flash cache is disabled (for example, because the flash is being written to), the external RAM also becomes inaccessible; any reads from or - writes to it will lead to an illegal cache access exception. This is also the reason that ESP-IDF does not by default allocate any task stacks in external RAM (see below). - * External RAM cannot be used as a place to store DMA transaction descriptors or as a buffer for a DMA transfer to read from or write into. Any - buffers that will be used in combination with DMA must be allocated using ``heap_caps_malloc(size, MALLOC_CAP_DMA)`` (and can be freed using a - standard ``free()`` call.) - * External RAM uses the same cache region as the external flash. This means that often accessed variables in external RAM can be read and - modified almost as quickly as in internal ram. However, when accessing large chunks of data (>32K), the cache can be insufficient and speeds - will fall back to the access speed of the external RAM. Moreover, accessing large chunks of data can 'push out' cached flash, possibly making - execution of code afterwards slower. - * External RAM cannot be used as task stack memory. Because of this, :cpp:func:`xTaskCreate` and similar functions will always allocate internal memory - for stack and task TCBs and functions like :cpp:func:`xTaskCreateStatic` will check if the buffers passed are internal. However, for tasks not calling - on code in ROM in any way, directly or indirectly, the menuconfig option :ref:`CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY` will eliminate - the check in xTaskCreateStatic, allowing a task's stack to be in external RAM. Using this is not advised, however. - * By default, failure to initialize external RAM will cause ESP-IDF startup to abort. This can be disabled by enabling config item :ref:`CONFIG_SPIRAM_IGNORE_NOTFOUND`. If :ref:`CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY` is enabled, the option to ignore failure is not available as the linker will have assigned symbols to external memory addresses at link time. - * When used at 80MHz clock speed, external RAM must also occupy either the HSPI or VSPI bus. Select which SPI host will be used by :ref:`CONFIG_SPIRAM_OCCUPY_SPI_HOST`. + * When flash cache is disabled (for example, if the flash is being written to), the external RAM also becomes inaccessible; any reads from or writes to it will lead to an illegal cache access exception. This is also the reason why ESP-IDF does not by default allocate any task stacks in external RAM (see below). + * External RAM cannot be used as a place to store DMA transaction descriptors or as a buffer for a DMA transfer to read from or write into. Any buffers that will be used in combination with DMA must be allocated using ``heap_caps_malloc(size, MALLOC_CAP_DMA)`` and can be freed using a standard ``free()`` call. + * External RAM uses the same cache region as the external flash. This means that frequently accessed variables in external RAM can be read and modified almost as quickly as in internal ram. However, when accessing large chunks of data (>32 KB), the cache can be insufficient, and speeds will fall back to the access speed of the external RAM. Moreover, accessing large chunks of data can "push out" cached flash, possibly making the execution of code slower afterwards. + * External RAM cannot be used as task stack memory. Due to this, :cpp:func:`xTaskCreate` and similar functions will always allocate internal memory for stack and task TCBs, and functions such as :cpp:func:`xTaskCreateStatic` will check if the buffers passed are internal. However, for tasks not calling on code in ROM in any way, directly or indirectly, the menuconfig option :ref:`CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY` will eliminate the check in xTaskCreateStatic, allowing a task's stack to be in external RAM. Using this is not advised, however. + * By default, failure to initialize external RAM will cause the ESP-IDF startup to abort. This can be disabled by enabling the config item :ref:`CONFIG_SPIRAM_IGNORE_NOTFOUND`. If :ref:`CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY` is enabled, the option to ignore failure is not available as the linker will have assigned symbols to external memory addresses at link time. + * When used at 80 MHz clock speed, external RAM must also occupy either the HSPI or VSPI bus. Select which SPI host will be used by :ref:`CONFIG_SPIRAM_OCCUPY_SPI_HOST`. + Chip revisions ============== -There are some issues with certain revisions of the ESP32 that have repercussions for use with external RAM. These are documented in the ESP32 -ECO_ document. In particular, ESP-IDF handles the bugs mentioned in the following ways: +There are some issues with certain revisions of ESP32 that have repercussions for use with external RAM. The issues are documented in the ESP32 ECO_ document. In particular, ESP-IDF handles the bugs mentioned in the following ways: + ESP32 rev v0 ------------ -ESP-IDF has no workaround for the bugs in this revision of silicon, and it cannot be used to map external PSRAM into the ESP32s main memory map. +ESP-IDF has no workaround for the bugs in this revision of silicon, and it cannot be used to map external PSRAM into ESP32's main memory map. + ESP32 rev v1 ------------ -The bugs in this silicon revision introduce a hazard when certain sequences of machine instructions operate on external memory locations (ESP32 ECO 3.2). -To work around this, the gcc compiler to compile ESP-IDF has been expanded with a flag: ``-mfix-esp32-psram-cache-issue``. With this flag passed to gcc -on the command line, the compiler works around these sequences and only outputs code that can safely be executed. +The bugs in this revision of silicon cause issues if certain sequences of machine instructions operate on external memory. (ESP32 ECO 3.2). As a workaround, the GCC compiler received the flag ``-mfix-esp32-psram-cache-issue`` to filter these sequences and only output the code that can safely be executed. Enable this flag by checking :ref:`CONFIG_SPIRAM_CACHE_WORKAROUND`. + +Aside from linking to a recompiled version of Newlib with the additional flag, ESP-IDF also does the following: + +- Avoids using some ROM functions +- Allocates static memory for the WiFi stack -In ESP-IDF, this flag is enabled when you select :ref:`CONFIG_SPIRAM_CACHE_WORKAROUND`. ESP-IDF also takes other measures to make -sure no combination of PSRAM access plus the offending instruction sets are used: it links to a version of Newlib recompiled with the gcc flag, doesn't use -some ROM functions and allocates static memory for the WiFi stack. .. _ECO: https://www.espressif.com/sites/default/files/documentation/eco_and_workarounds_for_bugs_in_esp32_en.pdf From fc3253163e2a6785621fd27e0458b9106c929ddf Mon Sep 17 00:00:00 2001 From: Island Date: Mon, 7 Jan 2019 15:16:47 +0800 Subject: [PATCH 118/486] component/ble_mesh: ESP BLE Mesh release 1. BLE Mesh Core * Provisioning: Node Role * Advertising and GATT bearer * Authentication OOB * Provisioning: Provisioner Role * Advertising and GATT bearer * Authentication OOB * Networking * Relay * Segmentation and Reassembly * Key Refresh * IV Update * Proxy Support * Multiple Client Models Run Simultaneously * Support multiple client models send packets to different nodes simultaneously * No blocking between client model and server * NVS Storage * Store Provisioning Data of BLE Mesh Nodes in Flash 2. BLE Mesh Applications * BLE Mesh Node & Provisioner * Node Example * Provisioner Example * Node + Generic OnOff Client Example * Fast Provisioning * Vendor Fast Prov Server Model * Vendor Fast Prov Client Model * Examples * Wi-Fi & BLE Mesh Coexistence * Example * BLE Mesh Console Commands * Example 3. BLE Mesh Models * Foundation Models * Configuration Server Model * Configuration Client Model * Health Server Model * Health Client Model * Generic Client Models * Generic OnOff Client * Generic Level Client * Generic Location Client * Generic Default Transition Timer Client * Generic Power OnOff Client * Generic Power Level Client * Generic Battery Client * Generic Property Client * Generic Server Models * Generic OnOff Server (Example) * Lighting Client Models * Light Lightness Client * Light CTL Client * Light HSL Client * Light xyL Client * Light LC Client * Sensor Client Model * Sensor Client * Time and Scenes Client Models * Time Client * Scene Client * Scheduler Client --- components/bt/CMakeLists.txt | 69 + components/bt/Kconfig | 811 ++++ .../api/core/esp_ble_mesh_common_api.c | 70 + .../esp_ble_mesh_local_data_operation_api.c | 80 + .../api/core/esp_ble_mesh_low_power_api.c | 26 + .../api/core/esp_ble_mesh_networking_api.c | 338 ++ .../api/core/esp_ble_mesh_provisioning_api.c | 423 ++ .../api/core/esp_ble_mesh_proxy_api.c | 65 + .../core/include/esp_ble_mesh_common_api.h | 37 + .../esp_ble_mesh_local_data_operation_api.h | 113 + .../core/include/esp_ble_mesh_low_power_api.h | 20 + .../include/esp_ble_mesh_networking_api.h | 267 ++ .../include/esp_ble_mesh_provisioning_api.h | 316 ++ .../api/core/include/esp_ble_mesh_proxy_api.h | 59 + .../bt/ble_mesh/api/esp_ble_mesh_defs.h | 1524 +++++++ .../models/esp_ble_mesh_config_model_api.c | 82 + .../models/esp_ble_mesh_generic_model_api.c | 75 + .../models/esp_ble_mesh_health_model_api.c | 98 + .../models/esp_ble_mesh_lighting_model_api.c | 76 + .../models/esp_ble_mesh_sensor_model_api.c | 76 + .../esp_ble_mesh_time_scene_model_api.c | 76 + .../include/esp_ble_mesh_config_model_api.h | 670 +++ .../include/esp_ble_mesh_generic_model_api.h | 478 +++ .../include/esp_ble_mesh_health_model_api.h | 261 ++ .../include/esp_ble_mesh_lighting_model_api.h | 526 +++ .../include/esp_ble_mesh_sensor_model_api.h | 230 ++ .../esp_ble_mesh_time_scene_model_api.h | 292 ++ .../ble_mesh/btc/btc_ble_mesh_config_model.c | 725 ++++ .../ble_mesh/btc/btc_ble_mesh_generic_model.c | 540 +++ .../ble_mesh/btc/btc_ble_mesh_health_model.c | 592 +++ .../btc/btc_ble_mesh_lighting_model.c | 381 ++ .../bt/ble_mesh/btc/btc_ble_mesh_prov.c | 1528 +++++++ .../ble_mesh/btc/btc_ble_mesh_sensor_model.c | 632 +++ .../btc/btc_ble_mesh_time_scene_model.c | 383 ++ .../btc/include/btc_ble_mesh_config_model.h | 64 + .../btc/include/btc_ble_mesh_generic_model.h | 52 + .../btc/include/btc_ble_mesh_health_model.h | 78 + .../btc/include/btc_ble_mesh_lighting_model.h | 53 + .../ble_mesh/btc/include/btc_ble_mesh_prov.h | 210 + .../btc/include/btc_ble_mesh_sensor_model.h | 53 + .../include/btc_ble_mesh_time_scene_model.h | 53 + components/bt/ble_mesh/mesh_core/access.c | 1043 +++++ components/bt/ble_mesh/mesh_core/access.h | 60 + components/bt/ble_mesh/mesh_core/adv.c | 411 ++ components/bt/ble_mesh/mesh_core/adv.h | 86 + components/bt/ble_mesh/mesh_core/beacon.c | 422 ++ components/bt/ble_mesh/mesh_core/beacon.h | 24 + components/bt/ble_mesh/mesh_core/cfg_cli.c | 1657 ++++++++ components/bt/ble_mesh/mesh_core/cfg_srv.c | 3593 +++++++++++++++++ components/bt/ble_mesh/mesh_core/crypto.c | 878 ++++ components/bt/ble_mesh/mesh_core/crypto.h | 166 + components/bt/ble_mesh/mesh_core/foundation.h | 166 + components/bt/ble_mesh/mesh_core/friend.c | 1326 ++++++ components/bt/ble_mesh/mesh_core/friend.h | 49 + components/bt/ble_mesh/mesh_core/health_cli.c | 462 +++ components/bt/ble_mesh/mesh_core/health_srv.c | 529 +++ .../bt/ble_mesh/mesh_core/include/cfg_cli.h | 297 ++ .../bt/ble_mesh/mesh_core/include/cfg_srv.h | 72 + .../ble_mesh/mesh_core/include/health_cli.h | 78 + .../ble_mesh/mesh_core/include/health_srv.h | 93 + .../ble_mesh/mesh_core/include/mesh_access.h | 444 ++ .../mesh_core/include/mesh_aes_encrypt.h | 171 + .../ble_mesh/mesh_core/include/mesh_atomic.h | 305 ++ .../mesh_core/include/mesh_bearer_adapt.h | 733 ++++ .../bt/ble_mesh/mesh_core/include/mesh_buf.h | 1064 +++++ .../ble_mesh/mesh_core/include/mesh_dlist.h | 496 +++ .../bt/ble_mesh/mesh_core/include/mesh_hci.h | 134 + .../ble_mesh/mesh_core/include/mesh_kernel.h | 275 ++ .../bt/ble_mesh/mesh_core/include/mesh_main.h | 584 +++ .../ble_mesh/mesh_core/include/mesh_proxy.h | 37 + .../ble_mesh/mesh_core/include/mesh_slist.h | 468 +++ .../ble_mesh/mesh_core/include/mesh_trace.h | 131 + .../ble_mesh/mesh_core/include/mesh_types.h | 46 + .../bt/ble_mesh/mesh_core/include/mesh_util.h | 440 ++ .../bt/ble_mesh/mesh_core/include/mesh_uuid.h | 530 +++ components/bt/ble_mesh/mesh_core/lpn.c | 1057 +++++ components/bt/ble_mesh/mesh_core/lpn.h | 67 + components/bt/ble_mesh/mesh_core/mesh.h | 22 + .../bt/ble_mesh/mesh_core/mesh_aes_encrypt.c | 409 ++ .../bt/ble_mesh/mesh_core/mesh_atomic.c | 179 + .../bt/ble_mesh/mesh_core/mesh_bearer_adapt.c | 1858 +++++++++ components/bt/ble_mesh/mesh_core/mesh_buf.c | 453 +++ components/bt/ble_mesh/mesh_core/mesh_hci.c | 45 + .../bt/ble_mesh/mesh_core/mesh_kernel.c | 205 + components/bt/ble_mesh/mesh_core/mesh_main.c | 509 +++ components/bt/ble_mesh/mesh_core/mesh_util.c | 86 + components/bt/ble_mesh/mesh_core/net.c | 1517 +++++++ components/bt/ble_mesh/mesh_core/net.h | 388 ++ components/bt/ble_mesh/mesh_core/prov.c | 1774 ++++++++ components/bt/ble_mesh/mesh_core/prov.h | 34 + .../ble_mesh/mesh_core/provisioner_beacon.c | 71 + .../ble_mesh/mesh_core/provisioner_beacon.h | 20 + .../bt/ble_mesh/mesh_core/provisioner_main.c | 1278 ++++++ .../bt/ble_mesh/mesh_core/provisioner_main.h | 122 + .../bt/ble_mesh/mesh_core/provisioner_prov.c | 3287 +++++++++++++++ .../bt/ble_mesh/mesh_core/provisioner_prov.h | 379 ++ .../bt/ble_mesh/mesh_core/provisioner_proxy.c | 608 +++ .../bt/ble_mesh/mesh_core/provisioner_proxy.h | 89 + components/bt/ble_mesh/mesh_core/proxy.c | 1393 +++++++ components/bt/ble_mesh/mesh_core/proxy.h | 51 + components/bt/ble_mesh/mesh_core/settings.c | 1578 ++++++++ components/bt/ble_mesh/mesh_core/settings.h | 39 + .../mesh_core/settings/settings_nvs.c | 374 ++ .../mesh_core/settings/settings_nvs.h | 44 + components/bt/ble_mesh/mesh_core/test.c | 131 + components/bt/ble_mesh/mesh_core/test.h | 43 + components/bt/ble_mesh/mesh_core/transport.c | 1681 ++++++++ components/bt/ble_mesh/mesh_core/transport.h | 102 + .../bt/ble_mesh/mesh_docs/BLE-Mesh_FAQs_EN.md | 9 + .../mesh_docs/BLE-Mesh_Feature_List_EN.md | 89 + .../mesh_docs/BLE-Mesh_Getting_Started_EN.md | 155 + .../mesh_docs/BLE-Mesh_Known_Issues_EN.md | 1 + components/bt/ble_mesh/mesh_docs/README.md | 50 + .../bt/ble_mesh/mesh_models/generic_client.c | 1225 ++++++ .../mesh_models/include/generic_client.h | 491 +++ .../mesh_models/include/lighting_client.h | 492 +++ .../mesh_models/include/mesh_common.h | 46 + .../mesh_models/include/model_common.h | 133 + .../mesh_models/include/model_opcode.h | 276 ++ .../mesh_models/include/sensor_client.h | 167 + .../mesh_models/include/time_scene_client.h | 257 ++ .../bt/ble_mesh/mesh_models/lighting_client.c | 1400 +++++++ .../bt/ble_mesh/mesh_models/mesh_common.c | 46 + .../bt/ble_mesh/mesh_models/model_common.c | 336 ++ .../bt/ble_mesh/mesh_models/sensor_client.c | 616 +++ .../ble_mesh/mesh_models/time_scene_client.c | 694 ++++ components/bt/bluedroid/btc/core/btc_task.c | 21 + .../bt/bluedroid/btc/include/btc/btc_task.h | 12 + components/bt/component.mk | 23 +- docs/en/COPYRIGHT.rst | 2 + .../ble_mesh_client_model/CMakeLists.txt | 6 + .../ble_mesh/ble_mesh_client_model/Makefile | 10 + .../ble_mesh/ble_mesh_client_model/README.md | 14 + .../ble_mesh_client_model/main/CMakeLists.txt | 6 + .../main/Kconfig.projbuild | 22 + .../main/ble_mesh_demo_main.c | 532 +++ .../ble_mesh_client_model/main/board.c | 115 + .../ble_mesh_client_model/main/board.h | 42 + .../ble_mesh_client_model/main/component.mk | 4 + .../ble_mesh_client_model/sdkconfig.defaults | 45 + .../tutorial/ble_mesh_client_model.md | 252 ++ .../tutorial/images/app.png | Bin 0 -> 443575 bytes .../tutorial/images/message.png | Bin 0 -> 67886 bytes .../tutorial/images/picture5.png | Bin 0 -> 26425 bytes .../ble_mesh_node/CMakeLists.txt | 6 + .../ble_mesh_console/ble_mesh_node/Makefile | 10 + .../ble_mesh_console/ble_mesh_node/README.md | 9 + .../ble_mesh_node/main/CMakeLists.txt | 12 + .../ble_mesh_node/main/ble_mesh_adapter.c | 164 + .../ble_mesh_node/main/ble_mesh_adapter.h | 97 + .../main/ble_mesh_cfg_srv_model.c | 208 + .../main/ble_mesh_cfg_srv_model.h | 107 + .../main/ble_mesh_console_decl.h | 28 + .../ble_mesh_node/main/ble_mesh_console_lib.c | 128 + .../ble_mesh_node/main/ble_mesh_console_lib.h | 31 + .../main/ble_mesh_console_main.c | 215 + .../main/ble_mesh_console_system.c | 183 + .../main/ble_mesh_register_node_cmd.c | 547 +++ .../main/ble_mesh_register_server_cmd.c | 83 + .../ble_mesh_node/main/component.mk | 5 + .../ble_mesh_node/main/register_bluetooth.c | 45 + .../ble_mesh_node/sdkconfig.defaults | 46 + .../ble_mesh_provisioner/CMakeLists.txt | 6 + .../ble_mesh_provisioner/Makefile | 10 + .../ble_mesh_provisioner/README.md | 10 + .../ble_mesh_provisioner/main/CMakeLists.txt | 15 + .../main/ble_mesh_adapter.c | 300 ++ .../main/ble_mesh_adapter.h | 123 + .../main/ble_mesh_cfg_srv_model.c | 205 + .../main/ble_mesh_cfg_srv_model.h | 107 + .../main/ble_mesh_console_decl.h | 38 + .../main/ble_mesh_console_lib.c | 124 + .../main/ble_mesh_console_lib.h | 29 + .../main/ble_mesh_console_main.c | 228 ++ .../main/ble_mesh_console_system.c | 179 + .../main/ble_mesh_reg_cfg_client_cmd.c | 390 ++ .../main/ble_mesh_reg_gen_onoff_client_cmd.c | 180 + .../main/ble_mesh_reg_test_perf_client_cmd.c | 219 + .../main/ble_mesh_register_node_cmd.c | 476 +++ .../main/ble_mesh_register_provisioner_cmd.c | 424 ++ .../ble_mesh_provisioner/main/component.mk | 5 + .../main/register_bluetooth.c | 45 + .../ble_mesh_provisioner/sdkconfig.defaults | 40 + .../ble_mesh_fast_prov_client/CMakeLists.txt | 8 + .../ble_mesh_fast_prov_client/Makefile | 12 + .../ble_mesh_fast_prov_client/README.md | 2 + .../main/CMakeLists.txt | 5 + .../main/ble_mesh_demo_main.c | 638 +++ .../main/component.mk | 4 + .../sdkconfig.defaults | 43 + .../ble_mesh_fast_provision_client.md | 218 + .../ble_mesh_fast_prov_server/CMakeLists.txt | 8 + .../ble_mesh_fast_prov_server/Makefile | 12 + .../ble_mesh_fast_prov_server/README.md | 2 + .../main/CMakeLists.txt | 6 + .../main/Kconfig.projbuild | 16 + .../main/ble_mesh_demo_main.c | 842 ++++ .../ble_mesh_fast_prov_server/main/board.c | 72 + .../ble_mesh_fast_prov_server/main/board.h | 47 + .../main/component.mk | 4 + .../sdkconfig.defaults | 51 + .../tutorial/EspBleMesh.md | 93 + .../ble_mesh_fast_provision_server.md | 409 ++ .../tutorial/images/app_ble.png | Bin 0 -> 182199 bytes .../tutorial/images/device.png | Bin 0 -> 356234 bytes .../tutorial/images/picture1.png | Bin 0 -> 73063 bytes .../tutorial/images/picture2.png | Bin 0 -> 69619 bytes .../tutorial/images/time.png | Bin 0 -> 56788 bytes .../ble_mesh/ble_mesh_node/CMakeLists.txt | 6 + .../bluetooth/ble_mesh/ble_mesh_node/Makefile | 10 + .../ble_mesh/ble_mesh_node/README.md | 16 + .../ble_mesh_node/main/CMakeLists.txt | 6 + .../ble_mesh_node/main/Kconfig.projbuild | 22 + .../ble_mesh_node/main/ble_mesh_demo_main.c | 395 ++ .../ble_mesh/ble_mesh_node/main/board.c | 130 + .../ble_mesh/ble_mesh_node/main/board.h | 42 + .../ble_mesh/ble_mesh_node/main/component.mk | 4 + .../ble_mesh/ble_mesh_node/sdkconfig.defaults | 43 + .../Ble_Mesh_Node_Example_Walkthrough.md | 471 +++ .../ble_mesh_provisioner/CMakeLists.txt | 6 + .../ble_mesh/ble_mesh_provisioner/Makefile | 10 + .../ble_mesh/ble_mesh_provisioner/README.md | 6 + .../ble_mesh_provisioner/main/CMakeLists.txt | 5 + .../main/ble_mesh_demo_main.c | 691 ++++ .../ble_mesh_provisioner/main/component.mk | 4 + .../ble_mesh_provisioner/sdkconfig.defaults | 40 + ...le_Mesh_Provisioner_Example_Walkthrough.md | 129 + .../fast_prov_vendor_model/CMakeLists.txt | 6 + .../fast_prov_vendor_model/Makefile | 10 + .../components/CMakeLists.txt | 9 + .../components/component.mk | 6 + .../components/esp_fast_prov_client_model.c | 407 ++ .../components/esp_fast_prov_client_model.h | 36 + .../components/esp_fast_prov_common.h | 121 + .../components/esp_fast_prov_operation.c | 577 +++ .../components/esp_fast_prov_operation.h | 69 + .../components/esp_fast_prov_server_model.c | 640 +++ .../components/esp_fast_prov_server_model.h | 102 + .../main/CMakeLists.txt | 3 + .../fast_prov_vendor_model/main/component.mk | 5 + .../fast_prov_vendor_model/main/main.c | 21 + .../fast_prov_vendor_model/sdkconfig.defaults | 33 + .../ble_mesh_wifi_coexist/CMakeLists.txt | 8 + .../ble_mesh/ble_mesh_wifi_coexist/Makefile | 12 + .../ble_mesh/ble_mesh_wifi_coexist/README.md | 20 + .../components/iperf/CMakeLists.txt | 8 + .../components/iperf/cmd_decl.h | 14 + .../components/iperf/cmd_wifi.c | 477 +++ .../components/iperf/component.mk | 11 + .../components/iperf/iperf.c | 461 +++ .../components/iperf/iperf.h | 61 + .../ble_mesh_wifi_coexist/main/CMakeLists.txt | 6 + .../main/Kconfig.projbuild | 16 + .../main/ble_mesh_demo_main.c | 977 +++++ .../ble_mesh_wifi_coexist/main/board.c | 72 + .../ble_mesh_wifi_coexist/main/board.h | 47 + .../ble_mesh_wifi_coexist/main/component.mk | 5 + .../ble_mesh_wifi_coexist/partitions.csv | 6 + .../ble_mesh_wifi_coexist/sdkconfig.defaults | 82 + .../tutorial/ble_mesh_wifi_coexist.md | 301 ++ 260 files changed, 71487 insertions(+), 1 deletion(-) create mode 100644 components/bt/ble_mesh/api/core/esp_ble_mesh_common_api.c create mode 100644 components/bt/ble_mesh/api/core/esp_ble_mesh_local_data_operation_api.c create mode 100644 components/bt/ble_mesh/api/core/esp_ble_mesh_low_power_api.c create mode 100644 components/bt/ble_mesh/api/core/esp_ble_mesh_networking_api.c create mode 100644 components/bt/ble_mesh/api/core/esp_ble_mesh_provisioning_api.c create mode 100644 components/bt/ble_mesh/api/core/esp_ble_mesh_proxy_api.c create mode 100644 components/bt/ble_mesh/api/core/include/esp_ble_mesh_common_api.h create mode 100644 components/bt/ble_mesh/api/core/include/esp_ble_mesh_local_data_operation_api.h create mode 100644 components/bt/ble_mesh/api/core/include/esp_ble_mesh_low_power_api.h create mode 100644 components/bt/ble_mesh/api/core/include/esp_ble_mesh_networking_api.h create mode 100644 components/bt/ble_mesh/api/core/include/esp_ble_mesh_provisioning_api.h create mode 100644 components/bt/ble_mesh/api/core/include/esp_ble_mesh_proxy_api.h create mode 100644 components/bt/ble_mesh/api/esp_ble_mesh_defs.h create mode 100644 components/bt/ble_mesh/api/models/esp_ble_mesh_config_model_api.c create mode 100644 components/bt/ble_mesh/api/models/esp_ble_mesh_generic_model_api.c create mode 100644 components/bt/ble_mesh/api/models/esp_ble_mesh_health_model_api.c create mode 100644 components/bt/ble_mesh/api/models/esp_ble_mesh_lighting_model_api.c create mode 100644 components/bt/ble_mesh/api/models/esp_ble_mesh_sensor_model_api.c create mode 100644 components/bt/ble_mesh/api/models/esp_ble_mesh_time_scene_model_api.c create mode 100644 components/bt/ble_mesh/api/models/include/esp_ble_mesh_config_model_api.h create mode 100644 components/bt/ble_mesh/api/models/include/esp_ble_mesh_generic_model_api.h create mode 100644 components/bt/ble_mesh/api/models/include/esp_ble_mesh_health_model_api.h create mode 100644 components/bt/ble_mesh/api/models/include/esp_ble_mesh_lighting_model_api.h create mode 100644 components/bt/ble_mesh/api/models/include/esp_ble_mesh_sensor_model_api.h create mode 100644 components/bt/ble_mesh/api/models/include/esp_ble_mesh_time_scene_model_api.h create mode 100644 components/bt/ble_mesh/btc/btc_ble_mesh_config_model.c create mode 100644 components/bt/ble_mesh/btc/btc_ble_mesh_generic_model.c create mode 100644 components/bt/ble_mesh/btc/btc_ble_mesh_health_model.c create mode 100644 components/bt/ble_mesh/btc/btc_ble_mesh_lighting_model.c create mode 100644 components/bt/ble_mesh/btc/btc_ble_mesh_prov.c create mode 100644 components/bt/ble_mesh/btc/btc_ble_mesh_sensor_model.c create mode 100644 components/bt/ble_mesh/btc/btc_ble_mesh_time_scene_model.c create mode 100644 components/bt/ble_mesh/btc/include/btc_ble_mesh_config_model.h create mode 100644 components/bt/ble_mesh/btc/include/btc_ble_mesh_generic_model.h create mode 100644 components/bt/ble_mesh/btc/include/btc_ble_mesh_health_model.h create mode 100644 components/bt/ble_mesh/btc/include/btc_ble_mesh_lighting_model.h create mode 100644 components/bt/ble_mesh/btc/include/btc_ble_mesh_prov.h create mode 100644 components/bt/ble_mesh/btc/include/btc_ble_mesh_sensor_model.h create mode 100644 components/bt/ble_mesh/btc/include/btc_ble_mesh_time_scene_model.h create mode 100644 components/bt/ble_mesh/mesh_core/access.c create mode 100644 components/bt/ble_mesh/mesh_core/access.h create mode 100644 components/bt/ble_mesh/mesh_core/adv.c create mode 100644 components/bt/ble_mesh/mesh_core/adv.h create mode 100644 components/bt/ble_mesh/mesh_core/beacon.c create mode 100644 components/bt/ble_mesh/mesh_core/beacon.h create mode 100644 components/bt/ble_mesh/mesh_core/cfg_cli.c create mode 100644 components/bt/ble_mesh/mesh_core/cfg_srv.c create mode 100644 components/bt/ble_mesh/mesh_core/crypto.c create mode 100644 components/bt/ble_mesh/mesh_core/crypto.h create mode 100644 components/bt/ble_mesh/mesh_core/foundation.h create mode 100644 components/bt/ble_mesh/mesh_core/friend.c create mode 100644 components/bt/ble_mesh/mesh_core/friend.h create mode 100644 components/bt/ble_mesh/mesh_core/health_cli.c create mode 100644 components/bt/ble_mesh/mesh_core/health_srv.c create mode 100644 components/bt/ble_mesh/mesh_core/include/cfg_cli.h create mode 100644 components/bt/ble_mesh/mesh_core/include/cfg_srv.h create mode 100644 components/bt/ble_mesh/mesh_core/include/health_cli.h create mode 100644 components/bt/ble_mesh/mesh_core/include/health_srv.h create mode 100644 components/bt/ble_mesh/mesh_core/include/mesh_access.h create mode 100644 components/bt/ble_mesh/mesh_core/include/mesh_aes_encrypt.h create mode 100644 components/bt/ble_mesh/mesh_core/include/mesh_atomic.h create mode 100644 components/bt/ble_mesh/mesh_core/include/mesh_bearer_adapt.h create mode 100644 components/bt/ble_mesh/mesh_core/include/mesh_buf.h create mode 100644 components/bt/ble_mesh/mesh_core/include/mesh_dlist.h create mode 100644 components/bt/ble_mesh/mesh_core/include/mesh_hci.h create mode 100644 components/bt/ble_mesh/mesh_core/include/mesh_kernel.h create mode 100644 components/bt/ble_mesh/mesh_core/include/mesh_main.h create mode 100644 components/bt/ble_mesh/mesh_core/include/mesh_proxy.h create mode 100644 components/bt/ble_mesh/mesh_core/include/mesh_slist.h create mode 100644 components/bt/ble_mesh/mesh_core/include/mesh_trace.h create mode 100644 components/bt/ble_mesh/mesh_core/include/mesh_types.h create mode 100644 components/bt/ble_mesh/mesh_core/include/mesh_util.h create mode 100644 components/bt/ble_mesh/mesh_core/include/mesh_uuid.h create mode 100644 components/bt/ble_mesh/mesh_core/lpn.c create mode 100644 components/bt/ble_mesh/mesh_core/lpn.h create mode 100644 components/bt/ble_mesh/mesh_core/mesh.h create mode 100644 components/bt/ble_mesh/mesh_core/mesh_aes_encrypt.c create mode 100644 components/bt/ble_mesh/mesh_core/mesh_atomic.c create mode 100644 components/bt/ble_mesh/mesh_core/mesh_bearer_adapt.c create mode 100644 components/bt/ble_mesh/mesh_core/mesh_buf.c create mode 100644 components/bt/ble_mesh/mesh_core/mesh_hci.c create mode 100644 components/bt/ble_mesh/mesh_core/mesh_kernel.c create mode 100644 components/bt/ble_mesh/mesh_core/mesh_main.c create mode 100644 components/bt/ble_mesh/mesh_core/mesh_util.c create mode 100644 components/bt/ble_mesh/mesh_core/net.c create mode 100644 components/bt/ble_mesh/mesh_core/net.h create mode 100644 components/bt/ble_mesh/mesh_core/prov.c create mode 100644 components/bt/ble_mesh/mesh_core/prov.h create mode 100644 components/bt/ble_mesh/mesh_core/provisioner_beacon.c create mode 100644 components/bt/ble_mesh/mesh_core/provisioner_beacon.h create mode 100644 components/bt/ble_mesh/mesh_core/provisioner_main.c create mode 100644 components/bt/ble_mesh/mesh_core/provisioner_main.h create mode 100644 components/bt/ble_mesh/mesh_core/provisioner_prov.c create mode 100644 components/bt/ble_mesh/mesh_core/provisioner_prov.h create mode 100644 components/bt/ble_mesh/mesh_core/provisioner_proxy.c create mode 100644 components/bt/ble_mesh/mesh_core/provisioner_proxy.h create mode 100644 components/bt/ble_mesh/mesh_core/proxy.c create mode 100644 components/bt/ble_mesh/mesh_core/proxy.h create mode 100644 components/bt/ble_mesh/mesh_core/settings.c create mode 100644 components/bt/ble_mesh/mesh_core/settings.h create mode 100644 components/bt/ble_mesh/mesh_core/settings/settings_nvs.c create mode 100644 components/bt/ble_mesh/mesh_core/settings/settings_nvs.h create mode 100644 components/bt/ble_mesh/mesh_core/test.c create mode 100644 components/bt/ble_mesh/mesh_core/test.h create mode 100644 components/bt/ble_mesh/mesh_core/transport.c create mode 100644 components/bt/ble_mesh/mesh_core/transport.h create mode 100644 components/bt/ble_mesh/mesh_docs/BLE-Mesh_FAQs_EN.md create mode 100644 components/bt/ble_mesh/mesh_docs/BLE-Mesh_Feature_List_EN.md create mode 100644 components/bt/ble_mesh/mesh_docs/BLE-Mesh_Getting_Started_EN.md create mode 100644 components/bt/ble_mesh/mesh_docs/BLE-Mesh_Known_Issues_EN.md create mode 100644 components/bt/ble_mesh/mesh_docs/README.md create mode 100644 components/bt/ble_mesh/mesh_models/generic_client.c create mode 100644 components/bt/ble_mesh/mesh_models/include/generic_client.h create mode 100644 components/bt/ble_mesh/mesh_models/include/lighting_client.h create mode 100644 components/bt/ble_mesh/mesh_models/include/mesh_common.h create mode 100644 components/bt/ble_mesh/mesh_models/include/model_common.h create mode 100644 components/bt/ble_mesh/mesh_models/include/model_opcode.h create mode 100644 components/bt/ble_mesh/mesh_models/include/sensor_client.h create mode 100644 components/bt/ble_mesh/mesh_models/include/time_scene_client.h create mode 100644 components/bt/ble_mesh/mesh_models/lighting_client.c create mode 100644 components/bt/ble_mesh/mesh_models/mesh_common.c create mode 100644 components/bt/ble_mesh/mesh_models/model_common.c create mode 100644 components/bt/ble_mesh/mesh_models/sensor_client.c create mode 100644 components/bt/ble_mesh/mesh_models/time_scene_client.c create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_client_model/CMakeLists.txt create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_client_model/Makefile create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_client_model/README.md create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_client_model/main/CMakeLists.txt create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_client_model/main/Kconfig.projbuild create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_client_model/main/ble_mesh_demo_main.c create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_client_model/main/board.c create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_client_model/main/board.h create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_client_model/main/component.mk create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_client_model/sdkconfig.defaults create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_client_model/tutorial/ble_mesh_client_model.md create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_client_model/tutorial/images/app.png create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_client_model/tutorial/images/message.png create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_client_model/tutorial/images/picture5.png create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/CMakeLists.txt create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/Makefile create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/README.md create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/CMakeLists.txt create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/ble_mesh_adapter.c create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/ble_mesh_adapter.h create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/ble_mesh_cfg_srv_model.c create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/ble_mesh_cfg_srv_model.h create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/ble_mesh_console_decl.h create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/ble_mesh_console_lib.c create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/ble_mesh_console_lib.h create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/ble_mesh_console_main.c create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/ble_mesh_console_system.c create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/ble_mesh_register_node_cmd.c create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/ble_mesh_register_server_cmd.c create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/component.mk create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/register_bluetooth.c create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/sdkconfig.defaults create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/CMakeLists.txt create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/Makefile create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/README.md create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/CMakeLists.txt create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_adapter.c create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_adapter.h create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_cfg_srv_model.c create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_cfg_srv_model.h create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_console_decl.h create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_console_lib.c create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_console_lib.h create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_console_main.c create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_console_system.c create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_reg_cfg_client_cmd.c create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_reg_gen_onoff_client_cmd.c create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_reg_test_perf_client_cmd.c create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_register_node_cmd.c create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_register_provisioner_cmd.c create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/component.mk create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/register_bluetooth.c create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/sdkconfig.defaults create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_client/CMakeLists.txt create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_client/Makefile create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_client/README.md create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_client/main/CMakeLists.txt create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_client/main/ble_mesh_demo_main.c create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_client/main/component.mk create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_client/sdkconfig.defaults create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_client/tutorial/ble_mesh_fast_provision_client.md create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/CMakeLists.txt create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/Makefile create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/README.md create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/main/CMakeLists.txt create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/main/Kconfig.projbuild create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/main/ble_mesh_demo_main.c create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/main/board.c create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/main/board.h create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/main/component.mk create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/sdkconfig.defaults create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/tutorial/EspBleMesh.md create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/tutorial/ble_mesh_fast_provision_server.md create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/tutorial/images/app_ble.png create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/tutorial/images/device.png create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/tutorial/images/picture1.png create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/tutorial/images/picture2.png create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/tutorial/images/time.png create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_node/CMakeLists.txt create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_node/Makefile create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_node/README.md create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_node/main/CMakeLists.txt create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_node/main/Kconfig.projbuild create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_node/main/ble_mesh_demo_main.c create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_node/main/board.c create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_node/main/board.h create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_node/main/component.mk create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_node/sdkconfig.defaults create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_node/tutorial/Ble_Mesh_Node_Example_Walkthrough.md create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_provisioner/CMakeLists.txt create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_provisioner/Makefile create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_provisioner/README.md create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_provisioner/main/CMakeLists.txt create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_provisioner/main/ble_mesh_demo_main.c create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_provisioner/main/component.mk create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_provisioner/sdkconfig.defaults create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_provisioner/tutorial/Ble_Mesh_Provisioner_Example_Walkthrough.md create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_vendor_models/fast_prov_vendor_model/CMakeLists.txt create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_vendor_models/fast_prov_vendor_model/Makefile create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_vendor_models/fast_prov_vendor_model/components/CMakeLists.txt create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_vendor_models/fast_prov_vendor_model/components/component.mk create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_vendor_models/fast_prov_vendor_model/components/esp_fast_prov_client_model.c create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_vendor_models/fast_prov_vendor_model/components/esp_fast_prov_client_model.h create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_vendor_models/fast_prov_vendor_model/components/esp_fast_prov_common.h create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_vendor_models/fast_prov_vendor_model/components/esp_fast_prov_operation.c create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_vendor_models/fast_prov_vendor_model/components/esp_fast_prov_operation.h create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_vendor_models/fast_prov_vendor_model/components/esp_fast_prov_server_model.c create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_vendor_models/fast_prov_vendor_model/components/esp_fast_prov_server_model.h create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_vendor_models/fast_prov_vendor_model/main/CMakeLists.txt create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_vendor_models/fast_prov_vendor_model/main/component.mk create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_vendor_models/fast_prov_vendor_model/main/main.c create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_vendor_models/fast_prov_vendor_model/sdkconfig.defaults create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_wifi_coexist/CMakeLists.txt create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_wifi_coexist/Makefile create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_wifi_coexist/README.md create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_wifi_coexist/components/iperf/CMakeLists.txt create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_wifi_coexist/components/iperf/cmd_decl.h create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_wifi_coexist/components/iperf/cmd_wifi.c create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_wifi_coexist/components/iperf/component.mk create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_wifi_coexist/components/iperf/iperf.c create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_wifi_coexist/components/iperf/iperf.h create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_wifi_coexist/main/CMakeLists.txt create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_wifi_coexist/main/Kconfig.projbuild create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_wifi_coexist/main/ble_mesh_demo_main.c create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_wifi_coexist/main/board.c create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_wifi_coexist/main/board.h create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_wifi_coexist/main/component.mk create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_wifi_coexist/partitions.csv create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_wifi_coexist/sdkconfig.defaults create mode 100644 examples/bluetooth/ble_mesh/ble_mesh_wifi_coexist/tutorial/ble_mesh_wifi_coexist.md diff --git a/components/bt/CMakeLists.txt b/components/bt/CMakeLists.txt index 73f6879039..58ccb27a82 100644 --- a/components/bt/CMakeLists.txt +++ b/components/bt/CMakeLists.txt @@ -287,6 +287,75 @@ if(CONFIG_BT_ENABLED) endif() endif() +if (CONFIG_BLE_MESH) + list(APPEND COMPONENT_ADD_INCLUDEDIRS + "bluedroid/osi/include" + "ble_mesh/mesh_core" + "ble_mesh/mesh_core/include" + "ble_mesh/mesh_core/settings" + "ble_mesh/btc/include" + "ble_mesh/mesh_models/include" + "ble_mesh/api/core/include" + "ble_mesh/api/models/include" + "ble_mesh/api") + + list(APPEND COMPONENT_SRCS + "ble_mesh/api/core/esp_ble_mesh_common_api.c" + "ble_mesh/api/core/esp_ble_mesh_local_data_operation_api.c" + "ble_mesh/api/core/esp_ble_mesh_low_power_api.c" + "ble_mesh/api/core/esp_ble_mesh_networking_api.c" + "ble_mesh/api/core/esp_ble_mesh_provisioning_api.c" + "ble_mesh/api/core/esp_ble_mesh_proxy_api.c" + "ble_mesh/api/models/esp_ble_mesh_config_model_api.c" + "ble_mesh/api/models/esp_ble_mesh_generic_model_api.c" + "ble_mesh/api/models/esp_ble_mesh_health_model_api.c" + "ble_mesh/api/models/esp_ble_mesh_lighting_model_api.c" + "ble_mesh/api/models/esp_ble_mesh_sensor_model_api.c" + "ble_mesh/api/models/esp_ble_mesh_time_scene_model_api.c" + "ble_mesh/btc/btc_ble_mesh_config_model.c" + "ble_mesh/btc/btc_ble_mesh_generic_model.c" + "ble_mesh/btc/btc_ble_mesh_health_model.c" + "ble_mesh/btc/btc_ble_mesh_lighting_model.c" + "ble_mesh/btc/btc_ble_mesh_prov.c" + "ble_mesh/btc/btc_ble_mesh_sensor_model.c" + "ble_mesh/btc/btc_ble_mesh_time_scene_model.c" + "ble_mesh/mesh_core/settings/settings_nvs.c" + "ble_mesh/mesh_core/access.c" + "ble_mesh/mesh_core/adv.c" + "ble_mesh/mesh_core/beacon.c" + "ble_mesh/mesh_core/cfg_cli.c" + "ble_mesh/mesh_core/cfg_srv.c" + "ble_mesh/mesh_core/crypto.c" + "ble_mesh/mesh_core/friend.c" + "ble_mesh/mesh_core/health_cli.c" + "ble_mesh/mesh_core/health_srv.c" + "ble_mesh/mesh_core/lpn.c" + "ble_mesh/mesh_core/mesh_aes_encrypt.c" + "ble_mesh/mesh_core/mesh_atomic.c" + "ble_mesh/mesh_core/mesh_bearer_adapt.c" + "ble_mesh/mesh_core/mesh_buf.c" + "ble_mesh/mesh_core/mesh_hci.c" + "ble_mesh/mesh_core/mesh_kernel.c" + "ble_mesh/mesh_core/mesh_main.c" + "ble_mesh/mesh_core/mesh_util.c" + "ble_mesh/mesh_core/net.c" + "ble_mesh/mesh_core/prov.c" + "ble_mesh/mesh_core/provisioner_beacon.c" + "ble_mesh/mesh_core/provisioner_main.c" + "ble_mesh/mesh_core/provisioner_prov.c" + "ble_mesh/mesh_core/provisioner_proxy.c" + "ble_mesh/mesh_core/proxy.c" + "ble_mesh/mesh_core/settings.c" + "ble_mesh/mesh_core/test.c" + "ble_mesh/mesh_core/transport.c" + "ble_mesh/mesh_models/generic_client.c" + "ble_mesh/mesh_models/lighting_client.c" + "ble_mesh/mesh_models/mesh_common.c" + "ble_mesh/mesh_models/model_common.c" + "ble_mesh/mesh_models/sensor_client.c" + "ble_mesh/mesh_models/time_scene_client.c") +endif() + # requirements can't depend on config set(COMPONENT_PRIV_REQUIRES nvs_flash soc) diff --git a/components/bt/Kconfig b/components/bt/Kconfig index d292e66c20..daf83b1be7 100644 --- a/components/bt/Kconfig +++ b/components/bt/Kconfig @@ -1352,3 +1352,814 @@ menu Bluetooth default 0 endmenu + +menuconfig BLE_MESH + bool "BLE Mesh Support" + help + This option enables BLE Mesh support. The specific features that are + available may depend on other features that have been enabled in the + stack, such as Bluetooth Support, Bluedroid Support & GATT support. + +if BLE_MESH + + config BLE_MESH_HCI_5_0 + bool "Support sending 20ms non-connectable adv packets" + default y + help + It is a temporary solution and needs further modifications. + + config BLE_MESH_USE_DUPLICATE_SCAN + bool "Support Duplicate Scan in BLE Mesh" + select BLE_SCAN_DUPLICATE + select BLE_MESH_SCAN_DUPLICATE_EN + default y + help + Enable this option to allow using specific duplicate scan filter + in BLE Mesh, and Scan Duplicate Type must be set to 0x02. + + config BLE_MESH_FAST_PROV + bool "Enable BLE Mesh Fast Provisioning" + select BLE_MESH_NODE + select BLE_MESH_PROVISIONER + select BLE_MESH_PB_ADV + default n + help + Enable this option to allow BLE Mesh fast provisioning solution to be used. + + config BLE_MESH_NODE + bool "Support for BLE Mesh Node" + help + Enable the device to be provisioned into a node. + + config BLE_MESH_PROVISIONER + bool "Support for BLE Mesh Provisioner" + help + Enable the device to be a provisioner. + + if BLE_MESH_PROVISIONER + + config BLE_MESH_WAIT_FOR_PROV_MAX_DEV_NUM + int "Maximum number of unprovisioned devices that can be added to device queue" + default 20 + range 1 100 + help + This option specifies how may unprovisioned devices can be added to device + queue for provisioning. + + config BLE_MESH_MAX_STORED_NODES + int "Maximum number of nodes whose information can be stored" + default 20 + range 1 1000 + help + This option specifies the maximum number of nodes whose information can be + stored by a provisioner in its upper layer. + + config BLE_MESH_MAX_PROV_NODES + int "Maximum number of devices that can be provisioned by provisioner" + default 20 + range 1 100 + help + This option specifies how many devices can be provisioned by provisioner. + + if BLE_MESH_PB_ADV + config BLE_MESH_PBA_SAME_TIME + int "Maximum number of PB-ADV running at the same time by provisioner" + default 2 + range 1 10 + help + This option specifies how many devices can be provisioned at the same + time using PB-ADV. + endif # BLE_MESH_PB_ADV + + if BLE_MESH_PB_GATT + config BLE_MESH_PBG_SAME_TIME + int "Maximum number of PB-GATT running at the same time by provisioner" + default 1 + range 1 5 + help + This option specifies how many devices can be provisioned at the same + time using PB-GATT. + endif # BLE_MESH_PB_GATT + + config BLE_MESH_PROVISIONER_SUBNET_COUNT + int "Maximum number of mesh subnets that can be created by provisioner" + default 3 + range 1 4096 + help + This option specifies how many subnets per network a provisioner can create. + + config BLE_MESH_PROVISIONER_APP_KEY_COUNT + int "Maximum number of application keys that can be owned by provisioner" + default 9 + range 1 4096 + help + This option specifies how many application keys the provisioner can have. + + endif # BLE_MESH_PROVISIONER + + # Virtual option enabled whenever Generic Provisioning layer is needed + config BLE_MESH_PROV + bool "BLE Mesh Provisioning support" + default y + help + Enable this option to support BLE Mesh Provisioning functionality. For + BLE Mesh, this option should be always enabled. + + config BLE_MESH_PB_ADV + bool "Provisioning support using the advertising bearer (PB-ADV)" + select BLE_MESH_PROV + default y + help + Enable this option to allow the device to be provisioned over the + advertising bearer. + + config BLE_MESH_PB_GATT + bool "Provisioning support using GATT (PB-GATT)" + select BLE_MESH_PROXY + select BLE_MESH_PROV + help + Enable this option to allow the device to be provisioned over GATT. + + # Virtual option enabled whenever any Proxy protocol is needed + config BLE_MESH_PROXY + bool "BLE Mesh Proxy protocol support" + default y + help + Enable this option to support BLE Mesh Proxy protocol used by PB-GATT + and other proxy pdu transmission. + + config BLE_MESH_GATT_PROXY + bool "BLE Mesh GATT Proxy Service" + select BLE_MESH_PROXY + help + This option enables support for Mesh GATT Proxy Service, i.e. the + ability to act as a proxy between a Mesh GATT Client and a Mesh network. + + config BLE_MESH_NODE_ID_TIMEOUT + int "Node Identity advertising timeout" + depends on BLE_MESH_GATT_PROXY + range 1 60 + default 60 + help + This option determines for how long the local node advertises using + Node Identity. The given value is in seconds. The specification limits + this to 60 seconds and lists it as the recommended value as well. + So leaving the default value is the safest option. + + if BLE_MESH_PROXY + + config BLE_MESH_PROXY_FILTER_SIZE + int "Maximum number of filter entries per Proxy Client" + default 1 + default 3 if BLE_MESH_GATT_PROXY + range 1 32767 + help + This option specifies how many Proxy Filter entries the local node supports. + + endif # BLE_MESH_PROXY + + config BLE_MESH_NET_BUF_POOL_USAGE + bool "BLE Mesh net buffer pool usage tracking" + default y + help + Enable BLE Mesh net buffer pool tracking. + + config BLE_MESH_SETTINGS + bool "Store BLE Mesh Node configuration persistently" + default n + help + When selected, the BLE Mesh stack will take care of storing/restoring the + BLE Mesh configuration persistently in flash. Currently this only supports + storing BLE Mesh node configuration. + + if BLE_MESH_SETTINGS + config BLE_MESH_STORE_TIMEOUT + int "Delay (in seconds) before storing anything persistently" + range 0 1000000 + default 0 + help + This value defines in seconds how soon any pending changes are actually + written into persistent storage (flash) after a change occurs. + + config BLE_MESH_SEQ_STORE_RATE + int "How often the sequence number gets updated in storage" + range 0 1000000 + default 128 + help + This value defines how often the local sequence number gets updated in + persistent storage (i.e. flash). e.g. a value of 100 means that the + sequence number will be stored to flash on every 100th increment. + If the node sends messages very frequently a higher value makes more + sense, whereas if the node sends infrequently a value as low as 0 + (update storage for every increment) can make sense. When the stack + gets initialized it will add sequence number to the last stored one, + so that it starts off with a value that's guaranteed to be larger than + the last one used before power off. + + config BLE_MESH_RPL_STORE_TIMEOUT + int "Minimum frequency that the RPL gets updated in storage" + range 0 1000000 + default 5 + help + This value defines in seconds how soon the RPL(Replay Protection List) + gets written to persistent storage after a change occurs. If the node + receives messages frequently, then a large value is recommended. If the + node receives messages rarely, then the value can be as low as 0 (which + means the PRL is written into the storage immediately). + Note that if the node operates in a security-sensitive case, and there is + a risk of sudden power-off, then a value of 0 is strongly recommended. + Otherwise, a power loss before RPL being written into the storage may + introduce message replay attacks and system security will be in a + vulnerable state. + endif # if BLE_MESH_SETTINGS + + config BLE_MESH_SUBNET_COUNT + int "Maximum number of mesh subnets per network" + default 3 + range 1 4096 + help + This option specifies how many subnets a Mesh network can have at the same time. + + config BLE_MESH_APP_KEY_COUNT + int "Maximum number of application keys per network" + default 3 + range 1 4096 + help + This option specifies how many application keys the device can store per network. + + config BLE_MESH_MODEL_KEY_COUNT + int "Maximum number of application keys per model" + default 3 + range 1 4096 + help + This option specifies the maximum number of application keys to which each model + can be bound. + + config BLE_MESH_MODEL_GROUP_COUNT + int "Maximum number of group address subscriptions per model" + default 3 + range 1 4096 + help + This option specifies the maximum number of addresses to which each model can + be subscribed. + + config BLE_MESH_LABEL_COUNT + int "Maximum number of Label UUIDs used for Virtual Addresses" + default 3 + range 0 4096 + help + This option specifies how many Label UUIDs can be stored. + + config BLE_MESH_CRPL + int "Maximum capacity of the replay protection list" + default 10 + range 2 65535 + help + This option specifies the maximum capacity of the replay protection list. + It is similar to Network message cache size, but has a different purpose. + + config BLE_MESH_MSG_CACHE_SIZE + int "Network message cache size" + default 10 + range 2 65535 + help + Number of messages that are cached for the network. This helps prevent + unnecessary decryption operations and unnecessary relays. This option + is similar to Replay protection list, but has a different purpose. + + config BLE_MESH_ADV_BUF_COUNT + int "Number of advertising buffers" + default 60 + range 6 256 + help + Number of advertising buffers available. The transport layer reserves + ADV_BUF_COUNT - 3 buffers for outgoing segments. The maximum outgoing + SDU size is 12 times this value (out of which 4 or 8 bytes are used + for the Transport Layer MIC). For example, 5 segments means the maximum + SDU size is 60 bytes, which leaves 56 bytes for application layer data + using a 4-byte MIC, or 52 bytes using an 8-byte MIC. + + config BLE_MESH_IVU_DIVIDER + int "Divider for IV Update state refresh timer" + default 4 + range 2 96 + help + When the IV Update state enters Normal operation or IV Update + in Progress, we need to keep track of how many hours has passed + in the state, since the specification requires us to remain in + the state at least for 96 hours (Update in Progress has an + additional upper limit of 144 hours). + + In order to fulfill the above requirement, even if the node might + be powered off once in a while, we need to store persistently + how many hours the node has been in the state. This doesn't + necessarily need to happen every hour (thanks to the flexible + duration range). The exact cadence will depend a lot on the + ways that the node will be used and what kind of power source it + has. + + Since there is no single optimal answer, this configuration + option allows specifying a divider, i.e. how many intervals + the 96 hour minimum gets split into. After each interval the + duration that the node has been in the current state gets + stored to flash. E.g. the default value of 4 means that the + state is saved every 24 hours (96 / 4). + + config BLE_MESH_TX_SEG_MSG_COUNT + int "Maximum number of simultaneous outgoing segmented messages" + default 1 + range 1 BLE_MESH_ADV_BUF_COUNT + help + Maximum number of simultaneous outgoing multi-segment and/or reliable messages. + + config BLE_MESH_RX_SEG_MSG_COUNT + int "Maximum number of simultaneous incoming segmented messages" + default 1 + range 1 255 + help + Maximum number of simultaneous incoming multi-segment and/or reliable messages. + + config BLE_MESH_RX_SDU_MAX + int "Maximum incoming Upper Transport Access PDU length" + default 384 + range 36 384 + help + Maximum incoming Upper Transport Access PDU length. Leave this to the default + value, unless you really need to optimize memory usage. + + config BLE_MESH_TX_SEG_MAX + int "Maximum number of segments in outgoing messages" + default 20 + range 2 32 + help + Maximum number of segments supported for outgoing messages. + This value should typically be fine-tuned based on what + models the local node supports, i.e. what's the largest + message payload that the node needs to be able to send. + This value affects memory and call stack consumption, which + is why the default is lower than the maximum that the + specification would allow (32 segments). + + The maximum outgoing SDU size is 12 times this number (out of + which 4 or 8 bytes is used for the Transport Layer MIC). For + example, 5 segments means the maximum SDU size is 60 bytes, + which leaves 56 bytes for application layer data using a + 4-byte MIC and 52 bytes using an 8-byte MIC. + + Be sure to specify a sufficient number of advertising buffers + when setting this option to a higher value. There must be at + least three more advertising buffers (BLE_MESH_ADV_BUF_COUNT) + as there are outgoing segments. + + config BLE_MESH_RELAY + bool "Relay support" + help + Support for acting as a Mesh Relay Node. + + config BLE_MESH_LOW_POWER + bool "Support for Low Power features" + help + Enable this option to operate as a Low Power Node. + + if BLE_MESH_LOW_POWER + + config BLE_MESH_LPN_ESTABLISHMENT + bool "Perform Friendship establishment using low power" + default y + help + Perform the Friendship establishment using low power with the help of a + reduced scan duty cycle. The downside of this is that the node may miss + out on messages intended for it until it has successfully set up Friendship + with a Friend node. + + config BLE_MESH_LPN_AUTO + bool "Automatically start looking for Friend nodes once provisioned" + default y + help + Once provisioned, automatically enable LPN functionality and start looking + for Friend nodes. If this option is disabled LPN mode needs to be manually + enabled by calling bt_mesh_lpn_set(true). + + config BLE_MESH_LPN_AUTO_TIMEOUT + int "Time from last received message before going to LPN mode" + default 15 + range 0 3600 + depends on BLE_MESH_LPN_AUTO + help + Time in seconds from the last received message, that the node waits out + before starting to look for Friend nodes. + + config BLE_MESH_LPN_RETRY_TIMEOUT + int "Retry timeout for Friend requests" + default 8 + range 1 3600 + help + Time in seconds between Friend Requests, if a previous Friend Request did + not yield any acceptable Friend Offers. + + config BLE_MESH_LPN_RSSI_FACTOR + int "RSSIFactor, used in Friend Offer Delay calculation" + range 0 3 + default 0 + help + The contribution of the RSSI, measured by the Friend node, used in Friend + Offer Delay calculations. 0 = 1, 1 = 1.5, 2 = 2, 3 = 2.5. + + config BLE_MESH_LPN_RECV_WIN_FACTOR + int "ReceiveWindowFactor, used in Friend Offer Delay calculation" + range 0 3 + default 0 + help + The contribution of the supported Receive Window used in Friend Offer + Delay calculations. 0 = 1, 1 = 1.5, 2 = 2, 3 = 2.5. + + config BLE_MESH_LPN_MIN_QUEUE_SIZE + int "Minimum size of the acceptable friend queue (MinQueueSizeLog)" + range 1 7 + default 1 + help + The MinQueueSizeLog field is defined as log_2(N), where N is the minimum + number of maximum size Lower Transport PDUs that the Friend node can store + in its Friend Queue. As an example, MinQueueSizeLog value 1 gives N = 2, + and value 7 gives N = 128. + + config BLE_MESH_LPN_RECV_DELAY + int "Receive delay requested by the local node" + range 10 255 + default 100 + help + The ReceiveDelay is the time between the Low Power node sending a + request and listening for a response. This delay allows the Friend + node time to prepare the response. The value is in units of milliseconds. + + config BLE_MESH_LPN_POLL_TIMEOUT + int "The value of the PollTimeout timer" + range 10 244735 + default 300 + help + PollTimeout timer is used to measure time between two consecutive + requests sent by a Low Power node. If no requests are received + the Friend node before the PollTimeout timer expires, then the + friendship is considered terminated. The value is in units of 100 + milliseconds, so e.g. a value of 300 means 30 seconds. + + config BLE_MESH_LPN_INIT_POLL_TIMEOUT + int "The starting value of the PollTimeout timer" + range 10 BLE_MESH_LPN_POLL_TIMEOUT + default BLE_MESH_LPN_POLL_TIMEOUT + help + The initial value of the PollTimeout timer when Friendship is to be + established for the first time. After this, the timeout gradually + grows toward the actual PollTimeout, doubling in value for each iteration. + The value is in units of 100 milliseconds, so e.g. a value of 300 means + 30 seconds. + + config BLE_MESH_LPN_SCAN_LATENCY + int "Latency for enabling scanning" + range 0 50 + default 10 + help + Latency (in milliseconds) is the time it takes to enable scanning. In + practice, it means how much time in advance of the Receive Window, the + request to enable scanning is made. + + config BLE_MESH_LPN_GROUPS + int "Number of groups the LPN can subscribe to" + range 0 16384 + default 8 + help + Maximum number of groups to which the LPN can subscribe. + endif # BLE_MESH_LOW_POWER + + config BLE_MESH_FRIEND + bool "Support for acting as a Friend Node" + help + Enable this option to be able to act as a Friend Node. + + if BLE_MESH_FRIEND + + config BLE_MESH_FRIEND_RECV_WIN + int "Friend Receive Window" + range 1 255 + default 255 + help + Receive Window in milliseconds supported by the Friend node. + + config BLE_MESH_FRIEND_QUEUE_SIZE + int "Minimum number of buffers supported per Friend Queue" + range 2 65536 + default 16 + help + Minimum number of buffers available to be stored for each local Friend Queue. + + config BLE_MESH_FRIEND_SUB_LIST_SIZE + int "Friend Subscription List Size" + range 0 1023 + default 3 + help + Size of the Subscription List that can be supported by a Friend node for a + Low Power node. + + config BLE_MESH_FRIEND_LPN_COUNT + int "Number of supported LPN nodes" + range 1 1000 + default 2 + help + Number of Low Power Nodes with which a Friend can have Friendship simultaneously. + + config BLE_MESH_FRIEND_SEG_RX + int "Number of incomplete segment lists per LPN" + range 1 1000 + default 1 + help + Number of incomplete segment lists tracked for each Friends' LPN. + In other words, this determines from how many elements can segmented + messages destined for the Friend queue be received simultaneously. + + endif # BLE_MESH_FRIEND + + config BLE_MESH_NO_LOG + bool "Disable BLE Mesh debug logs (minimize bin size)" + depends on BLE_MESH + default n + help + Select this to save the BLE Mesh related rodata code size. + + menu "BLE Mesh STACK DEBUG LOG LEVEL" + depends on BLE_MESH && !BLE_MESH_NO_LOG + + choice BLE_MESH_STACK_TRACE_LEVEL + prompt "BLE_MESH_STACK" + default BLE_MESH_TRACE_LEVEL_WARNING + depends on BLE_MESH && !BLE_MESH_NO_LOG + help + Define BLE Mesh trace level for BLE Mesh stack. + + config BLE_MESH_TRACE_LEVEL_NONE + bool "NONE" + config BLE_MESH_TRACE_LEVEL_ERROR + bool "ERROR" + config BLE_MESH_TRACE_LEVEL_WARNING + bool "WARNING" + config BLE_MESH_TRACE_LEVEL_INFO + bool "INFO" + config BLE_MESH_TRACE_LEVEL_DEBUG + bool "DEBUG" + config BLE_MESH_TRACE_LEVEL_VERBOSE + bool "VERBOSE" + endchoice + + config BLE_MESH_STACK_TRACE_LEVEL + int + depends on BLE_MESH + default 0 if BLE_MESH_TRACE_LEVEL_NONE + default 1 if BLE_MESH_TRACE_LEVEL_ERROR + default 2 if BLE_MESH_TRACE_LEVEL_WARNING + default 3 if BLE_MESH_TRACE_LEVEL_INFO + default 4 if BLE_MESH_TRACE_LEVEL_DEBUG + default 5 if BLE_MESH_TRACE_LEVEL_VERBOSE + default 2 + + endmenu #BLE Mesh DEBUG LOG LEVEL + + menu "BLE Mesh NET BUF DEBUG LOG LEVEL" + depends on BLE_MESH && !BLE_MESH_NO_LOG + + choice BLE_MESH_NET_BUF_TRACE_LEVEL + prompt "BLE_MESH_NET_BUF" + default BLE_MESH_NET_BUF_TRACE_LEVEL_WARNING + depends on BLE_MESH && !BLE_MESH_NO_LOG + help + Define BLE Mesh trace level for BLE Mesh net buffer. + + config BLE_MESH_NET_BUF_TRACE_LEVEL_NONE + bool "NONE" + config BLE_MESH_NET_BUF_TRACE_LEVEL_ERROR + bool "ERROR" + config BLE_MESH_NET_BUF_TRACE_LEVEL_WARNING + bool "WARNING" + config BLE_MESH_NET_BUF_TRACE_LEVEL_INFO + bool "INFO" + config BLE_MESH_NET_BUF_TRACE_LEVEL_DEBUG + bool "DEBUG" + config BLE_MESH_NET_BUF_TRACE_LEVEL_VERBOSE + bool "VERBOSE" + endchoice + + config BLE_MESH_NET_BUF_TRACE_LEVEL + int + depends on BLE_MESH + default 0 if BLE_MESH_NET_BUF_TRACE_LEVEL_NONE + default 1 if BLE_MESH_NET_BUF_TRACE_LEVEL_ERROR + default 2 if BLE_MESH_NET_BUF_TRACE_LEVEL_WARNING + default 3 if BLE_MESH_NET_BUF_TRACE_LEVEL_INFO + default 4 if BLE_MESH_NET_BUF_TRACE_LEVEL_DEBUG + default 5 if BLE_MESH_NET_BUF_TRACE_LEVEL_VERBOSE + default 2 + + endmenu #BLE Mesh NET BUF DEBUG LOG LEVEL + + config BLE_MESH_IRQ_LOCK + bool "Used the IRQ lock instead of task lock" + help + To improve the real-time requirements of bt controller in BLE Mesh, + task lock is used to replace IRQ lock. + + config BLE_MESH_CLIENT_MSG_TIMEOUT + int "Timeout(ms) for client message response" + range 100 1200000 + default 4000 + help + Timeout value used by the node to get response of the acknowledged + message which is sent by the client model. + + menu "Support for BLE Mesh Client Models" + + config BLE_MESH_CFG_CLI + bool "Configuration Client Model" + help + Enable support for Configuration client model. + + config BLE_MESH_HEALTH_CLI + bool "Health Client Model" + help + Enable support for Health client model. + + config BLE_MESH_GENERIC_ONOFF_CLI + bool "Generic OnOff Client Model" + help + Enable support for Generic OnOff client model. + + config BLE_MESH_GENERIC_LEVEL_CLI + bool "Generic Level Client Model" + help + Enable support for Generic Level client model. + + config BLE_MESH_GENERIC_DEF_TRANS_TIME_CLI + bool "Generic Default Transition Time Client Model" + help + Enable support for Generic Default Transition Time client model. + + config BLE_MESH_GENERIC_POWER_ONOFF_CLI + bool "Generic Power Onoff Client Model" + help + Enable support for Generic Power Onoff client model. + + config BLE_MESH_GENERIC_POWER_LEVEL_CLI + bool "Generic Power Level Client Model" + help + Enable support for Generic Power Level client model. + + config BLE_MESH_GENERIC_BATTERY_CLI + bool "Generic Battery Client Model" + help + Enable support for Generic Battery client model. + + config BLE_MESH_GENERIC_LOCATION_CLI + bool "Generic Location Client Model" + help + Enable support for Generic Location client model. + + config BLE_MESH_GENERIC_PROPERTY_CLI + bool "Generic Property Client Model" + help + Enable support for Generic Property client model. + + config BLE_MESH_SENSOR_CLI + bool "Sensor Client Model" + help + Enable support for Sensor client model. + + config BLE_MESH_TIME_CLI + bool "Time Client Model" + help + Enable support for Time client model. + + config BLE_MESH_SCENE_CLI + bool "Scene Client Model" + help + Enable support for Scene client model. + + config BLE_MESH_SCHEDULER_CLI + bool "Scheduler Client Model" + help + Enable support for Scheduler client model. + + config BLE_MESH_LIGHT_LIGHTNESS_CLI + bool "Light Lightness Client Model" + help + Enable support for Light Lightness client model. + + config BLE_MESH_LIGHT_CTL_CLI + bool "Light CTL Client Model" + help + Enable support for Light CTL client model. + + config BLE_MESH_LIGHT_HSL_CLI + bool "Light HSL Client Model" + help + Enable support for Light HSL client model. + + config BLE_MESH_LIGHT_XYL_CLI + bool "Light XYL Client Model" + help + Enable support for Light XYL client model. + + config BLE_MESH_LIGHT_LC_CLI + bool "Light LC Client Model" + help + Enable support for Light LC client model. + + endmenu + + config BLE_MESH_IV_UPDATE_TEST + bool "Test the IV Update Procedure" + default n + help + This option removes the 96 hour limit of the IV Update Procedure and + lets the state to be changed at any time. + + menu "BLE Mesh specific test option" + + config BLE_MESH_SELF_TEST + bool "Perform BLE Mesh self-tests" + default n + help + This option adds extra self-tests which are run every time BLE Mesh + networking is initialized. + + config BLE_MESH_SHELL + bool "Enable BLE Mesh shell" + default n + help + Activate shell module that provides BLE Mesh commands to the console. + + config BLE_MESH_DEBUG + bool "Enable BLE Mesh debug logs" + default n + help + Enable debug logs for the BLE Mesh functionality. + + if BLE_MESH_DEBUG + + config BLE_MESH_DEBUG_NET + bool "Network layer debug" + help + Enable Network layer debug logs for the BLE Mesh functionality. + + config BLE_MESH_DEBUG_TRANS + bool "Transport layer debug" + help + Enable Transport layer debug logs for the BLE Mesh functionality. + + config BLE_MESH_DEBUG_BEACON + bool "Beacon debug" + help + Enable Beacon-related debug logs for the BLE Mesh functionality. + + config BLE_MESH_DEBUG_CRYPTO + bool "Crypto debug" + help + Enable cryptographic debug logs for the BLE Mesh functionality. + + config BLE_MESH_DEBUG_PROV + bool "Provisioning debug" + help + Enable Provisioning debug logs for the BLE Mesh functionality. + + config BLE_MESH_DEBUG_ACCESS + bool "Access layer debug" + help + Enable Access layer debug logs for the BLE Mesh functionality. + + config BLE_MESH_DEBUG_MODEL + bool "Foundation model debug" + help + Enable Foundation Models debug logs for the BLE Mesh functionality. + + config BLE_MESH_DEBUG_ADV + bool "Advertising debug" + help + Enable advertising debug logs for the BLE Mesh functionality. + + config BLE_MESH_DEBUG_LOW_POWER + bool "Low Power debug" + help + Enable Low Power debug logs for the BLE Mesh functionality. + + config BLE_MESH_DEBUG_FRIEND + bool "Friend debug" + help + Enable Friend debug logs for the BLE Mesh functionality. + + config BLE_MESH_DEBUG_PROXY + bool "Proxy debug" + depends on BLE_MESH_PROXY + help + Enable Proxy protocol debug logs for the BLE Mesh functionality. + + endif # BLE_MESH_DEBUG + + endmenu + +endif # BLE_MESH diff --git a/components/bt/ble_mesh/api/core/esp_ble_mesh_common_api.c b/components/bt/ble_mesh/api/core/esp_ble_mesh_common_api.c new file mode 100644 index 0000000000..4172ef0c7d --- /dev/null +++ b/components/bt/ble_mesh/api/core/esp_ble_mesh_common_api.c @@ -0,0 +1,70 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 "freertos/FreeRTOS.h" +#include "freertos/semphr.h" + +#include "btc/btc_task.h" +#include "btc/btc_manage.h" + +#include "esp_err.h" +#include "esp_bt_defs.h" +#include "esp_bt_main.h" + +#include "btc_ble_mesh_prov.h" +#include "esp_ble_mesh_defs.h" + +esp_err_t esp_ble_mesh_init(esp_ble_mesh_prov_t *prov, esp_ble_mesh_comp_t *comp) +{ + btc_ble_mesh_prov_args_t arg = {0}; + SemaphoreHandle_t semaphore = NULL; + btc_msg_t msg = {0}; + + if (prov == NULL || comp == NULL) { + return ESP_ERR_INVALID_ARG; + } + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + // Create a semaphore + if ((semaphore = xSemaphoreCreateCounting(1, 0)) == NULL) { + LOG_ERROR("%s, Failed to allocate memory for the semaphore", __func__); + return ESP_ERR_NO_MEM; + } + + arg.mesh_init.prov = prov; + arg.mesh_init.comp = comp; + /* Transport semaphore pointer to BTC layer, and will give the semaphore in the BTC task */ + arg.mesh_init.semaphore = semaphore; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_PROV; + msg.act = BTC_BLE_MESH_ACT_MESH_INIT; + + if (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_prov_args_t), NULL) != BT_STATUS_SUCCESS) { + vSemaphoreDelete(semaphore); + LOG_ERROR("%s, BLE Mesh initialise failed", __func__); + return ESP_FAIL; + } + + /* Take the Semaphore, wait BLE Mesh initialization to finish. */ + xSemaphoreTake(semaphore, portMAX_DELAY); + /* Don't forget to delete the semaphore at the end. */ + vSemaphoreDelete(semaphore); + + return ESP_OK; +} + diff --git a/components/bt/ble_mesh/api/core/esp_ble_mesh_local_data_operation_api.c b/components/bt/ble_mesh/api/core/esp_ble_mesh_local_data_operation_api.c new file mode 100644 index 0000000000..6efeaf1765 --- /dev/null +++ b/components/bt/ble_mesh/api/core/esp_ble_mesh_local_data_operation_api.c @@ -0,0 +1,80 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 + +#include "btc/btc_task.h" +#include "btc/btc_manage.h" + +#include "esp_err.h" +#include "esp_bt_defs.h" +#include "esp_bt_main.h" + +#include "btc_ble_mesh_prov.h" +#include "esp_ble_mesh_defs.h" + +int32_t esp_ble_mesh_get_model_publish_period(esp_ble_mesh_model_t *model) +{ + if (model == NULL) { + return 0; + } + return btc_ble_mesh_model_pub_period_get(model); +} + +uint16_t esp_ble_mesh_get_primary_element_address(void) +{ + return btc_ble_mesh_get_primary_addr(); +} + +uint16_t *esp_ble_mesh_is_model_subscribed_to_group(esp_ble_mesh_model_t *model, uint16_t group_addr) +{ + if (model == NULL) { + return NULL; + } + return btc_ble_mesh_model_find_group(model, group_addr); +} + +esp_ble_mesh_elem_t *esp_ble_mesh_find_element(uint16_t element_addr) +{ + return btc_ble_mesh_elem_find(element_addr); +} + +uint8_t esp_ble_mesh_get_element_count(void) +{ + return btc_ble_mesh_elem_count(); +} + +esp_ble_mesh_model_t *esp_ble_mesh_find_vendor_model(const esp_ble_mesh_elem_t *element, + uint16_t company_id, uint16_t model_id) +{ + if (element == NULL) { + return NULL; + } + return btc_ble_mesh_model_find_vnd(element, company_id, model_id); +} + +esp_ble_mesh_model_t *esp_ble_mesh_find_sig_model(const esp_ble_mesh_elem_t *element, uint16_t model_id) +{ + if (element == NULL) { + return NULL; + } + return btc_ble_mesh_model_find(element, model_id); +} + +const esp_ble_mesh_comp_t *esp_ble_mesh_get_composition_data(void) +{ + return btc_ble_mesh_comp_get(); +} + diff --git a/components/bt/ble_mesh/api/core/esp_ble_mesh_low_power_api.c b/components/bt/ble_mesh/api/core/esp_ble_mesh_low_power_api.c new file mode 100644 index 0000000000..6d3745ca6e --- /dev/null +++ b/components/bt/ble_mesh/api/core/esp_ble_mesh_low_power_api.c @@ -0,0 +1,26 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 "btc/btc_task.h" +#include "btc/btc_manage.h" + +#include "esp_err.h" +#include "esp_bt_defs.h" +#include "esp_bt_main.h" + +#include "btc_ble_mesh_prov.h" +#include "esp_ble_mesh_defs.h" + diff --git a/components/bt/ble_mesh/api/core/esp_ble_mesh_networking_api.c b/components/bt/ble_mesh/api/core/esp_ble_mesh_networking_api.c new file mode 100644 index 0000000000..9e920dd113 --- /dev/null +++ b/components/bt/ble_mesh/api/core/esp_ble_mesh_networking_api.c @@ -0,0 +1,338 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 + +#include "btc/btc_task.h" +#include "btc/btc_manage.h" + +#include "esp_err.h" +#include "esp_bt_defs.h" +#include "esp_bt_main.h" + +#include "btc_ble_mesh_prov.h" +#include "esp_ble_mesh_networking_api.h" + +#define ESP_BLE_MESH_TX_SDU_MAX ((CONFIG_BLE_MESH_ADV_BUF_COUNT - 3) * 12) + +static esp_err_t ble_mesh_send_msg(esp_ble_mesh_model_t *model, + esp_ble_mesh_msg_ctx_t *ctx, + uint32_t opcode, + btc_ble_mesh_model_act_t act, + uint16_t length, uint8_t *data, + int32_t msg_timeout, bool need_rsp, + esp_ble_mesh_dev_role_t device_role) +{ + btc_ble_mesh_model_args_t arg = {0}; + uint8_t op_len = 0, mic_len = 0; + uint8_t *msg_data = NULL; + btc_msg_t msg = {0}; + esp_err_t status; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + if (device_role > ROLE_FAST_PROV) { + return ESP_ERR_INVALID_ARG; + } + + /* When data is NULL, it is mandatory to set length to 0 to prevent users from misinterpreting parameters. */ + if (data == NULL) { + length = 0; + } + + if (opcode < 0x100) { + op_len = 1; + } else if (opcode < 0x10000) { + op_len = 2; + } else { + op_len = 3; + } + + if (act == BTC_BLE_MESH_ACT_MODEL_PUBLISH) { + if (op_len + length > model->pub->msg->size) { + LOG_ERROR("%s, Model publication msg size %d is too small", __func__, model->pub->msg->size); + return ESP_ERR_INVALID_ARG; + } + } + + if (act == BTC_BLE_MESH_ACT_MODEL_PUBLISH) { + mic_len = 4; + } else { + mic_len = ctx->send_rel ? 8 : 4; + } + + if (op_len + length + mic_len > MIN(ESP_BLE_MESH_SDU_MAX_LEN, ESP_BLE_MESH_TX_SDU_MAX)) { + LOG_ERROR("%s, Data length %d is too large", __func__, length); + return ESP_ERR_INVALID_ARG; + } + + if (act == BTC_BLE_MESH_ACT_MODEL_PUBLISH) { + bt_mesh_model_msg_init(model->pub->msg, opcode); + net_buf_simple_add_mem(model->pub->msg, data, length); + } else { + msg_data = (uint8_t *)osi_malloc(op_len + length); + if (msg_data == NULL) { + return ESP_ERR_NO_MEM; + } + esp_ble_mesh_model_msg_opcode_init(msg_data, opcode); + memcpy(msg_data + op_len, data, length); + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_MODEL; + msg.act = act; + + if (act == BTC_BLE_MESH_ACT_MODEL_PUBLISH) { + arg.model_publish.model = model; + arg.model_publish.device_role = device_role; + } else { + arg.model_send.model = model; + arg.model_send.ctx = ctx; + arg.model_send.need_rsp = need_rsp; + arg.model_send.opcode = opcode; + arg.model_send.length = op_len + length; + arg.model_send.data = msg_data; + arg.model_send.device_role = device_role; + arg.model_send.msg_timeout = msg_timeout; + } + + status = (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_model_args_t), btc_ble_mesh_prov_arg_deep_copy) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); + + osi_free(msg_data); + + return status; +} + +esp_err_t esp_ble_mesh_register_custom_model_callback(esp_ble_mesh_model_cb_t callback) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + return (btc_profile_cb_set(BTC_PID_MODEL, callback) == 0 ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_mesh_model_msg_opcode_init(uint8_t *data, uint32_t opcode) +{ + uint16_t val; + + if (data == NULL) { + return ESP_ERR_INVALID_ARG; + } + + if (opcode < 0x100) { + /* 1-byte OpCode */ + data[0] = opcode & 0xff; + return ESP_OK; + } + + if (opcode < 0x10000) { + /* 2-byte OpCode, big endian */ + val = sys_cpu_to_be16 (opcode); + memcpy(data, &val, 2); + return ESP_OK; + } + + /* 3-byte OpCode, note that little endian for the least 2 bytes(Company ID) of opcode */ + data[0] = (opcode >> 16) & 0xff; + val = sys_cpu_to_le16(opcode & 0xffff); + memcpy(&data[1], &val, 2); + + return ESP_OK; +} + +esp_err_t esp_ble_mesh_client_model_init(esp_ble_mesh_model_t *model) +{ + if (model == NULL) { + return ESP_ERR_INVALID_ARG; + } + return btc_ble_mesh_client_init(model); +} + +esp_err_t esp_ble_mesh_server_model_send_msg(esp_ble_mesh_model_t *model, + esp_ble_mesh_msg_ctx_t *ctx, uint32_t opcode, + uint16_t length, uint8_t *data) +{ + if (!model || !ctx) { + return ESP_ERR_INVALID_ARG; + } + return ble_mesh_send_msg(model, ctx, opcode, BTC_BLE_MESH_ACT_SERVER_MODEL_SEND, + length, data, 0, false, ROLE_NODE); +} + +esp_err_t esp_ble_mesh_client_model_send_msg(esp_ble_mesh_model_t *model, + esp_ble_mesh_msg_ctx_t *ctx, uint32_t opcode, + uint16_t length, uint8_t *data, int32_t msg_timeout, + bool need_rsp, esp_ble_mesh_dev_role_t device_role) +{ + if (!model || !ctx) { + return ESP_ERR_INVALID_ARG; + } + return ble_mesh_send_msg(model, ctx, opcode, BTC_BLE_MESH_ACT_CLIENT_MODEL_SEND, + length, data, msg_timeout, need_rsp, device_role); +} + +esp_err_t esp_ble_mesh_model_publish(esp_ble_mesh_model_t *model, uint32_t opcode, + uint16_t length, uint8_t *data, + esp_ble_mesh_dev_role_t device_role) +{ + if (!model || !model->pub || !model->pub->msg) { + return ESP_ERR_INVALID_ARG; + } + return ble_mesh_send_msg(model, NULL, opcode, BTC_BLE_MESH_ACT_MODEL_PUBLISH, + length, data, 0, false, device_role); +} + +esp_err_t esp_ble_mesh_node_local_reset(void) +{ + btc_msg_t msg = {0}; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_PROV; + msg.act = BTC_BLE_MESH_ACT_NODE_RESET; + + return (btc_transfer_context(&msg, NULL, 0, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +#if (CONFIG_BLE_MESH_PROVISIONER) + +esp_err_t esp_ble_mesh_provisioner_set_node_name(int index, const char *name) +{ + btc_ble_mesh_prov_args_t arg = {0}; + btc_msg_t msg = {0}; + + if (!name || (strlen(name) > ESP_BLE_MESH_NODE_NAME_MAX_LEN)) { + return ESP_ERR_INVALID_ARG; + } + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_PROV; + msg.act = BTC_BLE_MESH_ACT_PROVISIONER_SET_NODE_NAME; + + arg.set_node_name.index = index; + memset(arg.set_node_name.name, 0, sizeof(arg.set_node_name.name)); + memcpy(arg.set_node_name.name, name, strlen(name)); + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_prov_args_t), NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +const char *esp_ble_mesh_provisioner_get_node_name(int index) +{ + return bt_mesh_provisioner_get_node_name(index); +} + +int esp_ble_mesh_provisioner_get_node_index(const char *name) +{ + if (!name || (strlen(name) > ESP_BLE_MESH_NODE_NAME_MAX_LEN)) { + return -EINVAL; + } + + return bt_mesh_provisioner_get_node_index(name); +} + +esp_err_t esp_ble_mesh_provisioner_add_local_app_key(const uint8_t app_key[16], + uint16_t net_idx, uint16_t app_idx) +{ + btc_ble_mesh_prov_args_t arg = {0}; + btc_msg_t msg = {0}; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_PROV; + msg.act = BTC_BLE_MESH_ACT_PROVISIONER_SET_LOCAL_APP_KEY; + + arg.add_local_app_key.net_idx = net_idx; + arg.add_local_app_key.app_idx = app_idx; + if (app_key) { + memcpy(arg.add_local_app_key.app_key, app_key, 16); + } else { + bzero(arg.add_local_app_key.app_key, 16); + } + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_prov_args_t), NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +const uint8_t *esp_ble_mesh_provisioner_get_local_app_key(uint16_t net_idx, uint16_t app_idx) +{ + return bt_mesh_provisioner_local_app_key_get(net_idx, app_idx); +} + +esp_err_t esp_ble_mesh_provisioner_bind_app_key_to_local_model(uint16_t element_addr, uint16_t app_idx, + uint16_t model_id, uint16_t company_id) +{ + btc_ble_mesh_prov_args_t arg = {0}; + btc_msg_t msg = {0}; + + if (!ESP_BLE_MESH_ADDR_IS_UNICAST(element_addr)) { + return ESP_ERR_INVALID_ARG; + } + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_PROV; + msg.act = BTC_BLE_MESH_ACT_PROVISIONER_BIND_LOCAL_MOD_APP; + + arg.local_mod_app_bind.elem_addr = element_addr; + arg.local_mod_app_bind.app_idx = app_idx; + arg.local_mod_app_bind.model_id = model_id; + arg.local_mod_app_bind.cid = company_id; + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_prov_args_t), NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_mesh_provisioner_add_local_net_key(const uint8_t net_key[16], uint16_t net_idx) +{ + btc_ble_mesh_prov_args_t arg = {0}; + btc_msg_t msg = {0}; + + if (net_idx == ESP_BLE_MESH_KEY_PRIMARY) { + return ESP_ERR_INVALID_ARG; + } + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_PROV; + msg.act = BTC_BLE_MESH_ACT_PROVISIONER_ADD_LOCAL_NET_KEY; + + arg.add_local_net_key.net_idx = net_idx; + if (net_key) { + memcpy(arg.add_local_net_key.net_key, net_key, 16); + } else { + bzero(arg.add_local_net_key.net_key, 16); + } + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_prov_args_t), NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +const uint8_t *esp_ble_mesh_provisioner_get_local_net_key(uint16_t net_idx) +{ + return bt_mesh_provisioner_local_net_key_get(net_idx); +} + +#endif /* CONFIG_BLE_MESH_PROVISIONER */ + +#if (CONFIG_BLE_MESH_FAST_PROV) +const uint8_t *esp_ble_mesh_get_fast_prov_app_key(uint16_t net_idx, uint16_t app_idx) +{ + return bt_mesh_get_fast_prov_app_key(net_idx, app_idx); +} +#endif /* CONFIG_BLE_MESH_FAST_PROV */ + diff --git a/components/bt/ble_mesh/api/core/esp_ble_mesh_provisioning_api.c b/components/bt/ble_mesh/api/core/esp_ble_mesh_provisioning_api.c new file mode 100644 index 0000000000..855bcf188f --- /dev/null +++ b/components/bt/ble_mesh/api/core/esp_ble_mesh_provisioning_api.c @@ -0,0 +1,423 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 "btc/btc_task.h" +#include "btc/btc_manage.h" + +#include "esp_err.h" +#include "esp_bt_defs.h" +#include "esp_bt_main.h" + +#include "btc_ble_mesh_prov.h" +#include "esp_ble_mesh_provisioning_api.h" + +#define MAX_PROV_LINK_IDX (CONFIG_BLE_MESH_PBA_SAME_TIME + CONFIG_BLE_MESH_PBG_SAME_TIME) +#define MAX_OOB_INPUT_NUM 0x5F5E0FF /* Decimal: 99999999 */ + +esp_err_t esp_ble_mesh_register_prov_callback(esp_ble_mesh_prov_cb_t callback) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + return (btc_profile_cb_set(BTC_PID_PROV, callback) == 0 ? ESP_OK : ESP_FAIL); +} + +bool esp_ble_mesh_node_is_provisioned(void) +{ + return bt_mesh_is_provisioned(); +} + +esp_err_t esp_ble_mesh_node_prov_enable(esp_ble_mesh_prov_bearer_t bearers) +{ + btc_ble_mesh_prov_args_t arg = {0}; + btc_msg_t msg = {0}; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_PROV; + msg.act = BTC_BLE_MESH_ACT_PROV_ENABLE; + arg.node_prov_enable.bearers = bearers; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_prov_args_t), NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_mesh_node_prov_disable(esp_ble_mesh_prov_bearer_t bearers) +{ + btc_ble_mesh_prov_args_t arg = {0}; + btc_msg_t msg = {0}; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_PROV; + msg.act = BTC_BLE_MESH_ACT_PROV_DISABLE; + arg.node_prov_disable.bearers = bearers; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_prov_args_t), NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_mesh_node_set_oob_pub_key(uint8_t pub_key_x[32], uint8_t pub_key_y[32], + uint8_t private_key[32]) +{ + btc_ble_mesh_prov_args_t arg = {0}; + btc_msg_t msg = {0}; + + if (!pub_key_x || !pub_key_y || !private_key) { + return ESP_ERR_INVALID_ARG; + } + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_PROV; + msg.act = BTC_BLE_MESH_ACT_SET_OOB_PUB_KEY; + + memcpy(arg.set_oob_pub_key.pub_key_x, pub_key_x, 32); + memcpy(arg.set_oob_pub_key.pub_key_y, pub_key_y, 32); + memcpy(arg.set_oob_pub_key.private_key, private_key, 32); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_prov_args_t), NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_mesh_node_input_number(uint32_t number) +{ + btc_ble_mesh_prov_args_t arg = {0}; + btc_msg_t msg = {0}; + + if (number > MAX_OOB_INPUT_NUM) { + return ESP_ERR_INVALID_ARG; + } + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_PROV; + msg.act = BTC_BLE_MESH_ACT_INPUT_NUMBER; + arg.input_number.number = number; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_prov_args_t), NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_mesh_node_input_string(const char *string) +{ + btc_ble_mesh_prov_args_t arg = {0}; + btc_msg_t msg = {0}; + + if (!string) { + return ESP_ERR_INVALID_ARG; + } + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_PROV; + msg.act = BTC_BLE_MESH_ACT_INPUT_STRING; + memset(arg.input_string.string, 0, sizeof(arg.input_string.string)); + strncpy(arg.input_string.string, string, strlen(string)); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_prov_args_t), NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_mesh_set_unprovisioned_device_name(const char *name) +{ + btc_ble_mesh_prov_args_t arg = {0}; + btc_msg_t msg = {0}; + + if (!name || strlen(name) > ESP_BLE_MESH_DEVICE_NAME_MAX_LEN) { + return ESP_ERR_INVALID_ARG; + } + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_PROV; + msg.act = BTC_BLE_MESH_ACT_SET_DEVICE_NAME; + + memset(arg.set_device_name.name, 0, sizeof(arg.set_device_name.name)); + memcpy(arg.set_device_name.name, name, strlen(name)); + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_prov_args_t), NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +#if (CONFIG_BLE_MESH_PROVISIONER) +esp_err_t esp_ble_mesh_provisioner_read_oob_pub_key(uint8_t link_idx, uint8_t pub_key_x[32], + uint8_t pub_key_y[32]) +{ + btc_ble_mesh_prov_args_t arg = {0}; + btc_msg_t msg = {0}; + + if (!pub_key_x || !pub_key_y || link_idx >= MAX_PROV_LINK_IDX) { + return ESP_ERR_INVALID_ARG; + } + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_PROV; + msg.act = BTC_BLE_MESH_ACT_PROVISIONER_READ_OOB_PUB_KEY; + + arg.provisioner_read_oob_pub_key.link_idx = link_idx; + memcpy(arg.provisioner_read_oob_pub_key.pub_key_x, pub_key_x, 32); + memcpy(arg.provisioner_read_oob_pub_key.pub_key_y, pub_key_y, 32); + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_prov_args_t), NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_mesh_provisioner_input_string(const char *string, uint8_t link_idx) +{ + btc_ble_mesh_prov_args_t arg = {0}; + btc_msg_t msg = {0}; + + if (!string || link_idx >= MAX_PROV_LINK_IDX) { + return ESP_ERR_INVALID_ARG; + } + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_PROV; + msg.act = BTC_BLE_MESH_ACT_PROVISIONER_INPUT_STR; + + memset(arg.provisioner_input_str.string, 0, sizeof(arg.provisioner_input_str.string)); + strncpy(arg.provisioner_input_str.string, string, strlen(string)); + arg.provisioner_input_str.link_idx = link_idx; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_prov_args_t), NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_mesh_provisioner_input_number(uint32_t number, uint8_t link_idx) +{ + btc_ble_mesh_prov_args_t arg = {0}; + btc_msg_t msg = {0}; + + if (number > MAX_OOB_INPUT_NUM || link_idx >= MAX_PROV_LINK_IDX) { + return ESP_ERR_INVALID_ARG; + } + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_PROV; + msg.act = BTC_BLE_MESH_ACT_PROVISIONER_INPUT_NUM; + + arg.provisioner_input_num.number = number; + arg.provisioner_input_num.link_idx = link_idx; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_prov_args_t), NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_mesh_provisioner_prov_enable(esp_ble_mesh_prov_bearer_t bearers) +{ + btc_ble_mesh_prov_args_t arg = {0}; + btc_msg_t msg = {0}; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_PROV; + msg.act = BTC_BLE_MESH_ACT_PROVISIONER_ENABLE; + + arg.provisioner_enable.bearers = bearers; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_prov_args_t), NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_mesh_provisioner_prov_disable(esp_ble_mesh_prov_bearer_t bearers) +{ + btc_ble_mesh_prov_args_t arg = {0}; + btc_msg_t msg = {0}; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_PROV; + msg.act = BTC_BLE_MESH_ACT_PROVISIONER_DISABLE; + + arg.provisioner_disable.bearers = bearers; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_prov_args_t), NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_mesh_provisioner_add_unprov_dev(esp_ble_mesh_unprov_dev_add_t *add_dev, + esp_ble_mesh_dev_add_flag_t flags) +{ + btc_ble_mesh_prov_args_t arg = {0}; + btc_msg_t msg = {0}; + + if (add_dev == NULL) { + return ESP_ERR_INVALID_ARG; + } + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_PROV; + msg.act = BTC_BLE_MESH_ACT_PROVISIONER_DEV_ADD; + + arg.provisioner_dev_add.add_dev.addr_type = add_dev->addr_type; + arg.provisioner_dev_add.add_dev.oob_info = add_dev->oob_info; + arg.provisioner_dev_add.add_dev.bearer = add_dev->bearer; + memcpy(arg.provisioner_dev_add.add_dev.addr, add_dev->addr, sizeof(esp_bd_addr_t)); + memcpy(arg.provisioner_dev_add.add_dev.uuid, add_dev->uuid, 16); + arg.provisioner_dev_add.flags = flags; + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_prov_args_t), NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_mesh_provisioner_delete_dev(esp_ble_mesh_device_delete_t *del_dev) +{ + uint8_t val = DEL_DEV_ADDR_FLAG | DEL_DEV_UUID_FLAG; + btc_ble_mesh_prov_args_t arg = {0}; + btc_msg_t msg = {0}; + + if (del_dev == NULL || (__builtin_popcount(del_dev->flag & val) != 1)) { + return ESP_ERR_INVALID_ARG; + } + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_PROV; + msg.act = BTC_BLE_MESH_ACT_PROVISIONER_DEV_DEL; + + arg.provisioner_dev_del.del_dev.flag = del_dev->flag; + if (del_dev->flag & DEL_DEV_ADDR_FLAG) { + arg.provisioner_dev_del.del_dev.addr_type = del_dev->addr_type; + memcpy(arg.provisioner_dev_del.del_dev.addr, del_dev->addr, sizeof(esp_bd_addr_t)); + } else if (del_dev->flag & DEL_DEV_UUID_FLAG) { + memcpy(arg.provisioner_dev_del.del_dev.uuid, del_dev->uuid, 16); + } + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_prov_args_t), NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_mesh_provisioner_set_dev_uuid_match(const uint8_t *match_val, uint8_t match_len, + uint8_t offset, bool prov_after_match) +{ + btc_ble_mesh_prov_args_t arg = {0}; + btc_msg_t msg = {0}; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_PROV; + msg.act = BTC_BLE_MESH_ACT_PROVISIONER_SET_DEV_UUID_MATCH; + + if (match_len && match_val) { + memcpy(arg.set_dev_uuid_match.match_val, match_val, match_len); + } + arg.set_dev_uuid_match.match_len = match_len; + arg.set_dev_uuid_match.offset = offset; + arg.set_dev_uuid_match.prov_after_match = prov_after_match; + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_prov_args_t), NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_mesh_provisioner_set_prov_data_info(esp_ble_mesh_prov_data_info_t *prov_data_info) +{ + uint8_t val = PROV_DATA_NET_IDX_FLAG | PROV_DATA_FLAGS_FLAG | PROV_DATA_IV_INDEX_FLAG; + btc_ble_mesh_prov_args_t arg = {0}; + btc_msg_t msg = {0}; + + if (prov_data_info == NULL || (__builtin_popcount(prov_data_info->flag & val) != 1)) { + return ESP_ERR_INVALID_ARG; + } + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_PROV; + msg.act = BTC_BLE_MESH_ACT_PROVISIONER_SET_PROV_DATA_INFO; + + arg.set_prov_data_info.prov_data.flag = prov_data_info->flag; + if (prov_data_info->flag & PROV_DATA_NET_IDX_FLAG) { + arg.set_prov_data_info.prov_data.net_idx = prov_data_info->net_idx; + } else if (prov_data_info->flag & PROV_DATA_FLAGS_FLAG) { + arg.set_prov_data_info.prov_data.flags = prov_data_info->flags; + } else if (prov_data_info->flag & PROV_DATA_IV_INDEX_FLAG) { + arg.set_prov_data_info.prov_data.iv_index = prov_data_info->iv_index; + } + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_prov_args_t), NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +#endif /* CONFIG_BLE_MESH_PROVISIONER */ + +/* The following APIs are for fast provisioning */ + +#if (CONFIG_BLE_MESH_FAST_PROV) + +esp_err_t esp_ble_mesh_set_fast_prov_info(esp_ble_mesh_fast_prov_info_t *fast_prov_info) +{ + btc_ble_mesh_prov_args_t arg = {0}; + btc_msg_t msg = {0}; + + if (fast_prov_info == NULL) { + return ESP_ERR_INVALID_ARG; + } + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_PROV; + msg.act = BTC_BLE_MESH_ACT_SET_FAST_PROV_INFO; + + arg.set_fast_prov_info.unicast_min = fast_prov_info->unicast_min; + arg.set_fast_prov_info.unicast_max = fast_prov_info->unicast_max; + arg.set_fast_prov_info.net_idx = fast_prov_info->net_idx; + arg.set_fast_prov_info.flags = fast_prov_info->flags; + arg.set_fast_prov_info.iv_index = fast_prov_info->iv_index; + arg.set_fast_prov_info.offset = fast_prov_info->offset; + arg.set_fast_prov_info.match_len = fast_prov_info->match_len; + if (fast_prov_info->match_len && fast_prov_info->match_val) { + memcpy(arg.set_fast_prov_info.match_val, fast_prov_info->match_val, fast_prov_info->match_len); + } + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_prov_args_t), NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_mesh_set_fast_prov_action(esp_ble_mesh_fast_prov_action_t action) +{ + btc_ble_mesh_prov_args_t arg = {0}; + btc_msg_t msg = {0}; + + if (action >= FAST_PROV_ACT_MAX) { + return ESP_ERR_INVALID_ARG; + } + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_PROV; + msg.act = BTC_BLE_MESH_ACT_SET_FAST_PROV_ACTION; + + arg.set_fast_prov_action.action = action; + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_prov_args_t), NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +#endif /* CONFIG_BLE_MESH_FAST_PROV */ + diff --git a/components/bt/ble_mesh/api/core/esp_ble_mesh_proxy_api.c b/components/bt/ble_mesh/api/core/esp_ble_mesh_proxy_api.c new file mode 100644 index 0000000000..0605e97a6b --- /dev/null +++ b/components/bt/ble_mesh/api/core/esp_ble_mesh_proxy_api.c @@ -0,0 +1,65 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 "btc/btc_task.h" +#include "btc/btc_manage.h" + +#include "esp_err.h" +#include "esp_bt_defs.h" +#include "esp_bt_main.h" + +#include "btc_ble_mesh_prov.h" +#include "esp_ble_mesh_defs.h" + +esp_err_t esp_ble_mesh_proxy_identity_enable(void) +{ + btc_msg_t msg = {0}; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_PROV; + msg.act = BTC_BLE_MESH_ACT_PROXY_IDENTITY_ENABLE; + + return (btc_transfer_context(&msg, NULL, 0, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_mesh_proxy_gatt_enable(void) +{ + btc_msg_t msg = {0}; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_PROV; + msg.act = BTC_BLE_MESH_ACT_PROXY_GATT_ENABLE; + + return (btc_transfer_context(&msg, NULL, 0, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_mesh_proxy_gatt_disable(void) +{ + btc_msg_t msg = {0}; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_PROV; + msg.act = BTC_BLE_MESH_ACT_PROXY_GATT_DISABLE; + + return (btc_transfer_context(&msg, NULL, 0, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + diff --git a/components/bt/ble_mesh/api/core/include/esp_ble_mesh_common_api.h b/components/bt/ble_mesh/api/core/include/esp_ble_mesh_common_api.h new file mode 100644 index 0000000000..a550ad381f --- /dev/null +++ b/components/bt/ble_mesh/api/core/include/esp_ble_mesh_common_api.h @@ -0,0 +1,37 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#ifndef _ESP_BLE_MESH_COMMON_API_H_ +#define _ESP_BLE_MESH_COMMON_API_H_ + +#include "esp_ble_mesh_defs.h" + +/** + * @brief Initialize BLE Mesh module. + * This API initializes provisioning capabilities and composition data information. + * + * @note After calling this API, the device needs to call esp_ble_mesh_prov_enable() + * to enable provisioning functionality again. + * + * @param[in] prov: Pointer to the device provisioning capabilities. This pointer must + * remain valid during the lifetime of the BLE Mesh device. + * @param[in] comp: Pointer to the device composition data information. This pointer + * must remain valid during the lifetime of the BLE Mesh device. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_init(esp_ble_mesh_prov_t *prov, esp_ble_mesh_comp_t *comp); + +#endif /* _ESP_BLE_MESH_COMMON_API_H_ */ diff --git a/components/bt/ble_mesh/api/core/include/esp_ble_mesh_local_data_operation_api.h b/components/bt/ble_mesh/api/core/include/esp_ble_mesh_local_data_operation_api.h new file mode 100644 index 0000000000..0acb6d73f0 --- /dev/null +++ b/components/bt/ble_mesh/api/core/include/esp_ble_mesh_local_data_operation_api.h @@ -0,0 +1,113 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#ifndef _ESP_BLE_MESH_LOCAL_DATA_OPERATION_API_H_ +#define _ESP_BLE_MESH_LOCAL_DATA_OPERATION_API_H_ + +#include "esp_ble_mesh_defs.h" + +/** + * @brief Get the model publish period, the unit is ms. + * + * @param[in] model: Model instance pointer. + * + * @return Publish period value on success, 0 or (negative) error code from errno.h on failure. + * + */ +int32_t esp_ble_mesh_get_model_publish_period(esp_ble_mesh_model_t *model); + +/** + * @brief Get the address of the primary element. + * + * @param None. + * + * @return Address of the primary element on success, or + * ESP_BLE_MESH_ADDR_UNASSIGNED on failure which means the device has not been provisioned. + * + */ +uint16_t esp_ble_mesh_get_primary_element_address(void); + +/** + * @brief Check if the model has subscribed to the given group address. + * Note: E.g., once a status message is received and the destination address + * is a group address, the model uses this API to check if it is successfully subscribed + * to the given group address. + * + * @param[in] model: Pointer to the model. + * @param[in] group_addr: Group address. + * + * @return Pointer to the group address within the Subscription List of the model on success, or + * NULL on failure which means the model has not subscribed to the given group address. + * Note: With the pointer to the group address returned, you can reset the group address + * to 0x0000 in order to unsubscribe the model from the group. + * + */ +uint16_t *esp_ble_mesh_is_model_subscribed_to_group(esp_ble_mesh_model_t *model, uint16_t group_addr); + +/** + * @brief Find the BLE Mesh element pointer via the element address. + * + * @param[in] element_addr: Element address. + * + * @return Pointer to the element on success, or NULL on failure. + * + */ +esp_ble_mesh_elem_t *esp_ble_mesh_find_element(uint16_t element_addr); + +/** + * @brief Get the number of elements that have been registered. + * + * @param None. + * + * @return Number of elements. + * + */ +uint8_t esp_ble_mesh_get_element_count(void); + +/** + * @brief Find the Vendor specific model with the given element, + * the company ID and the Vendor Model ID. + * + * @param[in] element: Element to which the model belongs. + * @param[in] company_id: A 16-bit company identifier assigned by the Bluetooth SIG. + * @param[in] model_id: A 16-bit vendor-assigned model identifier. + * + * @return Pointer to the Vendor Model on success, or NULL on failure which means the Vendor Model is not found. + * + */ +esp_ble_mesh_model_t *esp_ble_mesh_find_vendor_model(const esp_ble_mesh_elem_t *element, + uint16_t company_id, uint16_t model_id); + +/** + * @brief Find the SIG model with the given element and Model id. + * + * @param[in] element: Element to which the model belongs. + * @param[in] model_id: SIG model identifier. + * + * @return Pointer to the SIG Model on success, or NULL on failure which means the SIG Model is not found. + * + */ +esp_ble_mesh_model_t *esp_ble_mesh_find_sig_model(const esp_ble_mesh_elem_t *element, uint16_t model_id); + +/** + * @brief Get the Composition data which has been registered. + * + * @param None. + * + * @return Pointer to the Composition data on success, or NULL on failure which means the Composition data is not initialized. + * + */ +const esp_ble_mesh_comp_t *esp_ble_mesh_get_composition_data(void); + +#endif /* _ESP_BLE_MESH_LOCAL_DATA_OPERATION_API_H_ */ diff --git a/components/bt/ble_mesh/api/core/include/esp_ble_mesh_low_power_api.h b/components/bt/ble_mesh/api/core/include/esp_ble_mesh_low_power_api.h new file mode 100644 index 0000000000..7a203d50cc --- /dev/null +++ b/components/bt/ble_mesh/api/core/include/esp_ble_mesh_low_power_api.h @@ -0,0 +1,20 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#ifndef _ESP_BLE_MESH_LOW_POWER_API_H_ +#define _ESP_BLE_MESH_LOW_POWER_API_H_ + +#include "esp_ble_mesh_defs.h" + +#endif /* _ESP_BLE_MESH_LOW_POWER_API_H_ */ diff --git a/components/bt/ble_mesh/api/core/include/esp_ble_mesh_networking_api.h b/components/bt/ble_mesh/api/core/include/esp_ble_mesh_networking_api.h new file mode 100644 index 0000000000..d3df66879d --- /dev/null +++ b/components/bt/ble_mesh/api/core/include/esp_ble_mesh_networking_api.h @@ -0,0 +1,267 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#ifndef _ESP_BLE_MESH_NETWORKING_API_H_ +#define _ESP_BLE_MESH_NETWORKING_API_H_ + +#include "esp_ble_mesh_defs.h" + +/** @brief: event, event code of user-defined model events; param, parameters of user-defined model events */ +typedef void (* esp_ble_mesh_model_cb_t)(esp_ble_mesh_model_cb_event_t event, + esp_ble_mesh_model_cb_param_t *param); + +/** + * @brief Register BLE Mesh callback for user-defined models' operations. + * This callback can report the following events generated for the user-defined models: + * - Call back the messages received by user-defined client and server models to the + * application layer; + * - If users call esp_ble_mesh_server/client_model_send, this callback notifies the + * application layer of the send_complete event; + * - If user-defined client model sends a message that requires response, and the response + * message is received after the timer expires, the response message will be reported + * to the application layer as published by a peer device; + * - If the user-defined client model fails to receive the response message during a specified + * period of time, a timeout event will be reported to the application layer. + * + * @note The client models (i.e. Config Client model, Health Client model, Generic + * Client models, Sensor Client model, Scene Client model and Lighting Client models) + * that have been realized internally have their specific register functions. + * For example, esp_ble_mesh_register_config_client_callback is the register + * function for Config Client Model. + * + * @param[in] callback: Pointer to the callback function. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_register_custom_model_callback(esp_ble_mesh_model_cb_t callback); + +/** + * @brief Add the message opcode to the beginning of the model message + * before sending or publishing the model message. + * + * @note This API is only used to set the opcode of the message. + * + * @param[in] data: Pointer to the message data. + * @param[in] opcode: The message opcode. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_model_msg_opcode_init(uint8_t *data, uint32_t opcode); + +/** + * @brief Initialize the user-defined client model. All user-defined client models + * shall call this function to initialize the client model internal data. + * Node: Before calling this API, the op_pair_size and op_pair variabled within + * the user_data(defined using esp_ble_mesh_client_t_) of the client model + * need to be initialized. + * + * @param[in] model: BLE Mesh Client model to which the message belongs. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_client_model_init(esp_ble_mesh_model_t *model); + +/** + * @brief Send server model messages(such as server model status messages). + * + * @param[in] model: BLE Mesh Server Model to which the message belongs. + * @param[in] ctx: Message context, includes keys, TTL, etc. + * @param[in] opcode: Message opcode. + * @param[in] length: Message length (exclude the message opcode). + * @param[in] data: Parameters of Access Payload (exclude the message opcode) to be sent. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_server_model_send_msg(esp_ble_mesh_model_t *model, + esp_ble_mesh_msg_ctx_t *ctx, uint32_t opcode, + uint16_t length, uint8_t *data); + +/** + * @brief Send client model message (such as model get, set, etc). + * + * @param[in] model: BLE Mesh Client Model to which the message belongs. + * @param[in] ctx: Message context, includes keys, TTL, etc. + * @param[in] opcode: Message opcode. + * @param[in] length: Message length (exclude the message opcode). + * @param[in] data: Parameters of the Access Payload (exclude the message opcode) to be sent. + * @param[in] msg_timeout: Time to get response to the message (in milliseconds). + * @param[in] need_rsp: TRUE if the opcode requires the peer device to reply, FALSE otherwise. + * @param[in] device_role: Role of the device (Node/Provisioner) that sends the message. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_client_model_send_msg(esp_ble_mesh_model_t *model, + esp_ble_mesh_msg_ctx_t *ctx, uint32_t opcode, + uint16_t length, uint8_t *data, int32_t msg_timeout, + bool need_rsp, esp_ble_mesh_dev_role_t device_role); + +/** + * @brief Send a model publication message. + * + * @note Before calling this function, the user needs to ensure that the model + * publication message (@ref esp_ble_mesh_model_pub_t.msg) contains a valid + * message to be sent. And if users want to update the publishing message, + * this API should be called in ESP_BLE_MESH_MODEL_PUBLISH_UPDATE_EVT + * with the message updated. + * + * + * @param[in] model: Mesh (client) Model publishing the message. + * @param[in] opcode: Message opcode. + * @param[in] length: Message length (exclude the message opcode). + * @param[in] data: Parameters of the Access Payload (exclude the message opcode) to be sent. + * @param[in] device_role: Role of the device (node/provisioner) publishing the message of the type esp_ble_mesh_dev_role_t. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_model_publish(esp_ble_mesh_model_t *model, uint32_t opcode, + uint16_t length, uint8_t *data, + esp_ble_mesh_dev_role_t device_role); + +/** + * @brief Reset the provisioning procedure of the local BLE Mesh node. + * + * @note All provisioning information in this node will be deleted and the node + * needs to be reprovisioned. The API function esp_ble_mesh_node_prov_enable() + * needs to be called to start a new provisioning procedure. + * + * @param None. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_node_local_reset(void); + +/** + * @brief This function is called to set the node (provisioned device) name. + * + * @param[in] index: Index of the node in the node queue. + * @param[in] name: Name (end by '\0') to be set for the node. + * + * @note index is obtained from the parameters of ESP_BLE_MESH_PROVISIONER_PROV_COMPLETE_EVT. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_provisioner_set_node_name(int index, const char *name); + +/** + * @brief This function is called to get the node (provisioned device) name. + * + * @param[in] index: Index of the node in the node queue. + * + * @note index is obtained from the parameters of ESP_BLE_MESH_PROVISIONER_PROV_COMPLETE_EVT. + * + * @return Node name on success, or NULL on failure. + * + */ +const char *esp_ble_mesh_provisioner_get_node_name(int index); + +/** + * @brief This function is called to get the node (provisioned device) index. + * + * @param[in] name: Name of the node (end by '\0'). + * + * @return Node index on success, or (negative) error code from errno.h on failure. + * + */ +int esp_ble_mesh_provisioner_get_node_index(const char *name); + +/** + * @brief This function is called to set the app key for the local BLE Mesh stack. + * + * @param[in] app_key: The app key to be set for the local BLE Mesh stack. + * @param[in] net_idx: The network key index. + * @param[in] app_idx: The app key index. + * + * @note app_key: If set to NULL, app_key will be generated internally. + * net_idx: Should be an existing one. + * app_idx: If it is going to be generated internally, it should be set to + * 0xFFFF, and the new app_idx will be reported via an event. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_provisioner_add_local_app_key(const uint8_t app_key[16], uint16_t net_idx, uint16_t app_idx); + +/** + * @brief This function is called by Provisioner to get the local app key value. + * + * @param[in] net_idx: Network key index. + * @param[in] app_idx: Application key index. + * + * @return App key on success, or NULL on failure. + * + */ +const uint8_t *esp_ble_mesh_provisioner_get_local_app_key(uint16_t net_idx, uint16_t app_idx); + +/** + * @brief This function is called by Provisioner to bind own model with proper app key. + * + * @param[in] element_addr: Provisioner local element address + * @param[in] app_idx: Provisioner local appkey index + * @param[in] model_id: Provisioner local model id + * @param[in] company_id: Provisioner local company id + * + * @note company_id: If going to bind app_key with local vendor model, company_id + * should be set to 0xFFFF. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_provisioner_bind_app_key_to_local_model(uint16_t element_addr, uint16_t app_idx, + uint16_t model_id, uint16_t company_id); + +/** + * @brief This function is called by Provisioner to add local network key. + * + * @param[in] net_key: The network key to be added to the Provisioner local BLE Mesh stack. + * @param[in] net_idx: The network key index. + * + * @note net_key: If set to NULL, net_key will be generated internally. + * net_idx: If it is going to be generated internally, it should be set to + * 0xFFFF, and the new net_idx will be reported via an event. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_provisioner_add_local_net_key(const uint8_t net_key[16], uint16_t net_idx); + +/** + * @brief This function is called by Provisioner to get the local network key value. + * + * @param[in] net_idx: Network key index. + * + * @return Network key on success, or NULL on failure. + * + */ +const uint8_t *esp_ble_mesh_provisioner_get_local_net_key(uint16_t net_idx); + +/** + * @brief This function is called to get fast provisioning application key. + * + * @param[in] net_idx: Network key index. + * @param[in] app_idx: Application key index. + * + * @return Application key on success, or NULL on failure. + * + */ +const uint8_t *esp_ble_mesh_get_fast_prov_app_key(uint16_t net_idx, uint16_t app_idx); + +#endif /* _ESP_BLE_MESH_NETWORKING_API_H_ */ diff --git a/components/bt/ble_mesh/api/core/include/esp_ble_mesh_provisioning_api.h b/components/bt/ble_mesh/api/core/include/esp_ble_mesh_provisioning_api.h new file mode 100644 index 0000000000..e03c4d4ef1 --- /dev/null +++ b/components/bt/ble_mesh/api/core/include/esp_ble_mesh_provisioning_api.h @@ -0,0 +1,316 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#ifndef _ESP_BLE_MESH_PROVISIONING_API_H_ +#define _ESP_BLE_MESH_PROVISIONING_API_H_ + +#include "esp_ble_mesh_defs.h" + +/** @brief: event, event code of provisioning events; param, parameters of provisioning events */ +typedef void (* esp_ble_mesh_prov_cb_t)(esp_ble_mesh_prov_cb_event_t event, + esp_ble_mesh_prov_cb_param_t *param); + +/** + * @brief Register BLE Mesh provisioning callback. + * + * @param[in] callback: Pointer to the callback function. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_register_prov_callback(esp_ble_mesh_prov_cb_t callback); + +/** + * @brief Check if a device has been provisioned. + * + * @param None. + * + * @return TRUE if the device is provisioned, FALSE if the device is unprovisioned. + * + */ +bool esp_ble_mesh_node_is_provisioned(void); + +/** + * @brief Enable specific provisioning bearers to get the device ready for provisioning. + * + * @note PB-ADV: send unprovisioned device beacon. + * PB-GATT: send connectable advertising packets. + * + * @param bearers: Bit-wise OR of provisioning bearers. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_node_prov_enable(esp_ble_mesh_prov_bearer_t bearers); + +/** + * @brief Disable specific provisioning bearers to make a device inaccessible for provisioning. + * + * @param bearers: Bit-wise OR of provisioning bearers. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_node_prov_disable(esp_ble_mesh_prov_bearer_t bearers); + +/** + * @brief Unprovisioned device set own oob public key & private key pair. + * + * @param[in] pub_key_x: Unprovisioned device's Public Key X + * @param[in] pub_key_y: Unprovisioned device's Public Key Y + * @param[in] private_key: Unprovisioned device's Private Key + * + * @return ESP_OK on success or error code otherwise. + */ +esp_err_t esp_ble_mesh_node_set_oob_pub_key(uint8_t pub_key_x[32], uint8_t pub_key_y[32], + uint8_t private_key[32]); + +/** + * @brief Provide provisioning input OOB number. + * + * @note This is intended to be called if the user has received ESP_BLE_MESH_NODE_PROV_INPUT_EVT + * with ESP_BLE_MESH_ENTER_NUMBER as the action. + * + * @param[in] number: Number input by device. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_node_input_number(uint32_t number); + +/** + * @brief Provide provisioning input OOB string. + * + * @note This is intended to be called if the user has received ESP_BLE_MESH_NODE_PROV_INPUT_EVT + * with ESP_BLE_MESH_ENTER_STRING as the action. + * + * @param[in] string: String input by device. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_node_input_string(const char *string); + +/** + * @brief Using this function, an unprovisioned device can set its own device name, + * which will be broadcasted in its advertising data. + * + * @param[in] name: Unprovisioned device name + * + * @note This API applicable to PB-GATT mode only by setting the name to the scan response data, + * it doesn't apply to PB-ADV mode. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_set_unprovisioned_device_name(const char *name); + +/** + * @brief Provisioner inputs unprovisioned device's oob public key. + * + * @param[in] link_idx: The provisioning link index + * @param[in] pub_key_x: Unprovisioned device's Public Key X + * @param[in] pub_key_y: Unprovisioned device's Public Key Y + * + * @return ESP_OK on success or error code otherwise. + */ +esp_err_t esp_ble_mesh_provisioner_read_oob_pub_key(uint8_t link_idx, uint8_t pub_key_x[32], + uint8_t pub_key_y[32]); + +/** + * @brief Provide provisioning input OOB string. + * + * This is intended to be called after the esp_ble_mesh_prov_t prov_input_num + * callback has been called with ESP_BLE_MESH_ENTER_STRING as the action. + * + * @param[in] string: String input by Provisioner. + * @param[in] link_idx: The provisioning link index. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_provisioner_input_string(const char *string, uint8_t link_idx); + +/** + * @brief Provide provisioning input OOB number. + * + * This is intended to be called after the esp_ble_mesh_prov_t prov_input_num + * callback has been called with ESP_BLE_MESH_ENTER_NUMBER as the action. + * + * @param[in] number: Number input by Provisioner. + * @param[in] link_idx: The provisioning link index. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_provisioner_input_number(uint32_t number, uint8_t link_idx); + +/** + * @brief Enable one or more provisioning bearers. + * + * @param[in] bearers: Bit-wise OR of provisioning bearers. + * + * @note PB-ADV: Enable BLE scan. + * PB-GATT: Initialize corresponding BLE Mesh Proxy info. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_provisioner_prov_enable(esp_ble_mesh_prov_bearer_t bearers); + +/** + * @brief Disable one or more provisioning bearers. + * + * @param[in] bearers: Bit-wise OR of provisioning bearers. + * + * @note PB-ADV: Disable BLE scan. + * PB-GATT: Break any existing BLE Mesh Provisioning connections. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_provisioner_prov_disable(esp_ble_mesh_prov_bearer_t bearers); + +/** + * @brief Add unprovisioned device info to the unprov_dev queue. + * + * @param[in] add_dev: Pointer to a struct containing the device information + * @param[in] flags: Flags indicate several operations on the device information + * - Remove device information from queue after device has been provisioned (BIT0) + * - Start provisioning immediately after device is added to queue (BIT1) + * - Device can be removed if device queue is full (BIT2) + * + * @return ESP_OK on success or error code otherwise. + * + * @note: 1. Currently address type only supports public address and static random address. + * 2. If device UUID and/or device address as well as address type already exist in the + * device queue, but the bearer is different from the existing one, add operation + * will also be successful and it will update the provision bearer supported by + * the device. + * 3. For example, if the Provisioner wants to add an unprovisioned device info before + * receiving its unprovisioned device beacon or Mesh Provisioning advertising packets, + * the Provisioner can use this API to add the device info with each one or both of + * device UUID and device address added. When the Provisioner gets the device's + * advertising packets, it will start provisioning the device internally. + * - In this situation, the Provisioner can set bearers with each one or both of + * ESP_BLE_MESH_PROV_ADV and ESP_BLE_MESH_PROV_GATT enabled, and cannot set flags + * with ADD_DEV_START_PROV_NOW_FLAG enabled. + * 4. Another example is when the Provisioner receives the unprovisioned device's beacon or + * Mesh Provisioning advertising packets, the advertising packets will be reported on to + * the application layer using the callback registered by the function + * esp_ble_mesh_register_prov_callback. And in the callback, the Provisioner + * can call this API to start provisioning the device. + * - If the Provisioner uses PB-ADV to provision, either one or both of device UUID and + * device address can be added, bearers shall be set with ESP_BLE_MESH_PROV_ADV + * enabled and the flags shall be set with ADD_DEV_START_PROV_NOW_FLAG enabled. + * - If the Provisioner uses PB-GATT to provision, both the device UUID and device + * address need to be added, bearers shall be set with ESP_BLE_MESH_PROV_GATT enabled, + * and the flags shall be set with ADD_DEV_START_PROV_NOW_FLAG enabled. + * - If the Provisioner just wants to store the unprovisioned device info when receiving + * its advertising packets and start to provision it the next time (e.g. after receiving + * its advertising packets again), then it can add the device info with either one or both + * of device UUID and device address included. Bearers can be set with either one or both + * of ESP_BLE_MESH_PROV_ADV and ESP_BLE_MESH_PROV_GATT enabled (recommend to enable the + * bearer which will receive its advertising packets, because if the other bearer is + * enabled, the Provisioner is not aware if the device supports the bearer), and flags + * cannot be set with ADD_DEV_START_PROV_NOW_FLAG enabled. + * - Note: ESP_BLE_MESH_PROV_ADV, ESP_BLE_MESH_PROV_GATT and ADD_DEV_START_PROV_NOW_FLAG + * can not be enabled at the same time. + * + */ +esp_err_t esp_ble_mesh_provisioner_add_unprov_dev(esp_ble_mesh_unprov_dev_add_t *add_dev, + esp_ble_mesh_dev_add_flag_t flags); + +/** + * @brief Delete device from queue, reset current provisioning link and reset the node. + * + * @note If the device is in the queue, remove it from the queue; if the device is being + * provisioned, terminate the provisioning procedure; if the device has already + * been provisioned, reset the device. And either one of the addr or device UUID + * can be input. + * + * @param[in] del_dev: Pointer to a struct containing the device information. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_provisioner_delete_dev(esp_ble_mesh_device_delete_t *del_dev); + +/** + * @brief Callback for Provisioner that received advertising packets from unprovisioned devices which are + * not in the unprovisioned device queue. + * + * Report on the unprovisioned device beacon and mesh provisioning service adv data to application. + * + * @param[in] addr: Pointer to the unprovisioned device address. + * @param[in] addr_type: Unprovisioned device address type. + * @param[in] adv_type: Adv packet type(ADV_IND or ADV_NONCONN_IND). + * @param[in] dev_uuid: Unprovisioned device UUID pointer. + * @param[in] oob_info: OOB information of the unprovisioned device. + * @param[in] bearer: Adv packet received from PB-GATT or PB-ADV bearer. + * + */ +typedef void (*esp_ble_mesh_prov_adv_cb_t)(const esp_bd_addr_t addr, const esp_ble_addr_type_t addr_type, + const uint8_t adv_type, const uint8_t *dev_uuid, + uint16_t oob_info, esp_ble_mesh_prov_bearer_t bearer); + +/** + * @brief This function is called by Provisioner to set the part of the device UUID + * to be compared before starting to provision. + * + * @param[in] match_val: Value to be compared with the part of the device UUID. + * @param[in] match_len: Length of the compared match value. + * @param[in] offset: Offset of the device UUID to be compared (based on zero). + * @param[in] prov_after_match: Flag used to indicate whether provisioner should start to provision + * the device immediately if the part of the UUID matches. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_provisioner_set_dev_uuid_match(const uint8_t *match_val, uint8_t match_len, + uint8_t offset, bool prov_after_match); + +/** + * @brief This function is called by Provisioner to set provisioning data information + * before starting to provision. + * + * @param[in] prov_data_info: Pointer to a struct containing net_idx or flags or iv_index. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_provisioner_set_prov_data_info(esp_ble_mesh_prov_data_info_t *prov_data_info); + +/** + * @brief This function is called to set provisioning data information before starting + * fast provisioning. + * + * @param[in] fast_prov_info: Pointer to a struct containing unicast address range, net_idx, etc. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_set_fast_prov_info(esp_ble_mesh_fast_prov_info_t *fast_prov_info); + +/** + * @brief This function is called to start/suspend/exit fast provisioning. + * + * @param[in] action: fast provisioning action (i.e. enter, suspend, exit). + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_set_fast_prov_action(esp_ble_mesh_fast_prov_action_t action); + +#endif /* _ESP_BLE_MESH_PROVISIONING_API_H_ */ diff --git a/components/bt/ble_mesh/api/core/include/esp_ble_mesh_proxy_api.h b/components/bt/ble_mesh/api/core/include/esp_ble_mesh_proxy_api.h new file mode 100644 index 0000000000..67a785dda6 --- /dev/null +++ b/components/bt/ble_mesh/api/core/include/esp_ble_mesh_proxy_api.h @@ -0,0 +1,59 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#ifndef _ESP_BLE_MESH_PROXY_API_H_ +#define _ESP_BLE_MESH_PROXY_API_H_ + +#include "esp_ble_mesh_defs.h" + +/** + * @brief Enable advertising with Node Identity. + * + * @note This API requires that GATT Proxy support be enabled. Once called, + * each subnet starts advertising using Node Identity for the next 60 + * seconds, and after 60s Network ID will be advertised. + * Under normal conditions, the BLE Mesh Proxy Node Identity and + * Network ID advertising will be enabled automatically by BLE Mesh + * stack after the device is provisioned. + * + * @param None. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_proxy_identity_enable(void); + +/** + * @brief Enable BLE Mesh GATT Proxy Service. + * + * @param None. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_proxy_gatt_enable(void); + +/** + * @brief Disconnect the BLE Mesh GATT Proxy connection if there is any, and + * disable the BLE Mesh GATT Proxy Service. + * + * @param None. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_proxy_gatt_disable(void); + +#endif /* _ESP_BLE_MESH_PROXY_API_H_ */ + diff --git a/components/bt/ble_mesh/api/esp_ble_mesh_defs.h b/components/bt/ble_mesh/api/esp_ble_mesh_defs.h new file mode 100644 index 0000000000..6e751bd315 --- /dev/null +++ b/components/bt/ble_mesh/api/esp_ble_mesh_defs.h @@ -0,0 +1,1524 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#ifndef _ESP_BLE_MESH_DEFS_H_ +#define _ESP_BLE_MESH_DEFS_H_ + +#include + +#include "esp_bt_defs.h" + +#include "mesh_proxy.h" +#include "mesh_access.h" +#include "mesh_main.h" + +#include "mesh.h" +#include "proxy.h" +#include "foundation.h" +#include "provisioner_main.h" + +#include "model_opcode.h" +#include "mesh_common.h" + +#define ESP_BLE_MESH_SDU_MAX_LEN 384 + +/*!< The maximum length of a BLE Mesh provisioned node name */ +#define ESP_BLE_MESH_NODE_NAME_MAX_LEN 31 + +/*!< The maximum length of a BLE Mesh unprovisioned device name */ +#define ESP_BLE_MESH_DEVICE_NAME_MAX_LEN DEVICE_NAME_SIZE + +/*!< Define the BLE Mesh octet 16 bytes size */ +#define ESP_BLE_MESH_OCTET16_LEN 16 +typedef uint8_t esp_ble_mesh_octet16_t[ESP_BLE_MESH_OCTET16_LEN]; + +/*!< Define the BLE Mesh octet 8 bytes size */ +#define ESP_BLE_MESH_OCTET8_LEN 8 +typedef uint8_t esp_ble_mesh_octet8_t[ESP_BLE_MESH_OCTET8_LEN]; + +#define ESP_BLE_MESH_ADDR_UNASSIGNED BLE_MESH_ADDR_UNASSIGNED +#define ESP_BLE_MESH_ADDR_ALL_NODES BLE_MESH_ADDR_ALL_NODES +#define ESP_BLE_MESH_ADDR_PROXIES BLE_MESH_ADDR_PROXIES +#define ESP_BLE_MESH_ADDR_FRIENDS BLE_MESH_ADDR_FRIENDS +#define ESP_BLE_MESH_ADDR_RELAYS BLE_MESH_ADDR_RELAYS + +#define ESP_BLE_MESH_KEY_UNUSED BLE_MESH_KEY_UNUSED +#define ESP_BLE_MESH_KEY_DEV BLE_MESH_KEY_DEV + +#define ESP_BLE_MESH_KEY_PRIMARY BLE_MESH_KEY_PRIMARY +#define ESP_BLE_MESH_KEY_ANY BLE_MESH_KEY_ANY + +/*!< Primary Network Key index */ +#define ESP_BLE_MESH_NET_PRIMARY BLE_MESH_NET_PRIMARY + +/*!< Relay state value */ +#define ESP_BLE_MESH_RELAY_DISABLED BLE_MESH_RELAY_DISABLED +#define ESP_BLE_MESH_RELAY_ENABLED BLE_MESH_RELAY_ENABLED +#define ESP_BLE_MESH_RELAY_NOT_SUPPORTED BLE_MESH_RELAY_NOT_SUPPORTED + +/*!< Beacon state value */ +#define ESP_BLE_MESH_BEACON_DISABLED BLE_MESH_BEACON_DISABLED +#define ESP_BLE_MESH_BEACON_ENABLED BLE_MESH_BEACON_ENABLED + +/*!< GATT Proxy state value */ +#define ESP_BLE_MESH_GATT_PROXY_DISABLED BLE_MESH_GATT_PROXY_DISABLED +#define ESP_BLE_MESH_GATT_PROXY_ENABLED BLE_MESH_GATT_PROXY_ENABLED +#define ESP_BLE_MESH_GATT_PROXY_NOT_SUPPORTED BLE_MESH_GATT_PROXY_NOT_SUPPORTED + +/*!< Friend state value */ +#define ESP_BLE_MESH_FRIEND_DISABLED BLE_MESH_FRIEND_DISABLED +#define ESP_BLE_MESH_FRIEND_ENABLED BLE_MESH_FRIEND_ENABLED +#define ESP_BLE_MESH_FRIEND_NOT_SUPPORTED BLE_MESH_FRIEND_NOT_SUPPORTED + +/*!< Node identity state value */ +#define ESP_BLE_MESH_NODE_IDENTITY_STOPPED BLE_MESH_NODE_IDENTITY_STOPPED +#define ESP_BLE_MESH_NODE_IDENTITY_RUNNING BLE_MESH_NODE_IDENTITY_RUNNING +#define ESP_BLE_MESH_NODE_IDENTITY_NOT_SUPPORTED BLE_MESH_NODE_IDENTITY_NOT_SUPPORTED + +/*!< Supported features */ +#define ESP_BLE_MESH_FEATURE_RELAY BLE_MESH_FEAT_RELAY +#define ESP_BLE_MESH_FEATURE_PROXY BLE_MESH_FEAT_PROXY +#define ESP_BLE_MESH_FEATURE_FRIEND BLE_MESH_FEAT_FRIEND +#define ESP_BLE_MESH_FEATURE_LOW_POWER BLE_MESH_FEAT_LOW_POWER +#define ESP_BLE_MESH_FEATURE_ALL_SUPPORTED BLE_MESH_FEAT_SUPPORTED + +#define ESP_BLE_MESH_ADDR_IS_UNICAST(addr) BLE_MESH_ADDR_IS_UNICAST(addr) +#define ESP_BLE_MESH_ADDR_IS_GROUP(addr) BLE_MESH_ADDR_IS_GROUP(addr) +#define ESP_BLE_MESH_ADDR_IS_VIRTUAL(addr) BLE_MESH_ADDR_IS_VIRTUAL(addr) +#define ESP_BLE_MESH_ADDR_IS_RFU(addr) BLE_MESH_ADDR_IS_RFU(addr) + +#define ESP_BLE_MESH_INVALID_NODE_INDEX (-1) + +/*!< Foundation Models */ +#define ESP_BLE_MESH_MODEL_ID_CONFIG_SRV BLE_MESH_MODEL_ID_CFG_SRV +#define ESP_BLE_MESH_MODEL_ID_CONFIG_CLI BLE_MESH_MODEL_ID_CFG_CLI +#define ESP_BLE_MESH_MODEL_ID_HEALTH_SRV BLE_MESH_MODEL_ID_HEALTH_SRV +#define ESP_BLE_MESH_MODEL_ID_HEALTH_CLI BLE_MESH_MODEL_ID_HEALTH_CLI + +/*!< Models from the Mesh Model Specification */ +#define ESP_BLE_MESH_MODEL_ID_GEN_ONOFF_SRV BLE_MESH_MODEL_ID_GEN_ONOFF_SRV +#define ESP_BLE_MESH_MODEL_ID_GEN_ONOFF_CLI BLE_MESH_MODEL_ID_GEN_ONOFF_CLI +#define ESP_BLE_MESH_MODEL_ID_GEN_LEVEL_SRV BLE_MESH_MODEL_ID_GEN_LEVEL_SRV +#define ESP_BLE_MESH_MODEL_ID_GEN_LEVEL_CLI BLE_MESH_MODEL_ID_GEN_LEVEL_CLI +#define ESP_BLE_MESH_MODEL_ID_GEN_DEF_TRANS_TIME_SRV BLE_MESH_MODEL_ID_GEN_DEF_TRANS_TIME_SRV +#define ESP_BLE_MESH_MODEL_ID_GEN_DEF_TRANS_TIME_CLI BLE_MESH_MODEL_ID_GEN_DEF_TRANS_TIME_CLI +#define ESP_BLE_MESH_MODEL_ID_GEN_POWER_ONOFF_SRV BLE_MESH_MODEL_ID_GEN_POWER_ONOFF_SRV +#define ESP_BLE_MESH_MODEL_ID_GEN_POWER_ONOFF_SETUP_SRV BLE_MESH_MODEL_ID_GEN_POWER_ONOFF_SETUP_SRV +#define ESP_BLE_MESH_MODEL_ID_GEN_POWER_ONOFF_CLI BLE_MESH_MODEL_ID_GEN_POWER_ONOFF_CLI +#define ESP_BLE_MESH_MODEL_ID_GEN_POWER_LEVEL_SRV BLE_MESH_MODEL_ID_GEN_POWER_LEVEL_SRV +#define ESP_BLE_MESH_MODEL_ID_GEN_POWER_LEVEL_SETUP_SRV BLE_MESH_MODEL_ID_GEN_POWER_LEVEL_SETUP_SRV +#define ESP_BLE_MESH_MODEL_ID_GEN_POWER_LEVEL_CLI BLE_MESH_MODEL_ID_GEN_POWER_LEVEL_CLI +#define ESP_BLE_MESH_MODEL_ID_GEN_BATTERY_SRV BLE_MESH_MODEL_ID_GEN_BATTERY_SRV +#define ESP_BLE_MESH_MODEL_ID_GEN_BATTERY_CLI BLE_MESH_MODEL_ID_GEN_BATTERY_CLI +#define ESP_BLE_MESH_MODEL_ID_GEN_LOCATION_SRV BLE_MESH_MODEL_ID_GEN_LOCATION_SRV +#define ESP_BLE_MESH_MODEL_ID_GEN_LOCATION_SETUP_SRV BLE_MESH_MODEL_ID_GEN_LOCATION_SETUPSRV +#define ESP_BLE_MESH_MODEL_ID_GEN_LOCATION_CLI BLE_MESH_MODEL_ID_GEN_LOCATION_CLI +#define ESP_BLE_MESH_MODEL_ID_GEN_ADMIN_PROP_SRV BLE_MESH_MODEL_ID_GEN_ADMIN_PROP_SRV +#define ESP_BLE_MESH_MODEL_ID_GEN_MANUFACTURER_PROP_SRV BLE_MESH_MODEL_ID_GEN_MANUFACTURER_PROP_SRV +#define ESP_BLE_MESH_MODEL_ID_GEN_USER_PROP_SRV BLE_MESH_MODEL_ID_GEN_USER_PROP_SRV +#define ESP_BLE_MESH_MODEL_ID_GEN_CLIENT_PROP_SRV BLE_MESH_MODEL_ID_GEN_CLIENT_PROP_SRV +#define ESP_BLE_MESH_MODEL_ID_GEN_PROP_CLI BLE_MESH_MODEL_ID_GEN_PROP_CLI +#define ESP_BLE_MESH_MODEL_ID_SENSOR_SRV BLE_MESH_MODEL_ID_SENSOR_SRV +#define ESP_BLE_MESH_MODEL_ID_SENSOR_SETUP_SRV BLE_MESH_MODEL_ID_SENSOR_SETUP_SRV +#define ESP_BLE_MESH_MODEL_ID_SENSOR_CLI BLE_MESH_MODEL_ID_SENSOR_CLI +#define ESP_BLE_MESH_MODEL_ID_TIME_SRV BLE_MESH_MODEL_ID_TIME_SRV +#define ESP_BLE_MESH_MODEL_ID_TIME_SETUP_SRV BLE_MESH_MODEL_ID_TIME_SETUP_SRV +#define ESP_BLE_MESH_MODEL_ID_TIME_CLI BLE_MESH_MODEL_ID_TIME_CLI +#define ESP_BLE_MESH_MODEL_ID_SCENE_SRV BLE_MESH_MODEL_ID_SCENE_SRV +#define ESP_BLE_MESH_MODEL_ID_SCENE_SETUP_SRV BLE_MESH_MODEL_ID_SCENE_SETUP_SRV +#define ESP_BLE_MESH_MODEL_ID_SCENE_CLI BLE_MESH_MODEL_ID_SCENE_CLI +#define ESP_BLE_MESH_MODEL_ID_SCHEDULER_SRV BLE_MESH_MODEL_ID_SCHEDULER_SRV +#define ESP_BLE_MESH_MODEL_ID_SCHEDULER_SETUP_SRV BLE_MESH_MODEL_ID_SCHEDULER_SETUP_SRV +#define ESP_BLE_MESH_MODEL_ID_SCHEDULER_CLI BLE_MESH_MODEL_ID_SCHEDULER_CLI +#define ESP_BLE_MESH_MODEL_ID_LIGHT_LIGHTNESS_SRV BLE_MESH_MODEL_ID_LIGHT_LIGHTNESS_SRV +#define ESP_BLE_MESH_MODEL_ID_LIGHT_LIGHTNESS_SETUP_SRV BLE_MESH_MODEL_ID_LIGHT_LIGHTNESS_SETUP_SRV +#define ESP_BLE_MESH_MODEL_ID_LIGHT_LIGHTNESS_CLI BLE_MESH_MODEL_ID_LIGHT_LIGHTNESS_CLI +#define ESP_BLE_MESH_MODEL_ID_LIGHT_CTL_SRV BLE_MESH_MODEL_ID_LIGHT_CTL_SRV +#define ESP_BLE_MESH_MODEL_ID_LIGHT_CTL_SETUP_SRV BLE_MESH_MODEL_ID_LIGHT_CTL_SETUP_SRV +#define ESP_BLE_MESH_MODEL_ID_LIGHT_CTL_CLI BLE_MESH_MODEL_ID_LIGHT_CTL_CLI +#define ESP_BLE_MESH_MODEL_ID_LIGHT_CTL_TEMP_SRV BLE_MESH_MODEL_ID_LIGHT_CTL_TEMP_SRV +#define ESP_BLE_MESH_MODEL_ID_LIGHT_HSL_SRV BLE_MESH_MODEL_ID_LIGHT_HSL_SRV +#define ESP_BLE_MESH_MODEL_ID_LIGHT_HSL_SETUP_SRV BLE_MESH_MODEL_ID_LIGHT_HSL_SETUP_SRV +#define ESP_BLE_MESH_MODEL_ID_LIGHT_HSL_CLI BLE_MESH_MODEL_ID_LIGHT_HSL_CLI +#define ESP_BLE_MESH_MODEL_ID_LIGHT_HSL_HUE_SRV BLE_MESH_MODEL_ID_LIGHT_HSL_HUE_SRV +#define ESP_BLE_MESH_MODEL_ID_LIGHT_HSL_SAT_SRV BLE_MESH_MODEL_ID_LIGHT_HSL_SAT_SRV +#define ESP_BLE_MESH_MODEL_ID_LIGHT_XYL_SRV BLE_MESH_MODEL_ID_LIGHT_XYL_SRV +#define ESP_BLE_MESH_MODEL_ID_LIGHT_XYL_SETUP_SRV BLE_MESH_MODEL_ID_LIGHT_XYL_SETUP_SRV +#define ESP_BLE_MESH_MODEL_ID_LIGHT_XYL_CLI BLE_MESH_MODEL_ID_LIGHT_XYL_CLI +#define ESP_BLE_MESH_MODEL_ID_LIGHT_LC_SRV BLE_MESH_MODEL_ID_LIGHT_LC_SRV +#define ESP_BLE_MESH_MODEL_ID_LIGHT_LC_SETUP_SRV BLE_MESH_MODEL_ID_LIGHT_LC_SETUPSRV +#define ESP_BLE_MESH_MODEL_ID_LIGHT_LC_CLI BLE_MESH_MODEL_ID_LIGHT_LC_CLI + +/*!< The following opcodes will only be used in the esp_ble_mesh_config_client_get_state function. */ +typedef uint32_t esp_ble_mesh_opcode_config_client_get_t; /*!< esp_ble_mesh_opcode_config_client_get_t belongs to esp_ble_mesh_opcode_t, + this typedef is only used to locate the opcodes used by esp_ble_mesh_config_client_get_state */ +#define ESP_BLE_MESH_MODEL_OP_BEACON_GET OP_BEACON_GET /*!< To determine the Secure Network Beacon state of a Configuration Server */ +#define ESP_BLE_MESH_MODEL_OP_COMPOSITION_DATA_GET OP_DEV_COMP_DATA_GET /*!< To determine the Composition Data state of a Configuration Server, a Configuration + Client shall send a Config Composition Data Get message with the Page field value set + to 0xFF. The response is a Config Composition Data Status message that contains the last + page of the Composition Data state. If the Page field of the Config Composition Data Status + message contains a non-zero value, then the Configuration Client shall send another Composition + Data Get message with the Page field value set to one less than the Page field value of the + Config Composition Data Status message. */ +#define ESP_BLE_MESH_MODEL_OP_DEFAULT_TTL_GET OP_DEFAULT_TTL_GET /*!< To determine the Default TTL state of a Configuration Server */ +#define ESP_BLE_MESH_MODEL_OP_GATT_PROXY_GET OP_GATT_PROXY_GET /*!< To determine the GATT Proxy state of a Configuration Server */ +#define ESP_BLE_MESH_MODEL_OP_RELAY_GET OP_RELAY_GET /*!< To determine the Relay and Relay Retransmit states of a Configuration Server */ +#define ESP_BLE_MESH_MODEL_OP_MODEL_PUB_GET OP_MOD_PUB_GET /*!< To determine the Publish Address, Publish AppKey Index, CredentialFlag, + Publish Period, Publish Retransmit Count, Publish Retransmit Interval Steps, + and Publish TTL states of a particular Model within the element */ +#define ESP_BLE_MESH_MODEL_OP_FRIEND_GET OP_FRIEND_GET /*!< To determine the Friend state of a Configuration Server */ +#define ESP_BLE_MESH_MODEL_OP_HEARTBEAT_PUB_GET OP_HEARTBEAT_PUB_GET /*!< To determine the Heartbeat Subscription Source, Heartbeat Subscription Destination, + Heartbeat Subscription Count Log, Heartbeat Subscription Period Log, Heartbeat + Subscription Min Hops, and Heartbeat Subscription Max Hops states of a node */ +#define ESP_BLE_MESH_MODEL_OP_HEARTBEAT_SUB_GET OP_HEARTBEAT_SUB_GET /*!< To determine the Heartbeat Subscription Source, Heartbeat Subscription Destination, + Heartbeat Subscription Count Log, Heartbeat Subscription Period Log, Heartbeat + Subscription Min Hops, and Heartbeat Subscription Max Hops states of a node */ +#define ESP_BLE_MESH_MODEL_OP_NET_KEY_GET OP_NET_KEY_GET /*!< To determine all NetKeys known to the node */ +#define ESP_BLE_MESH_MODEL_OP_APP_KEY_GET OP_APP_KEY_GET /*!< To determine all AppKeys bound to the NetKey */ +#define ESP_BLE_MESH_MODEL_OP_NODE_IDENTITY_GET OP_NODE_IDENTITY_GET /*!< To get the current Node Identity state for a subnet */ +#define ESP_BLE_MESH_MODEL_OP_SIG_MODEL_SUB_GET OP_MOD_SUB_GET /*!< To get the list of subscription addresses of a model within the element */ +#define ESP_BLE_MESH_MODEL_OP_VENDOR_MODEL_SUB_GET OP_MOD_SUB_GET_VND /*!< To get the list of subscription addresses of a model within the element */ +#define ESP_BLE_MESH_MODEL_OP_SIG_MODEL_APP_GET OP_SIG_MOD_APP_GET /*!< To request report of all AppKeys bound to the SIG Model */ +#define ESP_BLE_MESH_MODEL_OP_VENDOR_MODEL_APP_GET OP_VND_MOD_APP_GET /*!< To request report of all AppKeys bound to the Vendor Model */ +#define ESP_BLE_MESH_MODEL_OP_KEY_REFRESH_PHASE_GET OP_KRP_GET /*!< To get the current Key Refresh Phase state of the identified network key */ +#define ESP_BLE_MESH_MODEL_OP_LPN_POLLTIMEOUT_GET OP_LPN_TIMEOUT_GET /*!< To get the current value of PollTimeout timer of the Low Power node within a Friend node */ +#define ESP_BLE_MESH_MODEL_OP_NETWORK_TRANSMIT_GET OP_NET_TRANSMIT_GET /*!< To get the current Network Transmit state of a node */ + +/*!< The following opcodes will only be used in the esp_ble_mesh_config_client_set_state function. */ +typedef uint32_t esp_ble_mesh_opcode_config_client_set_t; /*!< esp_ble_mesh_opcode_config_client_set_t belongs to esp_ble_mesh_opcode_t, + this typedef is only used to locate the opcodes used by esp_ble_mesh_config_client_set_state */ +#define ESP_BLE_MESH_MODEL_OP_BEACON_SET OP_BEACON_SET /*!< Set the Secure Network Beacon state of a Configuration Server with acknowledgment */ +#define ESP_BLE_MESH_MODEL_OP_DEFAULT_TTL_SET OP_DEFAULT_TTL_SET /*!< Set the Default TTL state of a Configuration Server with acknowledgment */ +#define ESP_BLE_MESH_MODEL_OP_GATT_PROXY_SET OP_GATT_PROXY_SET /*!< Determine the GATT Proxy state of a Configuration Server */ +#define ESP_BLE_MESH_MODEL_OP_RELAY_SET OP_RELAY_SET /*!< Set the Relay and Relay Retransmit states of a Configuration Server with acknowledgment */ +#define ESP_BLE_MESH_MODEL_OP_MODEL_PUB_SET OP_MOD_PUB_SET /*!< Set the Publish Address, Publish AppKey Index, CredentialFlag, Publish + Period, Publish Retransmit Count, Publish Retransmit Interval Steps, and + Publish TTL states of a particular model within the element with acknowledgment */ +#define ESP_BLE_MESH_MODEL_OP_MODEL_SUB_ADD OP_MOD_SUB_ADD /*!< Add the address to the Subscription List state of a particular model + within the element with acknowledgment */ +#define ESP_BLE_MESH_MODEL_OP_MODEL_SUB_VIRTUAL_ADDR_ADD OP_MOD_SUB_VA_ADD /*!< Add the Label UUID to the Subscription List state of a particular model + within the element with acknowledgment */ +#define ESP_BLE_MESH_MODEL_OP_MODEL_SUB_DELETE OP_MOD_SUB_DEL /*!< Delete the address from the Subscription List state of a particular + model within the element with acknowledgment */ +#define ESP_BLE_MESH_MODEL_OP_MODEL_SUB_VIRTUAL_ADDR_DELETE OP_MOD_SUB_VA_DEL /*!< Delete the Label UUID from the Subscription List state of a particular + model within the element with acknowledgment */ +#define ESP_BLE_MESH_MODEL_OP_MODEL_SUB_OVERWRITE OP_MOD_SUB_OVERWRITE /*!< Clear the Subscription List and add the address to the Subscription List + state of a particular Model within the element with acknowledgment */ +#define ESP_BLE_MESH_MODEL_OP_MODEL_SUB_VIRTUAL_ADDR_OVERWRITE OP_MOD_SUB_VA_OVERWRITE /*!< Clear the Subscription List and add the Label UUID to the Subscription + List state of a particular model within the element with acknowledgment */ +#define ESP_BLE_MESH_MODEL_OP_NET_KEY_ADD OP_NET_KEY_ADD /*!< Add the NetKey identified by NetKeyIndex to the NetKey List state with acknowledgment */ +#define ESP_BLE_MESH_MODEL_OP_APP_KEY_ADD OP_APP_KEY_ADD /*!< Add the AppKey to the AppKey List and bind it to the NetKey identified + by the NetKeyIndex of a Configuration Server with acknowledgment */ +#define ESP_BLE_MESH_MODEL_OP_MODEL_APP_BIND OP_MOD_APP_BIND /*!< Bind the AppKey to a model of a particular element of a Configuration Server with acknowledgment */ +#define ESP_BLE_MESH_MODEL_OP_NODE_RESET OP_NODE_RESET /*!< Reset a node (other than a Provisioner) and remove it from the network */ +#define ESP_BLE_MESH_MODEL_OP_FRIEND_SET OP_FRIEND_SET /*!< Set the Friend state of a Configuration Server with acknowledgment */ +#define ESP_BLE_MESH_MODEL_OP_HEARTBEAT_PUB_SET OP_HEARTBEAT_PUB_SET /*!< Set the Heartbeat Publication Destination, Heartbeat Publication Count, + Heartbeat Publication Period, Heartbeat Publication TTL, Publication Features, + and Publication NetKey Index of a node with acknowledgment */ +#define ESP_BLE_MESH_MODEL_OP_HEARTBEAT_SUB_SET OP_HEARTBEAT_SUB_SET /*!< Determine the Heartbeat Subscription Source, Heartbeat Subscription Destination, + Heartbeat Subscription Count Log, Heartbeat Subscription Period Log, Heartbeat + Subscription Min Hops, and Heartbeat Subscription Max Hops states of a node */ +#define ESP_BLE_MESH_MODEL_OP_NET_KEY_UPDATE OP_NET_KEY_UPDATE /*!< To update a NetKey on a node */ +#define ESP_BLE_MESH_MODEL_OP_NET_KEY_DELETE OP_NET_KEY_DEL /*!< To delete a NetKey on a NetKey List from a node */ +#define ESP_BLE_MESH_MODEL_OP_APP_KEY_UPDATE OP_APP_KEY_UPDATE /*!< To update an AppKey value on the AppKey List on a node */ +#define ESP_BLE_MESH_MODEL_OP_APP_KEY_DELETE OP_APP_KEY_DEL /*!< To delete an AppKey from the AppKey List on a node */ +#define ESP_BLE_MESH_MODEL_OP_NODE_IDENTITY_SET OP_NODE_IDENTITY_SET /*!< To set the current Node Identity state for a subnet */ +#define ESP_BLE_MESH_MODEL_OP_KEY_REFRESH_PHASE_SET OP_KRP_SET /*!< To set the Key Refresh Phase state of the identified network key */ +#define ESP_BLE_MESH_MODEL_OP_MODEL_PUB_VIRTUAL_ADDR_SET OP_MOD_PUB_VA_SET /*!< To set the model Publication state of an outgoing message that originates from a model */ +#define ESP_BLE_MESH_MODEL_OP_MODEL_SUB_DELETE_ALL OP_MOD_SUB_DEL_ALL /*!< To discard the Subscription List of a model */ +#define ESP_BLE_MESH_MODEL_OP_MODEL_APP_UNBIND OP_MOD_APP_UNBIND /*!< To remove the binding between an AppKey and a model */ +#define ESP_BLE_MESH_MODEL_OP_NETWORK_TRANSMIT_SET OP_NET_TRANSMIT_SET /*!< To set the Network Transmit state of a node */ + +/*!< The following opcodes are used by the BLE Mesh Config Server Model internally to respond to the Config Client Model's request messages */ +typedef uint32_t esp_ble_mesh_config_model_status_t; /*!< esp_ble_mesh_config_model_status_t belongs to esp_ble_mesh_opcode_t, this typedef + is only used to locate the opcodes used by the Config Model messages */ +#define ESP_BLE_MESH_MODEL_OP_BEACON_STATUS OP_BEACON_STATUS +#define ESP_BLE_MESH_MODEL_OP_COMPOSITION_DATA_STATUS OP_DEV_COMP_DATA_STATUS +#define ESP_BLE_MESH_MODEL_OP_DEFAULT_TTL_STATUS OP_DEFAULT_TTL_STATUS +#define ESP_BLE_MESH_MODEL_OP_GATT_PROXY_STATUS OP_GATT_PROXY_STATUS +#define ESP_BLE_MESH_MODEL_OP_RELAY_STATUS OP_RELAY_STATUS +#define ESP_BLE_MESH_MODEL_OP_MODEL_PUB_STATUS OP_MOD_PUB_STATUS +#define ESP_BLE_MESH_MODEL_OP_MODEL_SUB_STATUS OP_MOD_SUB_STATUS +#define ESP_BLE_MESH_MODEL_OP_SIG_MODEL_SUB_LIST OP_MOD_SUB_LIST +#define ESP_BLE_MESH_MODEL_OP_VENDOR_MODEL_SUB_LIST OP_MOD_SUB_LIST_VND +#define ESP_BLE_MESH_MODEL_OP_NET_KEY_STATUS OP_NET_KEY_STATUS +#define ESP_BLE_MESH_MODEL_OP_NET_KEY_LIST OP_NET_KEY_LIST +#define ESP_BLE_MESH_MODEL_OP_APP_KEY_STATUS OP_APP_KEY_STATUS +#define ESP_BLE_MESH_MODEL_OP_APP_KEY_LIST OP_APP_KEY_LIST +#define ESP_BLE_MESH_MODEL_OP_NODE_IDENTITY_STATUS OP_NODE_IDENTITY_STATUS +#define ESP_BLE_MESH_MODEL_OP_MODEL_APP_STATUS OP_MOD_APP_STATUS +#define ESP_BLE_MESH_MODEL_OP_SIG_MODEL_APP_LIST OP_SIG_MOD_APP_LIST +#define ESP_BLE_MESH_MODEL_OP_VENDOR_MODEL_APP_LIST OP_VND_MOD_APP_LIST +#define ESP_BLE_MESH_MODEL_OP_NODE_RESET_STATUS OP_NODE_RESET_STATUS +#define ESP_BLE_MESH_MODEL_OP_FRIEND_STATUS OP_FRIEND_STATUS +#define ESP_BLE_MESH_MODEL_OP_KEY_REFRESH_PHASE_STATUS OP_KRP_STATUS +#define ESP_BLE_MESH_MODEL_OP_HEARTBEAT_PUB_STATUS OP_HEARTBEAT_PUB_STATUS +#define ESP_BLE_MESH_MODEL_OP_HEARTBEAT_SUB_STATUS OP_HEARTBEAT_SUB_STATUS +#define ESP_BLE_MESH_MODEL_OP_LPN_POLLTIMEOUT_STATUS OP_LPN_TIMEOUT_STATUS +#define ESP_BLE_MESH_MODEL_OP_NETWORK_TRANSMIT_STATUS OP_NET_TRANSMIT_STATUS + +/*!< The following opcodes will only be used in the esp_ble_mesh_health_client_get_state function. */ +typedef uint32_t esp_ble_mesh_opcode_health_client_get_t; /*!< esp_ble_mesh_opcode_health_client_get_t belongs to esp_ble_mesh_opcode_t, + this typedef is only used to locate the opcodes used by esp_ble_mesh_health_client_get_state */ +#define ESP_BLE_MESH_MODEL_OP_HEALTH_FAULT_GET OP_HEALTH_FAULT_GET /*!< Get the current Registered Fault state */ +#define ESP_BLE_MESH_MODEL_OP_HEALTH_PERIOD_GET OP_HEALTH_PERIOD_GET /*!< Get the current Health Period state */ +#define ESP_BLE_MESH_MODEL_OP_ATTENTION_GET OP_ATTENTION_GET /*!< Get the current Attention Timer state */ + +/*!< The following opcodes will only be used in the esp_ble_mesh_health_client_set_state function. */ +typedef uint32_t esp_ble_mesh_opcode_health_client_set_t; /*!< esp_ble_mesh_opcode_health_client_set_t belongs to esp_ble_mesh_opcode_t, + this typedef is only used to locate the opcodes used by esp_ble_mesh_health_client_set_state */ +#define ESP_BLE_MESH_MODEL_OP_HEALTH_FAULT_CLEAR OP_HEALTH_FAULT_CLEAR /*!< Clear Health Fault acknowledged */ +#define ESP_BLE_MESH_MODEL_OP_HEALTH_FAULT_CLEAR_UNACK OP_HEALTH_FAULT_CLEAR_UNREL /*!< Clear Health Fault Unacknowledged */ +#define ESP_BLE_MESH_MODEL_OP_HEALTH_FAULT_TEST OP_HEALTH_FAULT_TEST /*!< Invoke Health Fault Test acknowledged */ +#define ESP_BLE_MESH_MODEL_OP_HEALTH_FAULT_TEST_UNACK OP_HEALTH_FAULT_TEST_UNREL /*!< Invoke Health Fault Test unacknowledged */ +#define ESP_BLE_MESH_MODEL_OP_HEALTH_PERIOD_SET OP_HEALTH_PERIOD_SET /*!< Set Health Period acknowledged */ +#define ESP_BLE_MESH_MODEL_OP_HEALTH_PERIOD_SET_UNACK OP_HEALTH_PERIOD_SET_UNREL /*!< Set Health Period unacknowledged */ +#define ESP_BLE_MESH_MODEL_OP_ATTENTION_SET OP_ATTENTION_SET /*!< Set Health Attention acknowledged of the Health Server */ +#define ESP_BLE_MESH_MODEL_OP_ATTENTION_SET_UNACK OP_ATTENTION_SET_UNREL /*!< Set Health Attention Unacknowledged of the Health Server */ + +/*!< The following opcodes are used by the BLE Mesh Health Server Model internally to respond to the Health Client Model's request messages */ +typedef uint32_t esp_ble_mesh_health_model_status_t; /*!< esp_ble_mesh_health_model_status_t belongs to esp_ble_mesh_opcode_t, this typedef + is only used to locate the opcodes used by the Health Model messages */ +#define ESP_BLE_MESH_MODEL_OP_HEALTH_CURRENT_STATUS OP_HEALTH_CURRENT_STATUS +#define ESP_BLE_MESH_MODEL_OP_HEALTH_FAULT_STATUS OP_HEALTH_FAULT_STATUS +#define ESP_BLE_MESH_MODEL_OP_HEALTH_PERIOD_STATUS OP_HEALTH_PERIOD_STATUS +#define ESP_BLE_MESH_MODEL_OP_ATTENTION_STATUS OP_ATTENTION_STATUS + +typedef uint32_t esp_ble_mesh_generic_message_opcode_t; /*!< esp_ble_mesh_generic_message_opcode_t belongs to esp_ble_mesh_opcode_t, + this typedef is only used to locate the opcodes used by functions + esp_ble_mesh_generic_client_get_state & esp_ble_mesh_generic_client_set_state */ +/*!< Generic OnOff Message Opcode */ +#define ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_GET BLE_MESH_MODEL_OP_GEN_ONOFF_GET +#define ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET BLE_MESH_MODEL_OP_GEN_ONOFF_SET +#define ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK BLE_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK +#define ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_STATUS BLE_MESH_MODEL_OP_GEN_ONOFF_STATUS + +/*!< Generic Level Message Opcode */ +#define ESP_BLE_MESH_MODEL_OP_GEN_LEVEL_GET BLE_MESH_MODEL_OP_GEN_LEVEL_GET +#define ESP_BLE_MESH_MODEL_OP_GEN_LEVEL_SET BLE_MESH_MODEL_OP_GEN_LEVEL_SET +#define ESP_BLE_MESH_MODEL_OP_GEN_LEVEL_SET_UNACK BLE_MESH_MODEL_OP_GEN_LEVEL_SET_UNACK +#define ESP_BLE_MESH_MODEL_OP_GEN_LEVEL_STATUS BLE_MESH_MODEL_OP_GEN_LEVEL_STATUS +#define ESP_BLE_MESH_MODEL_OP_GEN_DELTA_SET BLE_MESH_MODEL_OP_GEN_DELTA_SET +#define ESP_BLE_MESH_MODEL_OP_GEN_DELTA_SET_UNACK BLE_MESH_MODEL_OP_GEN_DELTA_SET_UNACK +#define ESP_BLE_MESH_MODEL_OP_GEN_MOVE_SET BLE_MESH_MODEL_OP_GEN_MOVE_SET +#define ESP_BLE_MESH_MODEL_OP_GEN_MOVE_SET_UNACK BLE_MESH_MODEL_OP_GEN_MOVE_SET_UNACK + +/*!< Generic Default Transition Time Message Opcode */ +#define ESP_BLE_MESH_MODEL_OP_GEN_DEF_TRANS_TIME_GET BLE_MESH_MODEL_OP_GEN_DEF_TRANS_TIME_GET +#define ESP_BLE_MESH_MODEL_OP_GEN_DEF_TRANS_TIME_SET BLE_MESH_MODEL_OP_GEN_DEF_TRANS_TIME_SET +#define ESP_BLE_MESH_MODEL_OP_GEN_DEF_TRANS_TIME_SET_UNACK BLE_MESH_MODEL_OP_GEN_DEF_TRANS_TIME_SET_UNACK +#define ESP_BLE_MESH_MODEL_OP_GEN_DEF_TRANS_TIME_STATUS BLE_MESH_MODEL_OP_GEN_DEF_TRANS_TIME_STATUS + +/*!< Generic Power OnOff Message Opcode */ +#define ESP_BLE_MESH_MODEL_OP_GEN_ONPOWERUP_GET BLE_MESH_MODEL_OP_GEN_ONPOWERUP_GET +#define ESP_BLE_MESH_MODEL_OP_GEN_ONPOWERUP_STATUS BLE_MESH_MODEL_OP_GEN_ONPOWERUP_STATUS + +/*!< Generic Power OnOff Setup Message Opcode */ +#define ESP_BLE_MESH_MODEL_OP_GEN_ONPOWERUP_SET BLE_MESH_MODEL_OP_GEN_ONPOWERUP_SET +#define ESP_BLE_MESH_MODEL_OP_GEN_ONPOWERUP_SET_UNACK BLE_MESH_MODEL_OP_GEN_ONPOWERUP_SET_UNACK + +/*!< Generic Power Level Message Opcode */ +#define ESP_BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_GET BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_GET +#define ESP_BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_SET BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_SET +#define ESP_BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_SET_UNACK BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_SET_UNACK +#define ESP_BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_STATUS BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_STATUS +#define ESP_BLE_MESH_MODEL_OP_GEN_POWER_LAST_GET BLE_MESH_MODEL_OP_GEN_POWER_LAST_GET +#define ESP_BLE_MESH_MODEL_OP_GEN_POWER_LAST_STATUS BLE_MESH_MODEL_OP_GEN_POWER_LAST_STATUS +#define ESP_BLE_MESH_MODEL_OP_GEN_POWER_DEFAULT_GET BLE_MESH_MODEL_OP_GEN_POWER_DEFAULT_GET +#define ESP_BLE_MESH_MODEL_OP_GEN_POWER_DEFAULT_STATUS BLE_MESH_MODEL_OP_GEN_POWER_DEFAULT_STATUS +#define ESP_BLE_MESH_MODEL_OP_GEN_POWER_RANGE_GET BLE_MESH_MODEL_OP_GEN_POWER_RANGE_GET +#define ESP_BLE_MESH_MODEL_OP_GEN_POWER_RANGE_STATUS BLE_MESH_MODEL_OP_GEN_POWER_RANGE_STATUS + +/*!< Generic Power Level Setup Message Opcode */ +#define ESP_BLE_MESH_MODEL_OP_GEN_POWER_DEFAULT_SET BLE_MESH_MODEL_OP_GEN_POWER_DEFAULT_SET +#define ESP_BLE_MESH_MODEL_OP_GEN_POWER_DEFAULT_SET_UNACK BLE_MESH_MODEL_OP_GEN_POWER_DEFAULT_SET_UNACK +#define ESP_BLE_MESH_MODEL_OP_GEN_POWER_RANGE_SET BLE_MESH_MODEL_OP_GEN_POWER_RANGE_SET +#define ESP_BLE_MESH_MODEL_OP_GEN_POWER_RANGE_SET_UNACK BLE_MESH_MODEL_OP_GEN_POWER_RANGE_SET_UNACK + +/*!< Generic Battery Message Opcode */ +#define ESP_BLE_MESH_MODEL_OP_GEN_BATTERY_GET BLE_MESH_MODEL_OP_GEN_BATTERY_GET +#define ESP_BLE_MESH_MODEL_OP_GEN_BATTERY_STATUS BLE_MESH_MODEL_OP_GEN_BATTERY_STATUS + +/*!< Generic Location Message Opcode */ +#define ESP_BLE_MESH_MODEL_OP_GEN_LOC_GLOBAL_GET BLE_MESH_MODEL_OP_GEN_LOC_GLOBAL_GET +#define ESP_BLE_MESH_MODEL_OP_GEN_LOC_GLOBAL_STATUS BLE_MESH_MODEL_OP_GEN_LOC_GLOBAL_STATUS +#define ESP_BLE_MESH_MODEL_OP_GEN_LOC_LOCAL_GET BLE_MESH_MODEL_OP_GEN_LOC_LOCAL_GET +#define ESP_BLE_MESH_MODEL_OP_GEN_LOC_LOCAL_STATUS BLE_MESH_MODEL_OP_GEN_LOC_LOCAL_STATUS + +/*!< Generic Location Setup Message Opcode */ +#define ESP_BLE_MESH_MODEL_OP_GEN_LOC_GLOBAL_SET BLE_MESH_MODEL_OP_GEN_LOC_GLOBAL_SET +#define ESP_BLE_MESH_MODEL_OP_GEN_LOC_GLOBAL_SET_UNACK BLE_MESH_MODEL_OP_GEN_LOC_GLOBAL_SET_UNACK +#define ESP_BLE_MESH_MODEL_OP_GEN_LOC_LOCAL_SET BLE_MESH_MODEL_OP_GEN_LOC_LOCAL_SET +#define ESP_BLE_MESH_MODEL_OP_GEN_LOC_LOCAL_SET_UNACK BLE_MESH_MODEL_OP_GEN_LOC_LOCAL_SET_UNACK + +/*!< Generic Manufacturer Property Message Opcode */ +#define ESP_BLE_MESH_MODEL_OP_GEN_MANUFACTURER_PROPERTIES_GET BLE_MESH_MODEL_OP_GEN_MANU_PROPERTIES_GET +#define ESP_BLE_MESH_MODEL_OP_GEN_MANUFACTURER_PROPERTIES_STATUS BLE_MESH_MODEL_OP_GEN_MANU_PROPERTIES_STATUS +#define ESP_BLE_MESH_MODEL_OP_GEN_MANUFACTURER_PROPERTY_GET BLE_MESH_MODEL_OP_GEN_MANU_PROPERTY_GET +#define ESP_BLE_MESH_MODEL_OP_GEN_MANUFACTURER_PROPERTY_SET BLE_MESH_MODEL_OP_GEN_MANU_PROPERTY_SET +#define ESP_BLE_MESH_MODEL_OP_GEN_MANUFACTURER_PROPERTY_SET_UNACK BLE_MESH_MODEL_OP_GEN_MANU_PROPERTY_SET_UNACK +#define ESP_BLE_MESH_MODEL_OP_GEN_MANUFACTURER_PROPERTY_STATUS BLE_MESH_MODEL_OP_GEN_MANU_PROPERTY_STATUS + +/*!< Generic Admin Property Message Opcode */ +#define ESP_BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTIES_GET BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTIES_GET +#define ESP_BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTIES_STATUS BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTIES_STATUS +#define ESP_BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_GET BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_GET +#define ESP_BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_SET BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_SET +#define ESP_BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_SET_UNACK BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_SET_UNACK +#define ESP_BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_STATUS BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_STATUS + +/*!< Generic User Property Message Opcode */ +#define ESP_BLE_MESH_MODEL_OP_GEN_USER_PROPERTIES_GET BLE_MESH_MODEL_OP_GEN_USER_PROPERTIES_GET +#define ESP_BLE_MESH_MODEL_OP_GEN_USER_PROPERTIES_STATUS BLE_MESH_MODEL_OP_GEN_USER_PROPERTIES_STATUS +#define ESP_BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_GET BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_GET +#define ESP_BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_SET BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_SET +#define ESP_BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_SET_UNACK BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_SET_UNACK +#define ESP_BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_STATUS BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_STATUS + +/*!< Generic Client Property Message Opcode */ +#define ESP_BLE_MESH_MODEL_OP_GEN_CLIENT_PROPERTIES_GET BLE_MESH_MODEL_OP_GEN_CLIENT_PROPERTIES_GET +#define ESP_BLE_MESH_MODEL_OP_GEN_CLIENT_PROPERTIES_STATUS BLE_MESH_MODEL_OP_GEN_CLIENT_PROPERTIES_STATUS + +typedef uint32_t esp_ble_mesh_sensor_message_opcode_t; /*!< esp_ble_mesh_sensor_message_opcode_t belongs to esp_ble_mesh_opcode_t, + this typedef is only used to locate the opcodes used by functions + esp_ble_mesh_sensor_client_get_state & esp_ble_mesh_sensor_client_set_state */ +/*!< Sensor Message Opcode */ +#define ESP_BLE_MESH_MODEL_OP_SENSOR_DESCRIPTOR_GET BLE_MESH_MODEL_OP_SENSOR_DESCRIPTOR_GET +#define ESP_BLE_MESH_MODEL_OP_SENSOR_DESCRIPTOR_STATUS BLE_MESH_MODEL_OP_SENSOR_DESCRIPTOR_STATUS +#define ESP_BLE_MESH_MODEL_OP_SENSOR_GET BLE_MESH_MODEL_OP_SENSOR_GET +#define ESP_BLE_MESH_MODEL_OP_SENSOR_STATUS BLE_MESH_MODEL_OP_SENSOR_STATUS +#define ESP_BLE_MESH_MODEL_OP_SENSOR_COLUMN_GET BLE_MESH_MODEL_OP_SENSOR_COLUMN_GET +#define ESP_BLE_MESH_MODEL_OP_SENSOR_COLUMN_STATUS BLE_MESH_MODEL_OP_SENSOR_COLUMN_STATUS +#define ESP_BLE_MESH_MODEL_OP_SENSOR_SERIES_GET BLE_MESH_MODEL_OP_SENSOR_SERIES_GET +#define ESP_BLE_MESH_MODEL_OP_SENSOR_SERIES_STATUS BLE_MESH_MODEL_OP_SENSOR_SERIES_STATUS + +/*!< Sensor Setup Message Opcode */ +#define ESP_BLE_MESH_MODEL_OP_SENSOR_CADENCE_GET BLE_MESH_MODEL_OP_SENSOR_CADENCE_GET +#define ESP_BLE_MESH_MODEL_OP_SENSOR_CADENCE_SET BLE_MESH_MODEL_OP_SENSOR_CADENCE_SET +#define ESP_BLE_MESH_MODEL_OP_SENSOR_CADENCE_SET_UNACK BLE_MESH_MODEL_OP_SENSOR_CADENCE_SET_UNACK +#define ESP_BLE_MESH_MODEL_OP_SENSOR_CADENCE_STATUS BLE_MESH_MODEL_OP_SENSOR_CADENCE_STATUS +#define ESP_BLE_MESH_MODEL_OP_SENSOR_SETTINGS_GET BLE_MESH_MODEL_OP_SENSOR_SETTINGS_GET +#define ESP_BLE_MESH_MODEL_OP_SENSOR_SETTINGS_STATUS BLE_MESH_MODEL_OP_SENSOR_SETTINGS_STATUS +#define ESP_BLE_MESH_MODEL_OP_SENSOR_SETTING_GET BLE_MESH_MODEL_OP_SENSOR_SETTING_GET +#define ESP_BLE_MESH_MODEL_OP_SENSOR_SETTING_SET BLE_MESH_MODEL_OP_SENSOR_SETTING_SET +#define ESP_BLE_MESH_MODEL_OP_SENSOR_SETTING_SET_UNACK BLE_MESH_MODEL_OP_SENSOR_SETTING_SET_UNACK +#define ESP_BLE_MESH_MODEL_OP_SENSOR_SETTING_STATUS BLE_MESH_MODEL_OP_SENSOR_SETTING_STATUS + +typedef uint32_t esp_ble_mesh_time_scene_message_opcode_t; /*!< esp_ble_mesh_time_scene_message_opcode_t belongs to esp_ble_mesh_opcode_t, + this typedef is only used to locate the opcodes used by functions + esp_ble_mesh_time_scene_client_get_state & esp_ble_mesh_time_scene_client_set_state */ +/*!< Time Message Opcode */ +#define ESP_BLE_MESH_MODEL_OP_TIME_GET BLE_MESH_MODEL_OP_TIME_GET +#define ESP_BLE_MESH_MODEL_OP_TIME_SET BLE_MESH_MODEL_OP_TIME_SET +#define ESP_BLE_MESH_MODEL_OP_TIME_STATUS BLE_MESH_MODEL_OP_TIME_STATUS +#define ESP_BLE_MESH_MODEL_OP_TIME_ROLE_GET BLE_MESH_MODEL_OP_TIME_ROLE_GET +#define ESP_BLE_MESH_MODEL_OP_TIME_ROLE_SET BLE_MESH_MODEL_OP_TIME_ROLE_SET +#define ESP_BLE_MESH_MODEL_OP_TIME_ROLE_STATUS BLE_MESH_MODEL_OP_TIME_ROLE_STATUS +#define ESP_BLE_MESH_MODEL_OP_TIME_ZONE_GET BLE_MESH_MODEL_OP_TIME_ZONE_GET +#define ESP_BLE_MESH_MODEL_OP_TIME_ZONE_SET BLE_MESH_MODEL_OP_TIME_ZONE_SET +#define ESP_BLE_MESH_MODEL_OP_TIME_ZONE_STATUS BLE_MESH_MODEL_OP_TIME_ZONE_STATUS +#define ESP_BLE_MESH_MODEL_OP_TAI_UTC_DELTA_GET BLE_MESH_MODEL_OP_TAI_UTC_DELTA_GET +#define ESP_BLE_MESH_MODEL_OP_TAI_UTC_DELTA_SET BLE_MESH_MODEL_OP_TAI_UTC_DELTA_SET +#define ESP_BLE_MESH_MODEL_OP_TAI_UTC_DELTA_STATUS BLE_MESH_MODEL_OP_TAI_UTC_DELTA_STATUS + +/*!< Scene Message Opcode */ +#define ESP_BLE_MESH_MODEL_OP_SCENE_GET BLE_MESH_MODEL_OP_SCENE_GET +#define ESP_BLE_MESH_MODEL_OP_SCENE_RECALL BLE_MESH_MODEL_OP_SCENE_RECALL +#define ESP_BLE_MESH_MODEL_OP_SCENE_RECALL_UNACK BLE_MESH_MODEL_OP_SCENE_RECALL_UNACK +#define ESP_BLE_MESH_MODEL_OP_SCENE_STATUS BLE_MESH_MODEL_OP_SCENE_STATUS +#define ESP_BLE_MESH_MODEL_OP_SCENE_REGISTER_GET BLE_MESH_MODEL_OP_SCENE_REGISTER_GET +#define ESP_BLE_MESH_MODEL_OP_SCENE_REGISTER_STATUS BLE_MESH_MODEL_OP_SCENE_REGISTER_STATUS + +/*!< Scene Setup Message Opcode */ +#define ESP_BLE_MESH_MODEL_OP_SCENE_STORE BLE_MESH_MODEL_OP_SCENE_STORE +#define ESP_BLE_MESH_MODEL_OP_SCENE_STORE_UNACK BLE_MESH_MODEL_OP_SCENE_STORE_UNACK +#define ESP_BLE_MESH_MODEL_OP_SCENE_DELETE BLE_MESH_MODEL_OP_SCENE_DELETE +#define ESP_BLE_MESH_MODEL_OP_SCENE_DELETE_UNACK BLE_MESH_MODEL_OP_SCENE_DELETE_UNACK + +/*!< Scheduler Message Opcode */ +#define ESP_BLE_MESH_MODEL_OP_SCHEDULER_ACT_GET BLE_MESH_MODEL_OP_SCHEDULER_ACT_GET +#define ESP_BLE_MESH_MODEL_OP_SCHEDULER_ACT_STATUS BLE_MESH_MODEL_OP_SCHEDULER_ACT_STATUS +#define ESP_BLE_MESH_MODEL_OP_SCHEDULER_GET BLE_MESH_MODEL_OP_SCHEDULER_GET +#define ESP_BLE_MESH_MODEL_OP_SCHEDULER_STATUS BLE_MESH_MODEL_OP_SCHEDULER_STATUS + +/*!< Scheduler Setup Message Opcode */ +#define ESP_BLE_MESH_MODEL_OP_SCHEDULER_ACT_SET BLE_MESH_MODEL_OP_SCHEDULER_ACT_SET +#define ESP_BLE_MESH_MODEL_OP_SCHEDULER_ACT_SET_UNACK BLE_MESH_MODEL_OP_SCHEDULER_ACT_SET_UNACK + +typedef uint32_t esp_ble_mesh_light_message_opcode_t; /*!< esp_ble_mesh_light_message_opcode_t belongs to esp_ble_mesh_opcode_t, + this typedef is only used to locate the opcodes used by functions + esp_ble_mesh_light_client_get_state & esp_ble_mesh_light_client_set_state */ +/*!< Light Lightness Message Opcode */ +#define ESP_BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_GET BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_GET +#define ESP_BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_SET BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_SET +#define ESP_BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_SET_UNACK BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_SET_UNACK +#define ESP_BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_STATUS BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_STATUS +#define ESP_BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_GET BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_GET +#define ESP_BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_SET BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_SET +#define ESP_BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_SET_UNACK BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_SET_UNACK +#define ESP_BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_STATUS BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_STATUS +#define ESP_BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LAST_GET BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LAST_GET +#define ESP_BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LAST_STATUS BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LAST_STATUS +#define ESP_BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_DEFAULT_GET BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_DEFAULT_GET +#define ESP_BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_DEFAULT_STATUS BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_DEFAULT_STATUS +#define ESP_BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_RANGE_GET BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_RANGE_GET +#define ESP_BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_RANGE_STATUS BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_RANGE_STATUS + +/*!< Light Lightness Setup Message Opcode */ +#define ESP_BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_DEFAULT_SET BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_DEFAULT_SET +#define ESP_BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_DEFAULT_SET_UNACK BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_DEFAULT_SET_UNACK +#define ESP_BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_RANGE_SET BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_RANGE_SET +#define ESP_BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_RANGE_SET_UNACK BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_RANGE_SET_UNACK + +/*!< Light CTL Message Opcode */ +#define ESP_BLE_MESH_MODEL_OP_LIGHT_CTL_GET BLE_MESH_MODEL_OP_LIGHT_CTL_GET +#define ESP_BLE_MESH_MODEL_OP_LIGHT_CTL_SET BLE_MESH_MODEL_OP_LIGHT_CTL_SET +#define ESP_BLE_MESH_MODEL_OP_LIGHT_CTL_SET_UNACK BLE_MESH_MODEL_OP_LIGHT_CTL_SET_UNACK +#define ESP_BLE_MESH_MODEL_OP_LIGHT_CTL_STATUS BLE_MESH_MODEL_OP_LIGHT_CTL_STATUS +#define ESP_BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_GET BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_GET +#define ESP_BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_RANGE_GET BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_RANGE_GET +#define ESP_BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_RANGE_STATUS BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_RANGE_STATUS +#define ESP_BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_SET BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_SET +#define ESP_BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_SET_UNACK BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_SET_UNACK +#define ESP_BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_STATUS BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_STATUS +#define ESP_BLE_MESH_MODEL_OP_LIGHT_CTL_DEFAULT_GET BLE_MESH_MODEL_OP_LIGHT_CTL_DEFAULT_GET +#define ESP_BLE_MESH_MODEL_OP_LIGHT_CTL_DEFAULT_STATUS BLE_MESH_MODEL_OP_LIGHT_CTL_DEFAULT_STATUS + +/*!< Light CTL Setup Message Opcode */ +#define ESP_BLE_MESH_MODEL_OP_LIGHT_CTL_DEFAULT_SET BLE_MESH_MODEL_OP_LIGHT_CTL_DEFAULT_SET +#define ESP_BLE_MESH_MODEL_OP_LIGHT_CTL_DEFAULT_SET_UNACK BLE_MESH_MODEL_OP_LIGHT_CTL_DEFAULT_SET_UNACK +#define ESP_BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_RANGE_SET BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_RANGE_SET +#define ESP_BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_RANGE_SET_UNACK BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_RANGE_SET_UNACK + +/*!< Light HSL Message Opcode */ +#define ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_GET BLE_MESH_MODEL_OP_LIGHT_HSL_GET +#define ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_GET BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_GET +#define ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_SET BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_SET +#define ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_SET_UNACK BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_SET_UNACK +#define ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_STATUS BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_STATUS +#define ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_GET BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_GET +#define ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_SET BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_SET +#define ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_SET_UNACK BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_SET_UNACK +#define ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_STATUS BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_STATUS +#define ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_SET BLE_MESH_MODEL_OP_LIGHT_HSL_SET +#define ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_SET_UNACK BLE_MESH_MODEL_OP_LIGHT_HSL_SET_UNACK +#define ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_STATUS BLE_MESH_MODEL_OP_LIGHT_HSL_STATUS +#define ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_TARGET_GET BLE_MESH_MODEL_OP_LIGHT_HSL_TARGET_GET +#define ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_TARGET_STATUS BLE_MESH_MODEL_OP_LIGHT_HSL_TARGET_STATUS +#define ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_DEFAULT_GET BLE_MESH_MODEL_OP_LIGHT_HSL_DEFAULT_GET +#define ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_DEFAULT_STATUS BLE_MESH_MODEL_OP_LIGHT_HSL_DEFAULT_STATUS +#define ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_RANGE_GET BLE_MESH_MODEL_OP_LIGHT_HSL_RANGE_GET +#define ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_RANGE_STATUS BLE_MESH_MODEL_OP_LIGHT_HSL_RANGE_STATUS + +/*!< Light HSL Setup Message Opcode */ +#define ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_DEFAULT_SET BLE_MESH_MODEL_OP_LIGHT_HSL_DEFAULT_SET +#define ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_DEFAULT_SET_UNACK BLE_MESH_MODEL_OP_LIGHT_HSL_DEFAULT_SET_UNACK +#define ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_RANGE_SET BLE_MESH_MODEL_OP_LIGHT_HSL_RANGE_SET +#define ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_RANGE_SET_UNACK BLE_MESH_MODEL_OP_LIGHT_HSL_RANGE_SET_UNACK /* Model spec is wrong */ + +/*!< Light xyL Message Opcode */ +#define ESP_BLE_MESH_MODEL_OP_LIGHT_XYL_GET BLE_MESH_MODEL_OP_LIGHT_XYL_GET +#define ESP_BLE_MESH_MODEL_OP_LIGHT_XYL_SET BLE_MESH_MODEL_OP_LIGHT_XYL_SET +#define ESP_BLE_MESH_MODEL_OP_LIGHT_XYL_SET_UNACK BLE_MESH_MODEL_OP_LIGHT_XYL_SET_UNACK +#define ESP_BLE_MESH_MODEL_OP_LIGHT_XYL_STATUS BLE_MESH_MODEL_OP_LIGHT_XYL_STATUS +#define ESP_BLE_MESH_MODEL_OP_LIGHT_XYL_TARGET_GET BLE_MESH_MODEL_OP_LIGHT_XYL_TARGET_GET +#define ESP_BLE_MESH_MODEL_OP_LIGHT_XYL_TARGET_STATUS BLE_MESH_MODEL_OP_LIGHT_XYL_TARGET_STATUS +#define ESP_BLE_MESH_MODEL_OP_LIGHT_XYL_DEFAULT_GET BLE_MESH_MODEL_OP_LIGHT_XYL_DEFAULT_GET +#define ESP_BLE_MESH_MODEL_OP_LIGHT_XYL_DEFAULT_STATUS BLE_MESH_MODEL_OP_LIGHT_XYL_DEFAULT_STATUS +#define ESP_BLE_MESH_MODEL_OP_LIGHT_XYL_RANGE_GET BLE_MESH_MODEL_OP_LIGHT_XYL_RANGE_GET +#define ESP_BLE_MESH_MODEL_OP_LIGHT_XYL_RANGE_STATUS BLE_MESH_MODEL_OP_LIGHT_XYL_RANGE_STATUS + +/*!< Light xyL Setup Message Opcode */ +#define ESP_BLE_MESH_MODEL_OP_LIGHT_XYL_DEFAULT_SET BLE_MESH_MODEL_OP_LIGHT_XYL_DEFAULT_SET +#define ESP_BLE_MESH_MODEL_OP_LIGHT_XYL_DEFAULT_SET_UNACK BLE_MESH_MODEL_OP_LIGHT_XYL_DEFAULT_SET_UNACK +#define ESP_BLE_MESH_MODEL_OP_LIGHT_XYL_RANGE_SET BLE_MESH_MODEL_OP_LIGHT_XYL_RANGE_SET +#define ESP_BLE_MESH_MODEL_OP_LIGHT_XYL_RANGE_SET_UNACK BLE_MESH_MODEL_OP_LIGHT_XYL_RANGE_SET_UNACK + +/*!< Light Control Message Opcode */ +#define ESP_BLE_MESH_MODEL_OP_LIGHT_LC_MODE_GET BLE_MESH_MODEL_OP_LIGHT_LC_MODE_GET +#define ESP_BLE_MESH_MODEL_OP_LIGHT_LC_MODE_SET BLE_MESH_MODEL_OP_LIGHT_LC_MODE_SET +#define ESP_BLE_MESH_MODEL_OP_LIGHT_LC_MODE_SET_UNACK BLE_MESH_MODEL_OP_LIGHT_LC_MODE_SET_UNACK +#define ESP_BLE_MESH_MODEL_OP_LIGHT_LC_MODE_STATUS BLE_MESH_MODEL_OP_LIGHT_LC_MODE_STATUS +#define ESP_BLE_MESH_MODEL_OP_LIGHT_LC_OM_GET BLE_MESH_MODEL_OP_LIGHT_LC_OM_GET +#define ESP_BLE_MESH_MODEL_OP_LIGHT_LC_OM_SET BLE_MESH_MODEL_OP_LIGHT_LC_OM_SET +#define ESP_BLE_MESH_MODEL_OP_LIGHT_LC_OM_SET_UNACK BLE_MESH_MODEL_OP_LIGHT_LC_OM_SET_UNACK +#define ESP_BLE_MESH_MODEL_OP_LIGHT_LC_OM_STATUS BLE_MESH_MODEL_OP_LIGHT_LC_OM_STATUS +#define ESP_BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_GET BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_GET +#define ESP_BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_SET BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_SET +#define ESP_BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_SET_UNACK BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_SET_UNACK +#define ESP_BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_STATUS BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_STATUS +#define ESP_BLE_MESH_MODEL_OP_LIGHT_LC_PROPERTY_GET BLE_MESH_MODEL_OP_LIGHT_LC_PROPERTY_GET +#define ESP_BLE_MESH_MODEL_OP_LIGHT_LC_PROPERTY_SET BLE_MESH_MODEL_OP_LIGHT_LC_PROPERTY_SET +#define ESP_BLE_MESH_MODEL_OP_LIGHT_LC_PROPERTY_SET_UNACK BLE_MESH_MODEL_OP_LIGHT_LC_PROPERTY_SET_UNACK +#define ESP_BLE_MESH_MODEL_OP_LIGHT_LC_PROPERTY_STATUS BLE_MESH_MODEL_OP_LIGHT_LC_PROPERTY_STATUS + +typedef uint32_t esp_ble_mesh_opcode_t; +/*!< End of defines of esp_ble_mesh_opcode_t */ + +#define ESP_BLE_MESH_CFG_STATUS_SUCCESS STATUS_SUCCESS +#define ESP_BLE_MESH_CFG_STATUS_INVALID_ADDRESS STATUS_INVALID_ADDRESS +#define ESP_BLE_MESH_CFG_STATUS_INVALID_MODEL STATUS_INVALID_MODEL +#define ESP_BLE_MESH_CFG_STATUS_INVALID_APPKEY STATUS_INVALID_APPKEY +#define ESP_BLE_MESH_CFG_STATUS_INVALID_NETKEY STATUS_INVALID_NETKEY +#define ESP_BLE_MESH_CFG_STATUS_INSUFFICIENT_RESOURCES STATUS_INSUFF_RESOURCES +#define ESP_BLE_MESH_CFG_STATUS_KEY_INDEX_ALREADY_STORED STATUS_IDX_ALREADY_STORED +#define ESP_BLE_MESH_CFG_STATUS_INVALID_PUBLISH_PARAMETERS STATUS_NVAL_PUB_PARAM +#define ESP_BLE_MESH_CFG_STATUS_NOT_A_SUBSCRIBE_MODEL STATUS_NOT_SUB_MOD +#define ESP_BLE_MESH_CFG_STATUS_STORAGE_FAILURE STATUS_STORAGE_FAIL +#define ESP_BLE_MESH_CFG_STATUS_FEATURE_NOT_SUPPORTED STATUS_FEAT_NOT_SUPP +#define ESP_BLE_MESH_CFG_STATUS_CANNOT_UPDATE STATUS_CANNOT_UPDATE +#define ESP_BLE_MESH_CFG_STATUS_CANNOT_REMOVE STATUS_CANNOT_REMOVE +#define ESP_BLE_MESH_CFG_STATUS_CANNOT_BIND STATUS_CANNOT_BIND +#define ESP_BLE_MESH_CFG_STATUS_TEMP_UNABLE_TO_CHANGE_STATE STATUS_TEMP_STATE_CHG_FAIL +#define ESP_BLE_MESH_CFG_STATUS_CANNOT_SET STATUS_CANNOT_SET +#define ESP_BLE_MESH_CFG_STATUS_UNSPECIFIED_ERROR STATUS_UNSPECIFIED +#define ESP_BLE_MESH_CFG_STATUS_INVALID_BINDING STATUS_INVALID_BINDING +typedef uint8_t esp_ble_mesh_cfg_status_t; /*!< This typedef is only used to indicate the status code + contained in some of the Config Server Model status message */ + +#define ESP_BLE_MESH_MODEL_STATUS_SUCCESS 0x00 +#define ESP_BLE_MESH_MODEL_STATUS_CANNOT_SET_RANGE_MIN 0x01 +#define ESP_BLE_MESH_MODEL_STATUS_CANNOT_SET_RANGE_MAX 0x02 +typedef uint8_t esp_ble_mesh_model_status_t; /*!< This typedef is only used to indicate the status code contained in + some of the server model (e.g. Generic Server Model) status message */ + +/** @def ESP_BLE_MESH_TRANSMIT + * + * @brief Encode transmission count & interval steps. + * + * @note For example, ESP_BLE_MESH_TRANSMIT(2, 20) means that the message + * will be sent about 90ms(count is 3, step is 1, interval is 30 ms + * which includes 10ms of advertising interval random delay). + * + * @param count Number of retransmissions (first transmission is excluded). + * @param int_ms Interval steps in milliseconds. Must be greater than 0 + * and a multiple of 10. + * + * @return BLE Mesh transmit value that can be used e.g. for the default + * values of the Configuration Model data. + */ +#define ESP_BLE_MESH_TRANSMIT(count, int_ms) BLE_MESH_TRANSMIT(count, int_ms) + +/** @def ESP_BLE_MESH_GET_TRANSMIT_COUNT + * + * @brief Decode transmit count from a transmit value. + * + * @param transmit Encoded transmit count & interval value. + * + * @return Transmission count (actual transmissions equal to N + 1). + */ +#define ESP_BLE_MESH_GET_TRANSMIT_COUNT(transmit) BLE_MESH_TRANSMIT_COUNT(transmit) + +/** @def ESP_BLE_MESH_GET_TRANSMIT_INTERVAL + * + * @brief Decode transmit interval from a transmit value. + * + * @param transmit Encoded transmit count & interval value. + * + * @return Transmission interval in milliseconds. + */ +#define ESP_BLE_MESH_GET_TRANSMIT_INTERVAL(transmit) BLE_MESH_TRANSMIT_INT(transmit) + +/** @def ESP_BLE_MESH_PUBLISH_TRANSMIT + * + * @brief Encode Publish Retransmit count & interval steps. + * + * @param count Number of retransmissions (first transmission is excluded). + * @param int_ms Interval steps in milliseconds. Must be greater than 0 + * and a multiple of 50. + * + * @return BLE Mesh transmit value that can be used e.g. for the default + * values of the Configuration Model data. + */ +#define ESP_BLE_MESH_PUBLISH_TRANSMIT(count, int_ms) BLE_MESH_PUB_TRANSMIT(count, int_ms) + +/** @def ESP_BLE_MESH_GET_PUBLISH_TRANSMIT_COUNT + * + * @brief Decode Publish Retransmit count from a given value. + * + * @param transmit Encoded Publish Retransmit count & interval value. + * + * @return Retransmission count (actual transmissions equal to N + 1). + */ +#define ESP_BLE_MESH_GET_PUBLISH_TRANSMIT_COUNT(transmit) BLE_MESH_PUB_TRANSMIT_COUNT(transmit) + +/** @def ESP_BLE_MESH_GET_PUBLISH_TRANSMIT_INTERVAL + * + * @brief Decode Publish Retransmit interval from a given value. + * + * @param transmit Encoded Publish Retransmit count & interval value. + * + * @return Transmission interval in milliseconds. + */ +#define ESP_BLE_MESH_GET_PUBLISH_TRANSMIT_INTERVAL(transmit) BLE_MESH_PUB_TRANSMIT_INT(transmit) + +/* esp_ble_mesh_cb_t is not needed to be initialized by users (set with 0 and will be initialized internally) */ +typedef uint32_t esp_ble_mesh_cb_t; + +typedef enum { + ESP_BLE_MESH_TYPE_PROV_CB, + ESP_BLE_MESH_TYPE_OUTPUT_NUM_CB, + ESP_BLE_MESH_TYPE_OUTPUT_STR_CB, + ESP_BLE_MESH_TYPE_INTPUT_CB, + ESP_BLE_MESH_TYPE_LINK_OPEN_CB, + ESP_BLE_MESH_TYPE_LINK_CLOSE_CB, + ESP_BLE_MESH_TYPE_COMPLETE_CB, + ESP_BLE_MESH_TYPE_RESET_CB, +} esp_ble_mesh_cb_type_t; + +/*!< This enum value is provisioning authentication oob method */ +typedef enum { + ESP_BLE_MESH_NO_OOB, + ESP_BLE_MESH_STATIC_OOB, + ESP_BLE_MESH_OUTPUT_OOB, + ESP_BLE_MESH_INPUT_OOB, +} esp_ble_mesh_oob_method_t; + +/*!< This enum value is associated with bt_mesh_output_action_t in mesh_main.h */ +typedef enum { + ESP_BLE_MESH_NO_OUTPUT = 0, + ESP_BLE_MESH_BLINK = BIT(0), + ESP_BLE_MESH_BEEP = BIT(1), + ESP_BLE_MESH_VIBRATE = BIT(2), + ESP_BLE_MESH_DISPLAY_NUMBER = BIT(3), + ESP_BLE_MESH_DISPLAY_STRING = BIT(4), +} esp_ble_mesh_output_action_t; + +/*!< This enum value is associated with bt_mesh_input_action_t in mesh_main.h */ +typedef enum { + ESP_BLE_MESH_NO_INPUT = 0, + ESP_BLE_MESH_PUSH = BIT(0), + ESP_BLE_MESH_TWIST = BIT(1), + ESP_BLE_MESH_ENTER_NUMBER = BIT(2), + ESP_BLE_MESH_ENTER_STRING = BIT(3), +} esp_ble_mesh_input_action_t; + +/*!< This enum value is associated with bt_mesh_prov_bearer_t in mesh_main.h */ +typedef enum { + ESP_BLE_MESH_PROV_ADV = BIT(0), + ESP_BLE_MESH_PROV_GATT = BIT(1), +} esp_ble_mesh_prov_bearer_t; + +/*!< This enum value is associated with bt_mesh_prov_oob_info_t in mesh_main.h */ +typedef enum { + ESP_BLE_MESH_PROV_OOB_OTHER = BIT(0), + ESP_BLE_MESH_PROV_OOB_URI = BIT(1), + ESP_BLE_MESH_PROV_OOB_2D_CODE = BIT(2), + ESP_BLE_MESH_PROV_OOB_BAR_CODE = BIT(3), + ESP_BLE_MESH_PROV_OOB_NFC = BIT(4), + ESP_BLE_MESH_PROV_OOB_NUMBER = BIT(5), + ESP_BLE_MESH_PROV_OOB_STRING = BIT(6), + /* 7 - 10 are reserved */ + ESP_BLE_MESH_PROV_OOB_ON_BOX = BIT(11), + ESP_BLE_MESH_PROV_OOB_IN_BOX = BIT(12), + ESP_BLE_MESH_PROV_OOB_ON_PAPER = BIT(13), + ESP_BLE_MESH_PROV_OOB_IN_MANUAL = BIT(14), + ESP_BLE_MESH_PROV_OOB_ON_DEV = BIT(15), +} esp_ble_mesh_prov_oob_info_t; + +#define ESP_BLE_MESH_MODEL_OP_1(b0) BLE_MESH_MODEL_OP_1(b0) +#define ESP_BLE_MESH_MODEL_OP_2(b0, b1) BLE_MESH_MODEL_OP_2(b0, b1) +#define ESP_BLE_MESH_MODEL_OP_3(b0, cid) BLE_MESH_MODEL_OP_3(b0, cid) + +/*!< This macro is associated with BLE_MESH_MODEL in mesh_access.h */ +#define ESP_BLE_MESH_SIG_MODEL(_id, _op, _pub, _user_data) \ +{ \ + .model_id = (_id), \ + .op = _op, \ + .keys = { [0 ... (CONFIG_BLE_MESH_MODEL_KEY_COUNT - 1)] = \ + ESP_BLE_MESH_KEY_UNUSED }, \ + .pub = _pub, \ + .groups = { [0 ... (CONFIG_BLE_MESH_MODEL_GROUP_COUNT - 1)] = \ + ESP_BLE_MESH_ADDR_UNASSIGNED }, \ + .user_data = _user_data, \ +} + +/*!< This macro is associated with BLE_MESH_MODEL_VND in mesh_access.h */ +#define ESP_BLE_MESH_VENDOR_MODEL(_company, _id, _op, _pub, _user_data) \ +{ \ + .vnd.company_id = (_company), \ + .vnd.model_id = (_id), \ + .op = _op, \ + .pub = _pub, \ + .keys = { [0 ... (CONFIG_BLE_MESH_MODEL_KEY_COUNT - 1)] = \ + ESP_BLE_MESH_KEY_UNUSED }, \ + .groups = { [0 ... (CONFIG_BLE_MESH_MODEL_GROUP_COUNT - 1)] = \ + ESP_BLE_MESH_ADDR_UNASSIGNED }, \ + .user_data = _user_data, \ +} + +/** @brief Helper to define a BLE Mesh element within an array. + * + * In case the element has no SIG or Vendor models, the helper + * macro ESP_BLE_MESH_MODEL_NONE can be given instead. + * + * @note This macro is associated with BLE_MESH_ELEM in mesh_access.h + * + * @param _loc Location Descriptor. + * @param _mods Array of SIG models. + * @param _vnd_mods Array of vendor models. + */ +#define ESP_BLE_MESH_ELEMENT(_loc, _mods, _vnd_mods) \ +{ \ + .location = (_loc), \ + .sig_model_count = ARRAY_SIZE(_mods), \ + .sig_models = (_mods), \ + .vnd_model_count = ARRAY_SIZE(_vnd_mods), \ + .vnd_models = (_vnd_mods), \ +} + +#define ESP_BLE_MESH_PROV(uuid, sta_val, sta_val_len, out_size, out_act, in_size, in_act) { \ + .uuid = uuid, \ + .static_val = sta_val, \ + .static_val_len = sta_val_len, \ + .output_size = out_size, \ + .output_action = out_act, \ + .input_size = in_size, \ + .input_action = in_act, \ +} + +typedef struct esp_ble_mesh_model esp_ble_mesh_model_t; + +/*!< Abstraction that describes a BLE Mesh Element. + This structure is associated with bt_mesh_elem in mesh_access.h */ +typedef struct { + /* Element Address, assigned during provisioning. */ + uint16_t element_addr; + + /* Location Descriptor (GATT Bluetooth Namespace Descriptors) */ + const uint16_t location; + + /* Model count */ + const uint8_t sig_model_count; + const uint8_t vnd_model_count; + + /* Models */ + esp_ble_mesh_model_t *sig_models; + esp_ble_mesh_model_t *vnd_models; +} esp_ble_mesh_elem_t; + +/*!< Model publication context. + This structure is associated with bt_mesh_model_pub in mesh_access.h */ +typedef struct { + /** The model to which the context belongs. Initialized by the stack. */ + esp_ble_mesh_model_t *model; + + uint16_t publish_addr; /**< Publish Address. */ + uint16_t app_idx; /**< Publish AppKey Index. */ + + uint8_t ttl; /**< Publish Time to Live. */ + uint8_t retransmit; /**< Retransmit Count & Interval Steps. */ + + uint8_t period; /*!< Publish Period. */ + uint16_t period_div: 4, /*!< Divisor for the Period. */ + cred: 1, /*!< Friendship Credentials Flag. */ + fast_period: 1, /**< Use FastPeriodDivisor */ + count: 3; /*!< Retransmissions left. */ + + uint32_t period_start; /**< Start of the current period. */ + + /** @brief Publication buffer, containing the publication message. + * + * This will get correctly created when the publication context + * has been defined using the ESP_BLE_MESH_MODEL_PUB_DEFINE macro. + * + * ESP_BLE_MESH_MODEL_PUB_DEFINE(name, size); + */ + struct net_buf_simple *msg; + + /* The callback is only used for the BLE Mesh stack, not for the app layer. */ + esp_ble_mesh_cb_t update; + + /* Role of the device that is going to publish messages */ + uint8_t dev_role; + + /** Publish Period Timer. Only for stack-internal use. */ + struct k_delayed_work timer; +} esp_ble_mesh_model_pub_t; + +/** @def ESP_BLE_MESH_MODEL_PUB_DEFINE + * + * Define a model publication context. + * + * @param _name Variable name given to the context. + * @param _msg_len Length of the publication message. + * @param _role Role of the device which contains the model. + */ +#define ESP_BLE_MESH_MODEL_PUB_DEFINE(_name, _msg_len, _role) \ + NET_BUF_SIMPLE_DEFINE_STATIC(bt_mesh_pub_msg_##_name, _msg_len); \ + static esp_ble_mesh_model_pub_t _name = { \ + .update = (uint32_t)NULL, \ + .msg = &bt_mesh_pub_msg_##_name, \ + .dev_role = _role, \ + } + +/*!< Model operation context. + This structure is associated with bt_mesh_model_op in mesh_access.h */ +#define ESP_BLE_MESH_MODEL_OP(_opcode, _min_len) \ +{ \ + .opcode = _opcode, \ + .min_len = _min_len, \ + .param_cb = (uint32_t)NULL, \ +} + +typedef struct { + const uint32_t opcode; /* Opcode encoded with the ESP_BLE_MESH_MODEL_OP_* macro */ + const size_t min_len; /* Minimum required message length */ + esp_ble_mesh_cb_t param_cb; /* The callback is only used for BLE Mesh stack, not for the app layer. */ +} esp_ble_mesh_model_op_t; + +/** Define the terminator for the model operation table, each + * model operation struct array must use this terminator as + * the end tag of the operation unit. + */ +#define ESP_BLE_MESH_MODEL_OP_END {0, 0, 0} + +/** Abstraction that describes a Mesh Model instance. + * This structure is associated with bt_mesh_model in mesh_access.h + */ +struct esp_ble_mesh_model { + /* Model ID */ + union { + const uint16_t model_id; + struct { + uint16_t company_id; + uint16_t model_id; + } vnd; + }; + + /* Internal information, mainly for persistent storage */ + uint8_t element_idx; /* Belongs to Nth element */ + uint8_t model_idx; /* Is the Nth model in the element */ + uint16_t flags; /* Information about what has changed */ + + /* The Element to which this Model belongs */ + esp_ble_mesh_elem_t *element; + + /* Model Publication */ + esp_ble_mesh_model_pub_t *const pub; + + /* AppKey List */ + uint16_t keys[CONFIG_BLE_MESH_MODEL_KEY_COUNT]; + + /* Subscription List (group or virtual addresses) */ + uint16_t groups[CONFIG_BLE_MESH_MODEL_GROUP_COUNT]; + + /* Model operation context */ + esp_ble_mesh_model_op_t *op; + + /* Model-specific user data */ + void *user_data; +}; + +/** Helper to define an empty model array. + * This structure is associated with BLE_MESH_MODEL_NONE in mesh_access.h + */ +#define ESP_BLE_MESH_MODEL_NONE ((esp_ble_mesh_model_t []){}) + +/** Message sending context. + * This structure is associated with bt_mesh_msg_ctx in mesh_access.h + */ +typedef struct { + /** NetKey Index of the subnet through which to send the message. */ + uint16_t net_idx; + + /** AppKey Index for message encryption. */ + uint16_t app_idx; + + /** Remote address. */ + uint16_t addr; + + /** Destination address of a received message. Not used for sending. */ + uint16_t recv_dst; + + /** Received TTL value. Not used for sending. */ + uint8_t recv_ttl: 7; + + /** Force sending reliably by using segment acknowledgement */ + uint8_t send_rel: 1; + + /** TTL, or BLE_MESH_TTL_DEFAULT for default TTL. */ + uint8_t send_ttl; + + /** Opcode of a received message. Not used for sending message. */ + uint32_t recv_op; + + /** Model corresponding to the message, no need to be initialized before sending message */ + esp_ble_mesh_model_t *model; + + /** Indicate if the message is sent by a node server model, no need to be initialized before sending message */ + bool srv_send; +} esp_ble_mesh_msg_ctx_t; + +/** Provisioning properties & capabilities. + * This structure is associated with bt_mesh_prov in mesh_access.h + */ +typedef struct { +#if CONFIG_BLE_MESH_NODE + /** The UUID that is used when advertising as an unprovisioned device */ + const uint8_t *uuid; + + /** Optional URI. This will be advertised separately from the + * unprovisioned beacon, however the unprovisioned beacon will + * contain a hash of it so the two can be associated by the + * provisioner. + */ + const char *uri; + + /** Out of Band information field. */ + esp_ble_mesh_prov_oob_info_t oob_info; + + /** Flag indicates whether unprovisioned devices support OOB public key */ + bool oob_pub_key; + + /* This callback is only used for the BLE Mesh stack, not for the app layer */ + esp_ble_mesh_cb_t oob_pub_key_cb; + + /** Static OOB value */ + const uint8_t *static_val; + /** Static OOB value length */ + uint8_t static_val_len; + + /** Maximum size of Output OOB supported */ + uint8_t output_size; + /** Supported Output OOB Actions */ + uint16_t output_actions; + + /** Maximum size of Input OOB supported */ + uint8_t input_size; + /** Supported Input OOB Actions */ + uint16_t input_actions; + + /* These callbacks are only used for the BLE Mesh stack, not for the app layer */ + esp_ble_mesh_cb_t output_num_cb; + esp_ble_mesh_cb_t output_str_cb; + esp_ble_mesh_cb_t input_cb; + esp_ble_mesh_cb_t link_open_cb; + esp_ble_mesh_cb_t link_close_cb; + esp_ble_mesh_cb_t complete_cb; + esp_ble_mesh_cb_t reset_cb; +#endif /* CONFIG_BLE_MESH_NODE */ + +#ifdef CONFIG_BLE_MESH_PROVISIONER + /* Provisioner device UUID */ + const uint8_t *prov_uuid; + + /* Primary element address of the provisioner */ + const uint16_t prov_unicast_addr; + + /* Pre-incremental unicast address value to be assigned to the first device */ + uint16_t prov_start_address; + + /* Attention timer contained in Provisioning Invite PDU */ + uint8_t prov_attention; + + /* Provisioning Algorithm for the Provisioner */ + uint8_t prov_algorithm; + + /* Provisioner public key oob */ + uint8_t prov_pub_key_oob; + + /* The callback is only used for BLE Mesh stack, not for the app layer */ + esp_ble_mesh_cb_t provisioner_prov_read_oob_pub_key; + + /* Provisioner static oob value */ + uint8_t *prov_static_oob_val; + /* Provisioner static oob value length */ + uint8_t prov_static_oob_len; + + /* These callbacks are only used for BLE Mesh stack, not for the app layer */ + esp_ble_mesh_cb_t provisioner_prov_input; + esp_ble_mesh_cb_t provisioner_prov_output; + + /* Key refresh and IV update flag */ + uint8_t flags; + + /* IV index */ + uint32_t iv_index; + + /* These callbacks are only used for BLE Mesh stack, not for the app layer */ + esp_ble_mesh_cb_t provisioner_link_open; + esp_ble_mesh_cb_t provisioner_link_close; + esp_ble_mesh_cb_t provisioner_prov_comp; +#endif /* CONFIG_BLE_MESH_PROVISIONER */ +} esp_ble_mesh_prov_t; + +/** Node Composition + * This structure is associated with bt_mesh_comp in mesh_access.h + */ +typedef struct { + uint16_t cid; + uint16_t pid; + uint16_t vid; + + size_t element_count; + esp_ble_mesh_elem_t *elements; +} esp_ble_mesh_comp_t; + +typedef enum { + ROLE_NODE = 0, + ROLE_PROVISIONER, + ROLE_FAST_PROV, +} esp_ble_mesh_dev_role_t; + +typedef struct { + esp_ble_mesh_opcode_t opcode; /*!< Message opcode */ + esp_ble_mesh_model_t *model; /*!< Pointer to the client model structure */ + esp_ble_mesh_msg_ctx_t ctx; /*!< The context used to send message */ + int32_t msg_timeout; /*!< Timeout value (ms) to get response to the sent message */ + /*!< Note: if using default timeout value in menuconfig, make sure to set this value to 0 */ + uint8_t msg_role; /*!< Role of the device - Node/Provisioner, only used for tx */ +} esp_ble_mesh_client_common_param_t; + +typedef uint8_t esp_ble_mesh_dev_add_flag_t; +#define ADD_DEV_RM_AFTER_PROV_FLAG BIT(0) +#define ADD_DEV_START_PROV_NOW_FLAG BIT(1) +#define ADD_DEV_FLUSHABLE_DEV_FLAG BIT(2) +typedef struct { + esp_bd_addr_t addr; + esp_ble_addr_type_t addr_type; + uint8_t uuid[16]; + uint16_t oob_info; + /*!< ADD_DEV_START_PROV_NOW_FLAG shall not be set if the bearer has both PB-ADV and PB-GATT enabled */ + esp_ble_mesh_prov_bearer_t bearer; +} esp_ble_mesh_unprov_dev_add_t; + +#define DEL_DEV_ADDR_FLAG BIT(0) +#define DEL_DEV_UUID_FLAG BIT(1) +typedef struct { + union { + struct { + esp_bd_addr_t addr; + esp_ble_addr_type_t addr_type; + }; + uint8_t uuid[16]; + }; + uint8_t flag; /*!< BIT0: device address; BIT1: device UUID */ +} esp_ble_mesh_device_delete_t; + +#define PROV_DATA_NET_IDX_FLAG BIT(0) +#define PROV_DATA_FLAGS_FLAG BIT(1) +#define PROV_DATA_IV_INDEX_FLAG BIT(2) +typedef struct { + union { + uint16_t net_idx; + uint8_t flags; + uint32_t iv_index; + }; + uint8_t flag; /*!< BIT0: net_idx; BIT1: flags; BIT2: iv_index */ +} esp_ble_mesh_prov_data_info_t; + +typedef struct { + uint16_t unicast_min; /* Minimum unicast address used for fast provisioning */ + uint16_t unicast_max; /* Maximum unicast address used for fast provisioning */ + uint16_t net_idx; /* Netkey index used for fast provisioning */ + uint8_t flags; /* Flags used for fast provisioning */ + uint32_t iv_index; /* IV Index used for fast provisioning */ + uint8_t offset; /* Offset of the UUID to be compared */ + uint8_t match_len; /* Length of the UUID to be compared */ + uint8_t match_val[16]; /* Value of UUID to be compared */ +} esp_ble_mesh_fast_prov_info_t; + +typedef enum { + FAST_PROV_ACT_NONE, + FAST_PROV_ACT_ENTER, + FAST_PROV_ACT_SUSPEND, + FAST_PROV_ACT_EXIT, + FAST_PROV_ACT_MAX, +} esp_ble_mesh_fast_prov_action_t; + +typedef enum { + ESP_BLE_MESH_PROV_REGISTER_COMP_EVT, /*!< Initialize BLE Mesh provisioning capabilities and internal data information completion event */ + ESP_BLE_MESH_NODE_SET_UNPROV_DEV_NAME_COMP_EVT, /*!< Set the unprovisioned device name completion event */ + ESP_BLE_MESH_NODE_PROV_ENABLE_COMP_EVT, /*!< Enable node provisioning functionality completion event */ + ESP_BLE_MESH_NODE_PROV_DISABLE_COMP_EVT, /*!< Disable node provisioning functionality completion event */ + ESP_BLE_MESH_NODE_PROV_LINK_OPEN_EVT, /*!< Establish a BLE Mesh link event */ + ESP_BLE_MESH_NODE_PROV_LINK_CLOSE_EVT, /*!< Close a BLE Mesh link event */ + ESP_BLE_MESH_NODE_PROV_OOB_PUB_KEY_EVT, /*!< Generate Node input OOB public key event */ + ESP_BLE_MESH_NODE_PROV_OUTPUT_NUMBER_EVT, /*!< Generate Node Output Number event */ + ESP_BLE_MESH_NODE_PROV_OUTPUT_STRING_EVT, /*!< Generate Node Output String event */ + ESP_BLE_MESH_NODE_PROV_INPUT_EVT, /*!< Event requiring the user to input a number or string */ + ESP_BLE_MESH_NODE_PROV_COMPLETE_EVT, /*!< Provisioning done event */ + ESP_BLE_MESH_NODE_PROV_RESET_EVT, /*!< Provisioning reset event */ + ESP_BLE_MESH_NODE_PROV_SET_OOB_PUB_KEY_COMP_EVT, /*!< Node set oob public key completion event */ + ESP_BLE_MESH_NODE_PROV_INPUT_NUMBER_COMP_EVT, /*!< Node input number completion event */ + ESP_BLE_MESH_NODE_PROV_INPUT_STRING_COMP_EVT, /*!< Node input string completion event */ + ESP_BLE_MESH_NODE_PROXY_IDENTITY_ENABLE_COMP_EVT, /*!< Enable BLE Mesh Proxy Identity advertising completion event */ + ESP_BLE_MESH_NODE_PROXY_GATT_ENABLE_COMP_EVT, /*!< Enable BLE Mesh GATT Proxy Service completion event */ + ESP_BLE_MESH_NODE_PROXY_GATT_DISABLE_COMP_EVT, /*!< Disable BLE Mesh GATT Proxy Service completion event */ + ESP_BLE_MESH_PROVISIONER_PROV_ENABLE_COMP_EVT, /*!< Provisioner enable provisioning functionality completion event */ + ESP_BLE_MESH_PROVISIONER_PROV_DISABLE_COMP_EVT, /*!< Provisioner disable provisioning functionality completion event */ + ESP_BLE_MESH_PROVISIONER_RECV_UNPROV_ADV_PKT_EVT, /*!< Provisioner receives unprovisioned device beacon event */ + ESP_BLE_MESH_PROVISIONER_PROV_READ_OOB_PUB_KEY_EVT, /*!< Provisioner read unprovisioned device OOB public key event */ + ESP_BLE_MESH_PROVISIONER_PROV_INPUT_EVT, /*!< Provisioner input value for provisioning procedure event */ + ESP_BLE_MESH_PROVISIONER_PROV_OUTPUT_EVT, /*!< Provisioner output value for provisioning procedure event */ + ESP_BLE_MESH_PROVISIONER_PROV_LINK_OPEN_EVT, /*!< Provisioner establish a BLE Mesh link event */ + ESP_BLE_MESH_PROVISIONER_PROV_LINK_CLOSE_EVT, /*!< Provisioner close a BLE Mesh link event */ + ESP_BLE_MESH_PROVISIONER_PROV_COMPLETE_EVT, /*!< Provisioner provisioning done event */ + ESP_BLE_MESH_PROVISIONER_ADD_UNPROV_DEV_COMP_EVT, /*!< Provisioner add a device to the list which contains devices that are waiting/going to be provisioned completion event */ + ESP_BLE_MESH_PROVISIONER_DELETE_DEV_COMP_EVT, /*!< Provisioner delete a device from the list, close provisioning link with the device if it exists and remove the device from network completion event */ + ESP_BLE_MESH_PROVISIONER_SET_DEV_UUID_MATCH_COMP_EVT, /*!< Provisioner set the value to be compared with part of the unprovisioned device UUID completion event */ + ESP_BLE_MESH_PROVISIONER_SET_PROV_DATA_INFO_COMP_EVT, /*!< Provisioner set net_idx/flags/iv_index used for provisioning completion event */ + ESP_BLE_MESH_PROVISIONER_PROV_READ_OOB_PUB_KEY_COMP_EVT, /*!< Provisioner read unprovisioned device OOB public key completion event */ + ESP_BLE_MESH_PROVISIONER_PROV_INPUT_NUMBER_COMP_EVT, /*!< Provisioner input number completion event */ + ESP_BLE_MESH_PROVISIONER_PROV_INPUT_STRING_COMP_EVT, /*!< Provisioner input string completion event */ + ESP_BLE_MESH_PROVISIONER_SET_NODE_NAME_COMP_EVT, /*!< Provisioner set node name completion event */ + ESP_BLE_MESH_PROVISIONER_ADD_LOCAL_APP_KEY_COMP_EVT, /*!< Provisioner add local app key completion event */ + ESP_BLE_MESH_PROVISIONER_BIND_APP_KEY_TO_MODEL_COMP_EVT, /*!< Provisioner bind local model with local app key completion event */ + ESP_BLE_MESH_PROVISIONER_ADD_LOCAL_NET_KEY_COMP_EVT, /*!< Provisioner add local network key completion event */ + ESP_BLE_MESH_SET_FAST_PROV_INFO_COMP_EVT, /* !< Set fast provisioning information (e.g. unicast address range, net_idx, etc.) completion event */ + ESP_BLE_MESH_SET_FAST_PROV_ACTION_COMP_EVT, /* !< Set fast provisioning action completion event */ + ESP_BLE_MESH_PROV_EVT_MAX, +} esp_ble_mesh_prov_cb_event_t; + +typedef enum { + ESP_BLE_MESH_MODEL_OPERATION_EVT, /*!< User-defined models receive messages from peer devices (e.g. get, set, status, etc) event */ + ESP_BLE_MESH_MODEL_SEND_COMP_EVT, /*!< User-defined models send messages completion event */ + ESP_BLE_MESH_MODEL_PUBLISH_COMP_EVT, /*!< User-defined models publish messages completion event */ + ESP_BLE_MESH_CLIENT_MODEL_RECV_PUBLISH_MSG_EVT, /*!< User-defined client models receive publish messages event */ + ESP_BLE_MESH_CLIENT_MODEL_SEND_TIMEOUT_EVT, /*!< Timeout event for the user-defined client models that failed to receive response from peer server models */ + ESP_BLE_MESH_MODEL_PUBLISH_UPDATE_EVT, /*!< When a model is configured to publish messages periodically, this event will occur during every publish period */ + ESP_BLE_MESH_MODEL_EVT_MAX, +} esp_ble_mesh_model_cb_event_t; + +typedef union { + /** + * @brief ESP_BLE_MESH_PROV_REGISTER_COMP_EVT + */ + struct ble_mesh_prov_register_comp_param { + int err_code; + } prov_register_comp; + /** + * @brief ESP_BLE_MESH_NODE_SET_UNPROV_DEV_NAME_COMP_EVT + */ + struct ble_mesh_set_unprov_dev_name_comp_param { + int err_code; + } node_set_unprov_dev_name_comp; + /** + * @brief ESP_BLE_MESH_NODE_PROV_ENABLE_COMP_EVT + */ + struct ble_mesh_prov_enable_comp_param { + int err_code; + } node_prov_enable_comp; + /** + * @brief ESP_BLE_MESH_NODE_PROV_DISABLE_COMP_EVT + */ + struct ble_mesh_prov_disable_comp_param { + int err_code; + } node_prov_disable_comp; + /** + * @brief ESP_BLE_MESH_NODE_PROV_LINK_OPEN_EVT + */ + struct ble_mesh_link_open_evt_param { + esp_ble_mesh_prov_bearer_t bearer; + } node_prov_link_open; + /** + * @brief ESP_BLE_MESH_NODE_PROV_LINK_CLOSE_EVT + */ + struct ble_mesh_link_close_evt_param { + esp_ble_mesh_prov_bearer_t bearer; + } node_prov_link_close; + /** + * @brief ESP_BLE_MESH_NODE_PROV_OUTPUT_NUMBER_EVT + */ + struct ble_mesh_output_num_evt_param { + esp_ble_mesh_output_action_t action; + uint32_t number; + } node_prov_output_num; + /** + * @brief ESP_BLE_MESH_NODE_PROV_OUTPUT_STRING_EVT + */ + struct ble_mesh_output_str_evt_param { + char string[8]; + } node_prov_output_str; + /** + * @brief ESP_BLE_MESH_NODE_PROV_INPUT_EVT + */ + struct ble_mesh_input_evt_param { + esp_ble_mesh_input_action_t action; + uint8_t size; + } node_prov_input; + /** + * @brief ESP_BLE_MESH_NODE_PROV_COMPLETE_EVT + */ + struct ble_mesh_provision_complete_evt_param { + uint16_t net_idx; + uint16_t addr; + uint8_t flags; + uint32_t iv_index; + } node_prov_complete; + /** + * @brief ESP_BLE_MESH_NODE_PROV_RESET_EVT + */ + struct ble_mesh_provision_reset_param { + + } node_prov_reset; + /** + * @brief ESP_BLE_MESH_NODE_PROV_SET_OOB_PUB_KEY_COMP_EVT + */ + struct ble_mesh_set_oob_pub_key_comp_param { + int err_code; + } node_prov_set_oob_pub_key_comp; + /** + * @brief ESP_BLE_MESH_NODE_PROV_INPUT_NUM_COMP_EVT + */ + struct ble_mesh_input_number_comp_param { + int err_code; + } node_prov_input_num_comp; + /** + * @brief ESP_BLE_MESH_NODE_PROV_INPUT_STR_COMP_EVT + */ + struct ble_mesh_input_string_comp_param { + int err_code; + } node_prov_input_str_comp; + /** + * @brief ESP_BLE_MESH_NODE_PROXY_IDENTITY_ENABLE_COMP_EVT + */ + struct ble_mesh_proxy_identity_enable_comp_param { + int err_code; + } node_proxy_identity_enable_comp; + /** + * @brief ESP_BLE_MESH_NODE_PROXY_GATT_ENABLE_COMP_EVT + */ + struct ble_mesh_proxy_gatt_enable_comp_param { + int err_code; + } node_proxy_gatt_enable_comp; + /** + * @brief ESP_BLE_MESH_NODE_PROXY_GATT_DISABLE_COMP_EVT + */ + struct ble_mesh_proxy_gatt_disable_comp_param { + int err_code; + } node_proxy_gatt_disable_comp; + /** + * @brief ESP_BLE_MESH_PROVISIONER_RECV_UNPROV_ADV_PKT_EVT + */ + struct ble_mesh_provisioner_recv_unprov_adv_pkt_param { + uint8_t dev_uuid[16]; + uint8_t addr[6]; + esp_ble_addr_type_t addr_type; + uint16_t oob_info; + uint8_t adv_type; + esp_ble_mesh_prov_bearer_t bearer; + } provisioner_recv_unprov_adv_pkt; + /** + * @brief ESP_BLE_MESH_PROVISIONER_PROV_ENABLE_COMP_EVT + */ + struct ble_mesh_provisioner_prov_enable_comp_param { + int err_code; + } provisioner_prov_enable_comp; + /** + * @brief ESP_BLE_MESH_PROVISIONER_PROV_DISABLE_COMP_EVT + */ + struct ble_mesh_provisioner_prov_disable_comp_param { + int err_code; + } provisioner_prov_disable_comp; + /** + * @brief ESP_BLE_MESH_PROVISIONER_PROV_LINK_OPEN_EVT + */ + struct ble_mesh_provisioner_link_open_evt_param { + esp_ble_mesh_prov_bearer_t bearer; + } provisioner_prov_link_open; + /** + * @brief ESP_BLE_MESH_PROVISIONER_PROV_READ_OOB_PUB_KEY_EVT + */ + struct ble_mesh_provisioner_prov_read_oob_pub_key_evt_param { + uint8_t link_idx; + } provisioner_prov_read_oob_pub_key; + /** + * @brief ESP_BLE_MESH_PROVISIONER_PROV_INPUT_EVT + */ + struct ble_mesh_provisioner_prov_input_evt_param { + esp_ble_mesh_oob_method_t method; + esp_ble_mesh_output_action_t action; + uint8_t size; + uint8_t link_idx; + } provisioner_prov_input; + /** + * @brief ESP_BLE_MESH_PROVISIONER_PROV_OUTPUT_EVT + */ + struct ble_mesh_provisioner_prov_output_evt_param { + esp_ble_mesh_oob_method_t method; + esp_ble_mesh_input_action_t action; + uint8_t size; + uint8_t link_idx; + union { + char string[8]; + uint32_t number; + }; + } provisioner_prov_output; + /** + * @brief ESP_BLE_MESH_PROVISIONER_PROV_LINK_CLOSE_EVT + */ + struct ble_mesh_provisioner_link_close_evt_param { + esp_ble_mesh_prov_bearer_t bearer; + uint8_t reason; + } provisioner_prov_link_close; + /** + * @brief ESP_BLE_MESH_PROVISIONER_PROV_COMPLETE_EVT + */ + struct ble_mesh_provisioner_prov_comp_param { + int node_idx; + esp_ble_mesh_octet16_t device_uuid; + uint16_t unicast_addr; + uint8_t element_num; + uint16_t netkey_idx; + } provisioner_prov_complete; + /** + * @brief ESP_BLE_MESH_PROVISIONER_ADD_UNPROV_DEV_COMP_EVT + */ + struct ble_mesh_provisioner_add_unprov_dev_comp_param { + int err_code; + } provisioner_add_unprov_dev_comp; + /** + * @brief ESP_BLE_MESH_PROVISIONER_DELETE_DEV_COMP_EVT + */ + struct ble_mesh_provisioner_delete_dev_comp_param { + int err_code; + } provisioner_delete_dev_comp; + /** + * @brief ESP_BLE_MESH_PROVISIONER_SET_DEV_UUID_MATCH_COMP_EVT + */ + struct ble_mesh_provisioner_set_dev_uuid_match_comp_param { + int err_code; + } provisioner_set_dev_uuid_match_comp; + /** + * @brief ESP_BLE_MESH_PROVISIONER_SET_PROV_DATA_INFO_COMP_EVT + */ + struct ble_mesh_provisioner_set_prov_data_info_comp_param { + int err_code; + } provisioner_set_prov_data_info_comp; + /** + * @brief ESP_BLE_MESH_PROVISIONER_PROV_READ_OOB_PUB_KEY_COMP_EVT + */ + struct ble_mesh_provisioner_prov_read_oob_pub_key_comp_param { + int err_code; + } provisioner_prov_read_oob_pub_key_comp; + /** + * @brief ESP_BLE_MESH_PROVISIONER_PROV_INPUT_NUMBER_COMP_EVT + */ + struct ble_mesh_provisioner_prov_input_num_comp_param { + int err_code; + } provisioner_prov_input_num_comp; + /** + * @brief ESP_BLE_MESH_PROVISIONER_PROV_INPUT_STRING_COMP_EVT + */ + struct ble_mesh_provisioner_prov_input_str_comp_param { + int err_code; + } provisioner_prov_input_str_comp; + /** + * @brief ESP_BLE_MESH_PROVISIONER_SET_NODE_NAME_COMP_EVT + */ + struct ble_mesh_provisioner_set_node_name_comp_param { + int err_code; + int node_index; + } provisioner_set_node_name_comp; + /** + * @brief ESP_BLE_MESH_PROVISIONER_ADD_LOCAL_APP_KEY_COMP_EVT + */ + struct ble_mesh_provisioner_add_local_app_key_comp_param { + int err_code; + uint16_t app_idx; + } provisioner_add_app_key_comp; + /** + * @brief ESP_BLE_MESH_PROVISIONER_BIND_APP_KEY_TO_MODEL_COMP_EVT + */ + struct ble_mesh_provisioner_bind_local_mod_app_comp_param { + int err_code; + } provisioner_bind_app_key_to_model_comp; + /** + * @brief ESP_BLE_MESH_PROVISIONER_ADD_LOCAL_NET_KEY_COMP_EVT + */ + struct ble_mesh_provisioner_add_local_net_key_comp_param { + int err_code; + uint16_t net_idx; + } provisioner_add_net_key_comp; + struct ble_mesh_set_fast_prov_info_comp_param { + uint8_t status_unicast; + uint8_t status_net_idx; + uint8_t status_match; + } set_fast_prov_info_comp; + struct ble_mesh_set_fast_prov_action_comp_param { + uint8_t status_action; + } set_fast_prov_action_comp; +} esp_ble_mesh_prov_cb_param_t; + +typedef union { + /** + * @brief ESP_BLE_MESH_MODEL_OPERATION_EVT + */ + struct ble_mesh_model_operation_evt_param { + uint32_t opcode; + esp_ble_mesh_model_t *model; + esp_ble_mesh_msg_ctx_t *ctx; + uint16_t length; + uint8_t *msg; + } model_operation; + /** + * @brief ESP_BLE_MESH_MODEL_SEND_COMP_EVT + */ + struct ble_mesh_model_send_comp_param { + int err_code; + uint32_t opcode; + esp_ble_mesh_model_t *model; + esp_ble_mesh_msg_ctx_t *ctx; + } model_send_comp; + /** + * @brief ESP_BLE_MESH_MODEL_PUBLISH_COMP_EVT + */ + struct ble_mesh_model_publish_comp_param { + int err_code; + esp_ble_mesh_model_t *model; + } model_publish_comp; + /** + * @brief ESP_BLE_MESH_CLIENT_MODEL_RECV_PUBLISH_MSG_EVT + */ + struct ble_mesh_mod_recv_publish_msg_param { + uint32_t opcode; + esp_ble_mesh_model_t *model; + esp_ble_mesh_msg_ctx_t *ctx; + uint16_t length; + uint8_t *msg; + } client_recv_publish_msg; + /** + * @brief ESP_BLE_MESH_CLIENT_MODEL_SEND_TIMEOUT_EVT + */ + struct ble_mesh_client_model_send_timeout_param { + uint32_t opcode; + esp_ble_mesh_model_t *model; + esp_ble_mesh_msg_ctx_t *ctx; + } client_send_timeout; + /** + * @brief ESP_BLE_MESH_MODEL_PUBLISH_UPDATE_EVT + */ + struct ble_mesh_model_publish_update_evt_param { + esp_ble_mesh_model_t *model; + } model_publish_update; +} esp_ble_mesh_model_cb_param_t; + +typedef struct { + uint32_t cli_op; /*!< The client message opcode */ + uint32_t status_op; /*!< The server status opcode corresponding to the client message opcode */ +} esp_ble_mesh_client_op_pair_t; + +/*!< Mesh Client Model Context */ +typedef struct { + esp_ble_mesh_model_t *model; + int op_pair_size; /*!< Size of the op_pair */ + const esp_ble_mesh_client_op_pair_t *op_pair; /*!< Table containing get/set message opcode and corresponding status message opcode */ + uint32_t publish_status; /*!< This variable is reserved for BLE Mesh Stack, does not require initializing on the application layer */ + void *internal_data; /*!< Pointer to the structure of the client model internal data */ + uint8_t msg_role; /*!< Role of the device (Node/Provisioner) that is going to send messages */ +} esp_ble_mesh_client_t; + +#endif /* _ESP_BLE_MESH_DEFS_H_ */ diff --git a/components/bt/ble_mesh/api/models/esp_ble_mesh_config_model_api.c b/components/bt/ble_mesh/api/models/esp_ble_mesh_config_model_api.c new file mode 100644 index 0000000000..67694eef4e --- /dev/null +++ b/components/bt/ble_mesh/api/models/esp_ble_mesh_config_model_api.c @@ -0,0 +1,82 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 "btc/btc_task.h" +#include "btc/btc_manage.h" + +#include "esp_bt_defs.h" +#include "esp_bt_main.h" + +#include "btc_ble_mesh_config_model.h" +#include "esp_ble_mesh_config_model_api.h" + +esp_err_t esp_ble_mesh_register_config_client_callback(esp_ble_mesh_cfg_client_cb_t callback) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + return (btc_profile_cb_set(BTC_PID_CFG_CLIENT, callback) == 0 ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_mesh_register_config_server_callback(esp_ble_mesh_cfg_server_cb_t callback) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + return (btc_profile_cb_set(BTC_PID_CFG_SERVER, callback) == 0 ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_mesh_config_client_get_state(esp_ble_mesh_client_common_param_t *params, + esp_ble_mesh_cfg_client_get_state_t *get_state) +{ + btc_ble_mesh_cfg_client_args_t arg = {0}; + btc_msg_t msg = {0}; + + if (!params || !params->model || !params->ctx.addr || !get_state) { + return ESP_ERR_INVALID_ARG; + } + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_CFG_CLIENT; + msg.act = BTC_BLE_MESH_ACT_CONFIG_CLIENT_GET_STATE; + arg.cfg_client_get_state.params = params; + arg.cfg_client_get_state.get_state = get_state; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_cfg_client_args_t), btc_ble_mesh_cfg_client_arg_deep_copy) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_mesh_config_client_set_state(esp_ble_mesh_client_common_param_t *params, + esp_ble_mesh_cfg_client_set_state_t *set_state) +{ + btc_ble_mesh_cfg_client_args_t arg = {0}; + btc_msg_t msg = {0}; + + if (!params || !params->model || !params->ctx.addr || !set_state) { + return ESP_ERR_INVALID_ARG; + } + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_CFG_CLIENT; + msg.act = BTC_BLE_MESH_ACT_CONFIG_CLIENT_SET_STATE; + arg.cfg_client_set_state.params = params; + arg.cfg_client_set_state.set_state = set_state; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_cfg_client_args_t), btc_ble_mesh_cfg_client_arg_deep_copy) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} diff --git a/components/bt/ble_mesh/api/models/esp_ble_mesh_generic_model_api.c b/components/bt/ble_mesh/api/models/esp_ble_mesh_generic_model_api.c new file mode 100644 index 0000000000..82f6ff5850 --- /dev/null +++ b/components/bt/ble_mesh/api/models/esp_ble_mesh_generic_model_api.c @@ -0,0 +1,75 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 "btc/btc_task.h" +#include "btc/btc_manage.h" + +#include "esp_bt_defs.h" +#include "esp_bt_main.h" + +#include "btc_ble_mesh_generic_model.h" +#include "esp_ble_mesh_generic_model_api.h" + +esp_err_t esp_ble_mesh_register_generic_client_callback(esp_ble_mesh_generic_client_cb_t callback) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + return (btc_profile_cb_set(BTC_PID_GENERIC_CLIENT, callback) == 0 ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_mesh_generic_client_get_state(esp_ble_mesh_client_common_param_t *params, + esp_ble_mesh_generic_client_get_state_t *get_state) +{ + btc_ble_mesh_generic_client_args_t arg = {0}; + btc_msg_t msg = {0}; + + if (!params || !params->model || !params->ctx.addr || !get_state) { + return ESP_ERR_INVALID_ARG; + } + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GENERIC_CLIENT; + msg.act = BTC_BLE_MESH_ACT_GENERIC_CLIENT_GET_STATE; + arg.generic_client_get_state.params = params; + arg.generic_client_get_state.get_state = get_state; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_generic_client_args_t), btc_ble_mesh_generic_client_arg_deep_copy) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_mesh_generic_client_set_state(esp_ble_mesh_client_common_param_t *params, + esp_ble_mesh_generic_client_set_state_t *set_state) +{ + btc_ble_mesh_generic_client_args_t arg = {0}; + btc_msg_t msg = {0}; + + if (!params || !params->model || !params->ctx.addr || !set_state) { + return ESP_ERR_INVALID_ARG; + } + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_GENERIC_CLIENT; + msg.act = BTC_BLE_MESH_ACT_GENERIC_CLIENT_SET_STATE; + arg.generic_client_set_state.params = params; + arg.generic_client_set_state.set_state = set_state; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_generic_client_args_t), btc_ble_mesh_generic_client_arg_deep_copy) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} diff --git a/components/bt/ble_mesh/api/models/esp_ble_mesh_health_model_api.c b/components/bt/ble_mesh/api/models/esp_ble_mesh_health_model_api.c new file mode 100644 index 0000000000..e7df777dc0 --- /dev/null +++ b/components/bt/ble_mesh/api/models/esp_ble_mesh_health_model_api.c @@ -0,0 +1,98 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 "btc/btc_task.h" +#include "btc/btc_manage.h" + +#include "esp_bt_defs.h" +#include "esp_bt_main.h" + +#include "btc_ble_mesh_health_model.h" +#include "esp_ble_mesh_health_model_api.h" + +esp_err_t esp_ble_mesh_register_health_client_callback(esp_ble_mesh_health_client_cb_t callback) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + return (btc_profile_cb_set(BTC_PID_HEALTH_CLIENT, callback) == 0 ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_mesh_register_health_server_callback(esp_ble_mesh_health_server_cb_t callback) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + return (btc_profile_cb_set(BTC_PID_HEALTH_SERVER, callback) == 0 ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_mesh_health_client_get_state(esp_ble_mesh_client_common_param_t *params, + esp_ble_mesh_health_client_get_state_t *get_state) +{ + btc_ble_mesh_health_client_args_t arg = {0}; + btc_msg_t msg = {0}; + + if (!params || !params->model || !params->ctx.addr || !get_state) { + return ESP_ERR_INVALID_ARG; + } + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HEALTH_CLIENT; + msg.act = BTC_BLE_MESH_ACT_HEALTH_CLIENT_GET_STATE; + arg.health_client_get_state.params = params; + arg.health_client_get_state.get_state = get_state; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_health_client_args_t), btc_ble_mesh_health_client_arg_deep_copy) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_mesh_health_client_set_state(esp_ble_mesh_client_common_param_t *params, + esp_ble_mesh_health_client_set_state_t *set_state) +{ + btc_ble_mesh_health_client_args_t arg = {0}; + btc_msg_t msg = {0}; + + if (!params || !params->model || !params->ctx.addr || !set_state) { + return ESP_ERR_INVALID_ARG; + } + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HEALTH_CLIENT; + msg.act = BTC_BLE_MESH_ACT_HEALTH_CLIENT_SET_STATE; + arg.health_client_set_state.params = params; + arg.health_client_set_state.set_state = set_state; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_health_client_args_t), btc_ble_mesh_health_client_arg_deep_copy) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_mesh_health_server_fault_update(esp_ble_mesh_elem_t *element) +{ + btc_ble_mesh_health_server_args_t arg = {0}; + btc_msg_t msg = {0}; + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HEALTH_SERVER; + msg.act = BTC_BLE_MESH_ACT_HEALTH_SERVER_FAULT_UPDATE; + arg.fault_update.element = element; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_health_server_args_t), NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} diff --git a/components/bt/ble_mesh/api/models/esp_ble_mesh_lighting_model_api.c b/components/bt/ble_mesh/api/models/esp_ble_mesh_lighting_model_api.c new file mode 100644 index 0000000000..eea415d618 --- /dev/null +++ b/components/bt/ble_mesh/api/models/esp_ble_mesh_lighting_model_api.c @@ -0,0 +1,76 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 "btc/btc_task.h" +#include "btc/btc_manage.h" + +#include "esp_bt_defs.h" +#include "esp_bt_main.h" + +#include "btc_ble_mesh_lighting_model.h" +#include "esp_ble_mesh_lighting_model_api.h" + +esp_err_t esp_ble_mesh_register_light_client_callback(esp_ble_mesh_light_client_cb_t callback) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + return (btc_profile_cb_set(BTC_PID_LIGHT_CLIENT, callback) == 0 ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_mesh_light_client_get_state(esp_ble_mesh_client_common_param_t *params, + esp_ble_mesh_light_client_get_state_t *get_state) +{ + btc_ble_mesh_light_client_args_t arg = {0}; + btc_msg_t msg = {0}; + + if (!params || !params->model || !params->ctx.addr || !get_state) { + return ESP_ERR_INVALID_ARG; + } + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_LIGHT_CLIENT; + msg.act = BTC_BLE_MESH_ACT_LIGHT_CLIENT_GET_STATE; + arg.light_client_get_state.params = params; + arg.light_client_get_state.get_state = get_state; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_light_client_args_t), btc_ble_mesh_light_client_arg_deep_copy) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_mesh_light_client_set_state(esp_ble_mesh_client_common_param_t *params, + esp_ble_mesh_light_client_set_state_t *set_state) +{ + btc_ble_mesh_light_client_args_t arg = {0}; + btc_msg_t msg = {0}; + + if (!params || !params->model || !params->ctx.addr || !set_state) { + return ESP_ERR_INVALID_ARG; + } + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_LIGHT_CLIENT; + msg.act = BTC_BLE_MESH_ACT_LIGHT_CLIENT_SET_STATE; + arg.light_client_set_state.params = params; + arg.light_client_set_state.set_state = set_state; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_light_client_args_t), btc_ble_mesh_light_client_arg_deep_copy) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + diff --git a/components/bt/ble_mesh/api/models/esp_ble_mesh_sensor_model_api.c b/components/bt/ble_mesh/api/models/esp_ble_mesh_sensor_model_api.c new file mode 100644 index 0000000000..41072d227f --- /dev/null +++ b/components/bt/ble_mesh/api/models/esp_ble_mesh_sensor_model_api.c @@ -0,0 +1,76 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 "btc/btc_task.h" +#include "btc/btc_manage.h" + +#include "esp_bt_defs.h" +#include "esp_bt_main.h" + +#include "btc_ble_mesh_sensor_model.h" +#include "esp_ble_mesh_sensor_model_api.h" + +esp_err_t esp_ble_mesh_register_sensor_client_callback(esp_ble_mesh_sensor_client_cb_t callback) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + return (btc_profile_cb_set(BTC_PID_SENSOR_CLIENT, callback) == 0 ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_mesh_sensor_client_get_state(esp_ble_mesh_client_common_param_t *params, + esp_ble_mesh_sensor_client_get_state_t *get_state) +{ + btc_ble_mesh_sensor_client_args_t arg = {0}; + btc_msg_t msg = {0}; + + if (!params || !params->model || !params->ctx.addr || !get_state) { + return ESP_ERR_INVALID_ARG; + } + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_SENSOR_CLIENT; + msg.act = BTC_BLE_MESH_ACT_SENSOR_CLIENT_GET_STATE; + arg.sensor_client_get_state.params = params; + arg.sensor_client_get_state.get_state = get_state; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_sensor_client_args_t), btc_ble_mesh_sensor_client_arg_deep_copy) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_mesh_sensor_client_set_state(esp_ble_mesh_client_common_param_t *params, + esp_ble_mesh_sensor_client_set_state_t *set_state) +{ + btc_ble_mesh_sensor_client_args_t arg = {0}; + btc_msg_t msg = {0}; + + if (!params || !params->model || !params->ctx.addr || !set_state) { + return ESP_ERR_INVALID_ARG; + } + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_SENSOR_CLIENT; + msg.act = BTC_BLE_MESH_ACT_SENSOR_CLIENT_SET_STATE; + arg.sensor_client_set_state.params = params; + arg.sensor_client_set_state.set_state = set_state; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_sensor_client_args_t), btc_ble_mesh_sensor_client_arg_deep_copy) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + diff --git a/components/bt/ble_mesh/api/models/esp_ble_mesh_time_scene_model_api.c b/components/bt/ble_mesh/api/models/esp_ble_mesh_time_scene_model_api.c new file mode 100644 index 0000000000..13823b9787 --- /dev/null +++ b/components/bt/ble_mesh/api/models/esp_ble_mesh_time_scene_model_api.c @@ -0,0 +1,76 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 "btc/btc_task.h" +#include "btc/btc_manage.h" + +#include "esp_bt_defs.h" +#include "esp_bt_main.h" + +#include "btc_ble_mesh_time_scene_model.h" +#include "esp_ble_mesh_time_scene_model_api.h" + +esp_err_t esp_ble_mesh_register_time_scene_client_callback(esp_ble_mesh_time_scene_client_cb_t callback) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + return (btc_profile_cb_set(BTC_PID_TIME_SCENE_CLIENT, callback) == 0 ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_mesh_time_scene_client_get_state(esp_ble_mesh_client_common_param_t *params, + esp_ble_mesh_time_scene_client_get_state_t *get_state) +{ + btc_ble_mesh_time_scene_client_args_t arg = {0}; + btc_msg_t msg = {0}; + + if (!params || !params->model || !params->ctx.addr || !get_state) { + return ESP_ERR_INVALID_ARG; + } + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_TIME_SCENE_CLIENT; + msg.act = BTC_BLE_MESH_ACT_TIME_SCENE_CLIENT_GET_STATE; + arg.time_scene_client_get_state.params = params; + arg.time_scene_client_get_state.get_state = get_state; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_time_scene_client_args_t), btc_ble_mesh_time_scene_client_arg_deep_copy) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_mesh_time_scene_client_set_state(esp_ble_mesh_client_common_param_t *params, + esp_ble_mesh_time_scene_client_set_state_t *set_state) +{ + btc_ble_mesh_time_scene_client_args_t arg = {0}; + btc_msg_t msg = {0}; + + if (!params || !params->model || !params->ctx.addr || !set_state) { + return ESP_ERR_INVALID_ARG; + } + + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_TIME_SCENE_CLIENT; + msg.act = BTC_BLE_MESH_ACT_TIME_SCENE_CLIENT_SET_STATE; + arg.time_scene_client_set_state.params = params; + arg.time_scene_client_set_state.set_state = set_state; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_time_scene_client_args_t), btc_ble_mesh_time_scene_client_arg_deep_copy) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + diff --git a/components/bt/ble_mesh/api/models/include/esp_ble_mesh_config_model_api.h b/components/bt/ble_mesh/api/models/include/esp_ble_mesh_config_model_api.h new file mode 100644 index 0000000000..755e0671b2 --- /dev/null +++ b/components/bt/ble_mesh/api/models/include/esp_ble_mesh_config_model_api.h @@ -0,0 +1,670 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#ifndef _ESP_BLE_MESH_CONFIG_MODEL_API_H_ +#define _ESP_BLE_MESH_CONFIG_MODEL_API_H_ + +#include "esp_ble_mesh_defs.h" + +/** @def ESP_BLE_MESH_MODEL_CFG_SRV + * + * @brief Define a new Config Server Model. + * + * @note The Config Server Model can only be included by a Primary Element. + * + * @param srv_data Pointer to a unique Config Server Model user_data. + * + * @return New Config Server Model instance. + */ +#define ESP_BLE_MESH_MODEL_CFG_SRV(srv_data) \ + ESP_BLE_MESH_SIG_MODEL(ESP_BLE_MESH_MODEL_ID_CONFIG_SRV, \ + NULL, NULL, srv_data) + +/** @def ESP_BLE_MESH_MODEL_CFG_CLI + * + * @brief Define a new Config Client Model. + * + * @note The Config Client Model can only be included by a Primary Element. + * + * @param cli_data Pointer to a unique struct esp_ble_mesh_client_t. + * + * @return New Config Client Model instance. + */ +#define ESP_BLE_MESH_MODEL_CFG_CLI(cli_data) \ + ESP_BLE_MESH_SIG_MODEL(ESP_BLE_MESH_MODEL_ID_CONFIG_CLI, \ + NULL, NULL, cli_data) + +typedef struct esp_ble_mesh_cfg_srv { + esp_ble_mesh_model_t *model; + + uint8_t net_transmit; /*!< Network Transmit state */ + uint8_t relay; /*!< Relay Mode state */ + uint8_t relay_retransmit; /*!< Relay Retransmit state */ + uint8_t beacon; /*!< Secure Network Beacon state */ + uint8_t gatt_proxy; /*!< GATT Proxy state */ + uint8_t friend_state; /*!< Friend state */ + uint8_t default_ttl; /*!< Default TTL */ + + /** Heartbeat Publication */ + struct { + struct k_delayed_work timer; + + uint16_t dst; + uint16_t count; + uint8_t period; + uint8_t ttl; + uint16_t feature; + uint16_t net_idx; + } heartbeat_pub; + + /** Heartbeat Subscription */ + struct { + int64_t expiry; + + uint16_t src; + uint16_t dst; + uint16_t count; + uint8_t min_hops; + uint8_t max_hops; + + /** Optional subscription tracking function */ + void (*func)(uint8_t hops, uint16_t feature); + } heartbeat_sub; +} esp_ble_mesh_cfg_srv_t; + +/** Parameters of Composition Data Get. */ +typedef struct { + uint8_t page; /*!< Page number of the Composition Data. */ +} esp_ble_mesh_cfg_composition_data_get_t; + +/** Parameters of Model Publication Get. */ +typedef struct { + uint16_t element_addr; /*!< The element address */ + uint16_t model_id; /*!< The model id */ + uint16_t company_id; /*!< The company id, if not a vendor model, shall set to 0xFFFF */ +} esp_ble_mesh_cfg_model_pub_get_t; + +/** Parameters of SIG Model Subscription Get. */ +typedef struct { + uint16_t element_addr; /*!< The element address */ + uint16_t model_id; /*!< The model id */ +} esp_ble_mesh_cfg_sig_model_sub_get_t; + +/** Parameters of Vendor Model Subscription Get. */ +typedef struct { + uint16_t element_addr; /*!< The element address */ + uint16_t model_id; /*!< The model id */ + uint16_t company_id; /*!< The company id, if not a vendor model, shall set to 0xFFFF */ +} esp_ble_mesh_cfg_vnd_model_sub_get_t; + +/** Parameters of Application Key Get. */ +typedef struct { + uint16_t net_idx; /*!< The network key index */ +} esp_ble_mesh_cfg_app_key_get_t; + +/** Parameters of Node Identity Get. */ +typedef struct { + uint16_t net_idx; /*!< The network key index */ +} esp_ble_mesh_cfg_node_identity_get_t; + +/** Parameters of SIG Model App Get. */ +typedef struct { + uint16_t element_addr; /*!< The element address */ + uint16_t model_id; /*!< The model id */ +} esp_ble_mesh_cfg_sig_model_app_get_t; + +/** Parameters of Vendor Model App Get. */ +typedef struct { + uint16_t element_addr; /*!< The element address */ + uint16_t model_id; /*!< The model id */ + uint16_t company_id; /*!< The company id, if not a vendor model, shall set to 0xFFFF */ +} esp_ble_mesh_cfg_vnd_model_app_get_t; + +/** Parameters of Key Refresh Phase Get. */ +typedef struct { + uint16_t net_idx; /*!< The network key index */ +} esp_ble_mesh_cfg_kr_phase_get_t; + +/** Parameters of Low Power Node PollTimeout Get. */ +typedef struct { + uint16_t lpn_addr; /*!< The unicast address of the Low Power node */ +} esp_ble_mesh_cfg_lpn_polltimeout_get_t; + +/** Parameters of Beacon Set. */ +typedef struct { + uint8_t beacon; +} esp_ble_mesh_cfg_beacon_set_t; + +/** Parameters of Default TTL Set. */ +typedef struct { + uint8_t ttl; /*!< The default TTL state value */ +} esp_ble_mesh_cfg_default_ttl_set_t; + +/** Parameters of Friend Set. */ +typedef struct { + uint8_t friend_state; /*!< The friend state value */ +} esp_ble_mesh_cfg_friend_set_t; + +/** Parameters of GATT Proxy Set. */ +typedef struct { + uint8_t gatt_proxy; /*!< The GATT Proxy state value */ +} esp_ble_mesh_cfg_gatt_proxy_set_t; + +/** Parameters of Relay Set. */ +typedef struct { + uint8_t relay; /*!< The relay value */ + uint8_t relay_retransmit; /*!< The relay retransmit value */ +} esp_ble_mesh_cfg_relay_set_t; + +/** Parameters of Network Key Add. */ +typedef struct { + uint16_t net_idx; /*!< The network key index */ + uint8_t net_key[16]; /*!< The network key value */ +} esp_ble_mesh_cfg_net_key_add_t; + +/** Parameters of Application Key Add. */ +typedef struct { + uint16_t net_idx; /*!< The network key index */ + uint16_t app_idx; /*!< The app key index */ + uint8_t app_key[16]; /*!< The app key value */ +} esp_ble_mesh_cfg_app_key_add_t; + +/** Parameters of Model Application Key Bind. */ +typedef struct { + uint16_t element_addr; /*!< The element address */ + uint16_t model_app_idx; /*!< Index of the app key to bind with the model */ + uint16_t model_id; /*!< The model id */ + uint16_t company_id; /*!< The company id, if not a vendor model, shall set to 0xFFFF */ +} esp_ble_mesh_cfg_model_app_bind_t; + +/** Parameters of Model Publication Set. */ +typedef struct { + uint16_t element_addr; /*!< The element address */ + uint16_t publish_addr; /*!< Value of the publish address */ + uint16_t publish_app_idx; /*!< Index of the application key */ + bool cred_flag; /*!< Value of the Friendship Credential Flag */ + uint8_t publish_ttl; /*!< Default TTL value for the publishing messages */ + uint8_t publish_period; /*!< Period for periodic status publishing */ + uint8_t publish_retransmit; /*!< Number of retransmissions and number of 50-millisecond steps between retransmissions */ + uint16_t model_id; /*!< The model id */ + uint16_t company_id; /*!< The company id, if not a vendor model, shall set to 0xFFFF */ +} esp_ble_mesh_cfg_model_pub_set_t; + +/** Parameters of Model Subscription Add. */ +typedef struct { + uint16_t element_addr; /*!< The element address */ + uint16_t sub_addr; /*!< The address to be added to the Subscription List */ + uint16_t model_id; /*!< The model id */ + uint16_t company_id; /*!< The company id, if not a vendor model, shall set to 0xFFFF */ +} esp_ble_mesh_cfg_model_sub_add_t; + +/** Parameters of Model Subscription Delete. */ +typedef struct { + uint16_t element_addr; /*!< The element address */ + uint16_t sub_addr; /*!< The address to be removed from the Subscription List */ + uint16_t model_id; /*!< The model id */ + uint16_t company_id; /*!< The company id, if not a vendor model, shall set to 0xFFFF */ +} esp_ble_mesh_cfg_model_sub_delete_t; + +/** Parameters of Model Subscription Overwrite. */ +typedef struct { + uint16_t element_addr; /*!< The element address */ + uint16_t sub_addr; /*!< The address to be added to the Subscription List */ + uint16_t model_id; /*!< The model id */ + uint16_t company_id; /*!< The company id, if not a vendor model, shall set to 0xFFFF */ +} esp_ble_mesh_cfg_model_sub_overwrite_t; + +/** Parameters of Model Subscription Virtual Address Add. */ +typedef struct { + uint16_t element_addr; /*!< The element address */ + uint8_t label_uuid[16]; /*!< The Label UUID of the virtual address to be added to the Subscription List */ + uint16_t model_id; /*!< The model id */ + uint16_t company_id; /*!< The company id, if not a vendor model, shall set to 0xFFFF */ +} esp_ble_mesh_cfg_model_sub_va_add_t; + +/** Parameters of Model Subscription Virtual Address Delete. */ +typedef struct { + uint16_t element_addr; /*!< The element address */ + uint8_t label_uuid[16]; /*!< The Label UUID of the virtual address to be removed from the Subscription List */ + uint16_t model_id; /*!< The model id */ + uint16_t company_id; /*!< The company id, if not a vendor model, shall set to 0xFFFF */ +} esp_ble_mesh_cfg_model_sub_va_delete_t; + +/** Parameters of Model Subscription Virtual Address Overwrite. */ +typedef struct { + uint16_t element_addr; /*!< The element address */ + uint8_t label_uuid[16]; /*!< The Label UUID of the virtual address to be added to the Subscription List */ + uint16_t model_id; /*!< The model id */ + uint16_t company_id; /*!< The company id, if not a vendor model, shall set to 0xFFFF */ +} esp_ble_mesh_cfg_model_sub_va_overwrite_t; + +/** Parameters of Model Publication Virtual Address Set. */ +typedef struct { + uint16_t element_addr; /*!< The element address */ + uint8_t label_uuid[16]; /*!< Value of the Label UUID publish address */ + uint16_t publish_app_idx; /*!< Index of the application key */ + bool cred_flag; /*!< Value of the Friendship Credential Flag */ + uint8_t publish_ttl; /*!< Default TTL value for the publishing messages */ + uint8_t publish_period; /*!< Period for periodic status publishing */ + uint8_t publish_retransmit; /*!< Number of retransmissions and number of 50-millisecond steps between retransmissions */ + uint16_t model_id; /*!< The model id */ + uint16_t company_id; /*!< The company id, if not a vendor model, shall set to 0xFFFF */ +} esp_ble_mesh_cfg_model_pub_va_set_t; + +/** Parameters of Model Subscription Delete All. */ +typedef struct { + uint16_t element_addr; /*!< The element address */ + uint16_t model_id; /*!< The model id */ + uint16_t company_id; /*!< The company id, if not a vendor model, shall set to 0xFFFF */ +} esp_ble_mesh_cfg_model_sub_delete_all_t; + +/** Parameters of Network Key Update. */ +typedef struct { + uint16_t net_idx; /*!< The network key index */ + uint8_t net_key[16]; /*!< The network key value */ +} esp_ble_mesh_cfg_net_key_update_t; + +/** Parameters of Network Key Delete. */ +typedef struct { + uint16_t net_idx; /*!< The network key index */ +} esp_ble_mesh_cfg_net_key_delete_t; + +/** Parameters of Application Key Update. */ +typedef struct { + uint16_t net_idx; /*!< The network key index */ + uint16_t app_idx; /*!< The app key index */ + uint8_t app_key[16]; /*!< The app key value */ +} esp_ble_mesh_cfg_app_key_update_t; + +/** Parameters of Application Key Delete. */ +typedef struct { + uint16_t net_idx; /*!< The network key index */ + uint16_t app_idx; /*!< The app key index */ +} esp_ble_mesh_cfg_app_key_delete_t; + +/** Parameters of Node Identity Set. */ +typedef struct { + uint16_t net_idx; /*!< The network key index */ + uint8_t identity; /*!< New Node Identity state */ +} esp_ble_mesh_cfg_node_identity_set_t; + +/** Parameters of Model Application Key Unbind. */ +typedef struct { + uint16_t element_addr; /*!< The element address */ + uint16_t model_app_idx; /*!< Index of the app key to bind with the model */ + uint16_t model_id; /*!< The model id */ + uint16_t company_id; /*!< The company id, if not a vendor model, shall set to 0xFFFF */ +} esp_ble_mesh_cfg_model_app_unbind_t; + +/** Parameters of Key Refresh Phase Set. */ +typedef struct { + uint16_t net_idx; /*!< The network key index */ + uint8_t transition; /*!< New Key Refresh Phase Transition */ +} esp_ble_mesh_cfg_kr_phase_set_t; + +/** Parameters of Network Transmit Set. */ +typedef struct { + uint8_t net_transmit; /*!< Network Transmit State */ +} esp_ble_mesh_cfg_net_transmit_set_t; + +/** Parameters of Model Heartbeat Publication Set. */ +typedef struct { + uint16_t dst; + uint8_t count; + uint8_t period; + uint8_t ttl; + uint16_t feature; + uint16_t net_idx; +} esp_ble_mesh_cfg_heartbeat_pub_set_t; + +/** Parameters of Model Heartbeat Subscription Set. */ +typedef struct { + uint16_t src; + uint16_t dst; + uint8_t period; +} esp_ble_mesh_cfg_heartbeat_sub_set_t; + +/** + * @brief For ESP_BLE_MESH_MODEL_OP_BEACON_GET + * ESP_BLE_MESH_MODEL_OP_COMPOSITION_DATA_GET + * ESP_BLE_MESH_MODEL_OP_DEFAULT_TTL_GET + * ESP_BLE_MESH_MODEL_OP_GATT_PROXY_GET + * ESP_BLE_MESH_MODEL_OP_RELAY_GET + * ESP_BLE_MESH_MODEL_OP_MODEL_PUB_GET + * ESP_BLE_MESH_MODEL_OP_FRIEND_GET + * ESP_BLE_MESH_MODEL_OP_HEARTBEAT_PUB_GET + * ESP_BLE_MESH_MODEL_OP_HEARTBEAT_SUB_GET + * the get_state parameter in the esp_ble_mesh_config_client_get_state function should not be set to NULL. + */ +typedef union { + esp_ble_mesh_cfg_model_pub_get_t model_pub_get; /*!< For ESP_BLE_MESH_MODEL_OP_MODEL_PUB_GET. */ + esp_ble_mesh_cfg_composition_data_get_t comp_data_get; /*!< For ESP_BLE_MESH_MODEL_OP_COMPOSITION_DATA_GET. */ + esp_ble_mesh_cfg_sig_model_sub_get_t sig_model_sub_get; /*!< For ESP_BLE_MESH_MODEL_OP_SIG_MODEL_SUB_GET */ + esp_ble_mesh_cfg_vnd_model_sub_get_t vnd_model_sub_get; /*!< For ESP_BLE_MESH_MODEL_OP_VENDOR_MODEL_SUB_GET */ + esp_ble_mesh_cfg_app_key_get_t app_key_get; /*!< For ESP_BLE_MESH_MODEL_OP_APP_KEY_GET. */ + esp_ble_mesh_cfg_node_identity_get_t node_identity_get; /*!< For ESP_BLE_MESH_MODEL_OP_NODE_IDENTITY_GET. */ + esp_ble_mesh_cfg_sig_model_app_get_t sig_model_app_get; /*!< For ESP_BLE_MESH_MODEL_OP_SIG_MODEL_APP_GET */ + esp_ble_mesh_cfg_vnd_model_app_get_t vnd_model_app_get; /*!< For ESP_BLE_MESH_MODEL_OP_VENDOR_MODEL_APP_GET */ + esp_ble_mesh_cfg_kr_phase_get_t kr_phase_get; /*!< For ESP_BLE_MESH_MODEL_OP_KEY_REFRESH_PHASE_GET */ + esp_ble_mesh_cfg_lpn_polltimeout_get_t lpn_pollto_get; /*!< For ESP_BLE_MESH_MODEL_OP_LPN_POLLTIMEOUT_GET */ +} esp_ble_mesh_cfg_client_get_state_t; + +/** + * @brief For ESP_BLE_MESH_MODEL_OP_BEACON_SET + * ESP_BLE_MESH_MODEL_OP_DEFAULT_TTL_SET + * ESP_BLE_MESH_MODEL_OP_GATT_PROXY_SET + * ESP_BLE_MESH_MODEL_OP_RELAY_SET + * ESP_BLE_MESH_MODEL_OP_MODEL_PUB_SET + * ESP_BLE_MESH_MODEL_OP_MODEL_SUB_ADD + * ESP_BLE_MESH_MODEL_OP_MODEL_SUB_VIRTUAL_ADDR_ADD + * ESP_BLE_MESH_MODEL_OP_MODEL_SUB_DELETE + * ESP_BLE_MESH_MODEL_OP_MODEL_SUB_VIRTUAL_ADDR_DELETE + * ESP_BLE_MESH_MODEL_OP_MODEL_SUB_OVERWRITE + * ESP_BLE_MESH_MODEL_OP_MODEL_SUB_VIRTUAL_ADDR_OVERWRITE + * ESP_BLE_MESH_MODEL_OP_NET_KEY_ADD + * ESP_BLE_MESH_MODEL_OP_APP_KEY_ADD + * ESP_BLE_MESH_MODEL_OP_MODEL_APP_BIND + * ESP_BLE_MESH_MODEL_OP_NODE_RESET + * ESP_BLE_MESH_MODEL_OP_FRIEND_SET + * ESP_BLE_MESH_MODEL_OP_HEARTBEAT_PUB_SET + * ESP_BLE_MESH_MODEL_OP_HEARTBEAT_SUB_SET + * the set_state parameter in the esp_ble_mesh_config_client_set_state function should not be set to NULL. + */ +typedef union { + esp_ble_mesh_cfg_beacon_set_t beacon_set; /*!< For ESP_BLE_MESH_MODEL_OP_BEACON_SET */ + esp_ble_mesh_cfg_default_ttl_set_t default_ttl_set; /*!< For ESP_BLE_MESH_MODEL_OP_DEFAULT_TTL_SET */ + esp_ble_mesh_cfg_friend_set_t friend_set; /*!< For ESP_BLE_MESH_MODEL_OP_FRIEND_SET */ + esp_ble_mesh_cfg_gatt_proxy_set_t gatt_proxy_set; /*!< For ESP_BLE_MESH_MODEL_OP_GATT_PROXY_SET */ + esp_ble_mesh_cfg_relay_set_t relay_set; /*!< For ESP_BLE_MESH_MODEL_OP_RELAY_SET */ + esp_ble_mesh_cfg_net_key_add_t net_key_add; /*!< For ESP_BLE_MESH_MODEL_OP_NET_KEY_ADD */ + esp_ble_mesh_cfg_app_key_add_t app_key_add; /*!< For ESP_BLE_MESH_MODEL_OP_APP_KEY_ADD */ + esp_ble_mesh_cfg_model_app_bind_t model_app_bind; /*!< For ESP_BLE_MESH_MODEL_OP_MODEL_APP_BIND */ + esp_ble_mesh_cfg_model_pub_set_t model_pub_set; /*!< For ESP_BLE_MESH_MODEL_OP_MODEL_PUB_SET */ + esp_ble_mesh_cfg_model_sub_add_t model_sub_add; /*!< For ESP_BLE_MESH_MODEL_OP_MODEL_SUB_ADD */ + esp_ble_mesh_cfg_model_sub_delete_t model_sub_delete; /*!< For ESP_BLE_MESH_MODEL_OP_MODEL_SUB_DELETE */ + esp_ble_mesh_cfg_model_sub_overwrite_t model_sub_overwrite; /*!< For ESP_BLE_MESH_MODEL_OP_MODEL_SUB_OVERWRITE */ + esp_ble_mesh_cfg_model_sub_va_add_t model_sub_va_add; /*!< For ESP_BLE_MESH_MODEL_OP_MODEL_SUB_VIRTUAL_ADDR_ADD */ + esp_ble_mesh_cfg_model_sub_va_delete_t model_sub_va_delete; /*!< For ESP_BLE_MESH_MODEL_OP_MODEL_SUB_VIRTUAL_ADDR_DELETE */ + esp_ble_mesh_cfg_model_sub_va_overwrite_t model_sub_va_overwrite; /*!< For ESP_BLE_MESH_MODEL_OP_MODEL_SUB_VIRTUAL_ADDR_OVERWRITE */ + esp_ble_mesh_cfg_heartbeat_pub_set_t heartbeat_pub_set; /*!< For ESP_BLE_MESH_MODEL_OP_HEARTBEAT_PUB_SET */ + esp_ble_mesh_cfg_heartbeat_sub_set_t heartbeat_sub_set; /*!< For ESP_BLE_MESH_MODEL_OP_HEARTBEAT_SUB_SET */ + esp_ble_mesh_cfg_model_pub_va_set_t model_pub_va_set; /*!< For ESP_BLE_MESH_MODEL_OP_MODEL_PUB_VIRTUAL_ADDR_SET */ + esp_ble_mesh_cfg_model_sub_delete_all_t model_sub_delete_all; /*!< For ESP_BLE_MESH_MODEL_OP_MODEL_SUB_DELETE_ALL */ + esp_ble_mesh_cfg_net_key_update_t net_key_update; /*!< For ESP_BLE_MESH_MODEL_OP_NET_KEY_UPDATE */ + esp_ble_mesh_cfg_net_key_delete_t net_key_delete; /*!< For ESP_BLE_MESH_MODEL_OP_NET_KEY_DELETE */ + esp_ble_mesh_cfg_app_key_update_t app_key_update; /*!< For ESP_BLE_MESH_MODEL_OP_APP_KEY_UPDATE */ + esp_ble_mesh_cfg_app_key_delete_t app_key_delete; /*!< For ESP_BLE_MESH_MODEL_OP_APP_KEY_DELETE */ + esp_ble_mesh_cfg_node_identity_set_t node_identity_set; /*!< For ESP_BLE_MESH_MODEL_OP_NODE_IDENTITY_SET */ + esp_ble_mesh_cfg_model_app_unbind_t model_app_unbind; /*!< For ESP_BLE_MESH_MODEL_OP_MODEL_APP_UNBIND */ + esp_ble_mesh_cfg_kr_phase_set_t kr_phase_set; /*!< For ESP_BLE_MESH_MODEL_OP_KEY_REFRESH_PHASE_SET */ + esp_ble_mesh_cfg_net_transmit_set_t net_transmit_set; /*!< For ESP_BLE_MESH_MODEL_OP_NETWORK_TRANSMIT_SET */ +} esp_ble_mesh_cfg_client_set_state_t; + +typedef struct { + uint8_t beacon; /*!< Secure Network Beacon state value */ +} esp_ble_mesh_cfg_beacon_status_cb_t; + +typedef struct { + uint8_t page; /*!< Page number of the Composition Data */ + struct net_buf_simple *composition_data; /*!< Pointer to Composition Data for the identified page */ +} esp_ble_mesh_cfg_comp_data_status_cb_t; + +typedef struct { + uint8_t default_ttl; /*!< Default TTL state value */ +} esp_ble_mesh_cfg_default_ttl_status_cb_t; + +typedef struct { + uint8_t gatt_proxy; /*!< GATT Proxy state value */ +} esp_ble_mesh_cfg_gatt_proxy_status_cb_t; + +typedef struct { + uint8_t relay; /*!< Relay state value */ + uint8_t retransmit; /*!< Relay retransmit value(number of retransmissions and number of 10-millisecond steps between retransmissions) */ +} esp_ble_mesh_cfg_relay_status_cb_t; + +typedef struct { + uint8_t status; /*!< Status Code for the request message */ + uint16_t element_addr; /*!< Address of the element */ + uint16_t publish_addr; /*!< Value of the publish address */ + uint16_t app_idx; /*!< Index of the application key */ + bool cred_flag; /*!< Value of the Friendship Credential Flag */ + uint8_t ttl; /*!< Default TTL value for the outgoing messages */ + uint8_t period; /*!< Period for periodic status publishing */ + uint8_t transmit; /*!< Number of retransmissions and number of 50-millisecond steps between retransmissions */ + uint16_t company_id; /*!< Company ID */ + uint16_t model_id; /*!< Model ID */ +} esp_ble_mesh_cfg_model_pub_status_cb_t; + +typedef struct { + uint8_t status; /*!< Status Code for the request message */ + uint16_t element_addr; /*!< Address of the element */ + uint16_t sub_addr; /*!< Value of the address */ + uint16_t company_id; /*!< Company ID */ + uint16_t model_id; /*!< Model ID */ +} esp_ble_mesh_cfg_model_sub_status_cb_t; + +typedef struct { + uint8_t status; /*!< Status Code for the request message */ + uint16_t net_idx; /*!< Index of the NetKey */ +} esp_ble_mesh_cfg_net_key_status_cb_t; + +typedef struct { + uint8_t status; /*!< Status Code for the request message */ + uint16_t net_idx; /*!< Index of the NetKey */ + uint16_t app_idx; /*!< Index of the application key */ +} esp_ble_mesh_cfg_app_key_status_cb_t; + +typedef struct { + uint8_t status; /*!< Status Code for the request message */ + uint16_t element_addr; /*!< Address of the element */ + uint16_t app_idx; /*!< Index of the application key */ + uint16_t company_id; /*!< Company ID */ + uint16_t model_id; /*!< Model ID */ +} esp_ble_mesh_cfg_mod_app_status_cb_t; + +typedef struct { + uint8_t friend_state; /*!< Friend state value */ +} esp_ble_mesh_cfg_friend_status_cb_t; + +typedef struct { + uint8_t status; /*!< Status Code for the request message */ + uint16_t dst; /*!< Destination address for Heartbeat messages */ + uint8_t count; /*!< Number of Heartbeat messages remaining to be sent */ + uint8_t period; /*!< Period for sending Heartbeat messages */ + uint8_t ttl; /*!< TTL to be used when sending Heartbeat messages */ + uint16_t features; /*!< Features that trigger Heartbeat messages when changed */ + uint16_t net_idx; /*!< Index of the NetKey */ +} esp_ble_mesh_cfg_hb_pub_status_cb_t; + +typedef struct { + uint8_t status; /*!< Status Code for the request message */ + uint16_t src; /*!< Source address for Heartbeat messages */ + uint16_t dst; /*!< Destination address for Heartbeat messages */ + uint8_t period; /*!< Remaining Period for processing Heartbeat messages */ + uint8_t count; /*!< Number of Heartbeat messages received */ + uint8_t min_hops; /*!< Minimum hops when receiving Heartbeat messages */ + uint8_t max_hops; /*!< Maximum hops when receiving Heartbeat messages */ +} esp_ble_mesh_cfg_hb_sub_status_cb_t; + +typedef struct { + uint8_t net_trans_count:3; /*!< Number of transmissions for each Network PDU originating from the node */ + uint8_t net_trans_step :5; /*!< Maximum hops when receiving Heartbeat messages */ +} esp_ble_mesh_cfg_net_trans_status_cb_t; + +typedef struct { + uint8_t status; /*!< Status Code for the request message */ + uint16_t element_addr; /*!< Address of the element */ + uint16_t company_id; /*!< Company ID */ + uint16_t model_id; /*!< Model ID */ + struct net_buf_simple *sub_addr; /*!< A block of all addresses from the Subscription List */ +} esp_ble_mesh_cfg_model_sub_list_cb_t; + +typedef struct { + struct net_buf_simple *net_idx; /*!< A list of NetKey Indexes known to the node */ +} esp_ble_mesh_cfg_net_key_list_cb_t; + +typedef struct { + uint8_t status; /*!< Status Code for the request message */ + uint16_t net_idx; /*!< NetKey Index of the NetKey that the AppKeys are bound to */ + struct net_buf_simple *app_idx; /*!< A list of AppKey indexes that are bound to the NetKey identified by NetKeyIndex */ +} esp_ble_mesh_cfg_app_key_list_cb_t; + +typedef struct { + uint8_t status; /*!< Status Code for the request message */ + uint16_t net_idx; /*!< Index of the NetKey */ + uint8_t identity; /*!< Node Identity state */ +} esp_ble_mesh_cfg_node_id_status_cb_t; + +typedef struct { + uint8_t status; /*!< Status Code for the request message */ + uint16_t element_addr; /*!< Address of the element */ + uint16_t company_id; /*!< Company ID */ + uint16_t model_id; /*!< Model ID */ + struct net_buf_simple *app_idx; /*!< All AppKey indexes bound to the Model */ +} esp_ble_mesh_cfg_model_app_list_cb_t; + +typedef struct { + uint8_t status; /*!< Status Code for the request message */ + uint16_t net_idx; /*!< Index of the NetKey */ + uint8_t phase; /*!< Key Refresh Phase state */ +} esp_ble_mesh_cfg_kr_phase_status_cb_t; + +typedef struct { + uint16_t lpn_addr; /*!< The unicast address of the Low Power node */ + int32_t poll_timeout; /*!< The current value of the PollTimeout timer of the Low Power node */ +} esp_ble_mesh_cfg_lpn_pollto_status_cb_t; + +typedef union { + esp_ble_mesh_cfg_beacon_status_cb_t beacon_status; /*!< The beacon status value */ + esp_ble_mesh_cfg_comp_data_status_cb_t comp_data_status; /*!< The composition data status value */ + esp_ble_mesh_cfg_default_ttl_status_cb_t default_ttl_status; /*!< The default_ttl status value */ + esp_ble_mesh_cfg_gatt_proxy_status_cb_t gatt_proxy_status; /*!< The gatt_proxy status value */ + esp_ble_mesh_cfg_relay_status_cb_t relay_status; /*!< The relay status value */ + esp_ble_mesh_cfg_model_pub_status_cb_t model_pub_status; /*!< The model publication status value */ + esp_ble_mesh_cfg_model_sub_status_cb_t model_sub_status; /*!< The model subscription status value */ + esp_ble_mesh_cfg_net_key_status_cb_t netkey_status; /*!< The netkey status value */ + esp_ble_mesh_cfg_app_key_status_cb_t appkey_status; /*!< The appkey status value */ + esp_ble_mesh_cfg_mod_app_status_cb_t model_app_status; /*!< The model app status value */ + esp_ble_mesh_cfg_friend_status_cb_t friend_status; /*!< The friend status value */ + esp_ble_mesh_cfg_hb_pub_status_cb_t heartbeat_pub_status; /*!< The heartbeat publication status value */ + esp_ble_mesh_cfg_hb_sub_status_cb_t heartbeat_sub_status; /*!< The heartbeat subscription status value */ + esp_ble_mesh_cfg_net_trans_status_cb_t net_transmit_status; /*!< The network transmit status value */ + esp_ble_mesh_cfg_model_sub_list_cb_t model_sub_list; /*!< The model subscription list value */ + esp_ble_mesh_cfg_net_key_list_cb_t netkey_list; /*!< The network key index list value */ + esp_ble_mesh_cfg_app_key_list_cb_t appkey_list; /*!< The application key index list value */ + esp_ble_mesh_cfg_node_id_status_cb_t node_identity_status; /*!< The node identity status value */ + esp_ble_mesh_cfg_model_app_list_cb_t model_app_list; /*!< The model application key index list value */ + esp_ble_mesh_cfg_kr_phase_status_cb_t kr_phase_status; /*!< The key refresh phase status value */ + esp_ble_mesh_cfg_lpn_pollto_status_cb_t lpn_timeout_status; /*!< The low power node poll timeout status value */ +} esp_ble_mesh_cfg_client_common_cb_param_t; + +typedef struct { + int error_code; /*!< Appropriate error code */ + esp_ble_mesh_client_common_param_t *params; /*!< The client common parameters */ + esp_ble_mesh_cfg_client_common_cb_param_t status_cb; /*!< The config status message callback values */ +} esp_ble_mesh_cfg_client_cb_param_t; + +typedef enum { + ESP_BLE_MESH_CFG_CLIENT_GET_STATE_EVT, + ESP_BLE_MESH_CFG_CLIENT_SET_STATE_EVT, + ESP_BLE_MESH_CFG_CLIENT_PUBLISH_EVT, + ESP_BLE_MESH_CFG_CLIENT_TIMEOUT_EVT, + ESP_BLE_MESH_CFG_CLIENT_EVT_MAX, +} esp_ble_mesh_cfg_client_cb_event_t; + +typedef struct { + uint16_t app_idx; /* AppKey Index of the Config AppKey Add */ +} esp_ble_mesh_cfg_srv_app_key_add_cb_t; + +typedef union { + esp_ble_mesh_cfg_srv_app_key_add_cb_t app_key_add; /* !< The Config AppKey Add event value */ +} esp_ble_mesh_cfg_server_common_cb_param_t; + +typedef struct { + esp_ble_mesh_model_t *model; /*!< Pointer to the server model structure */ + esp_ble_mesh_msg_ctx_t ctx; /*!< The context of the received message */ + esp_ble_mesh_cfg_server_common_cb_param_t status_cb; /*!< The received configuration message callback values */ +} esp_ble_mesh_cfg_server_cb_param_t; + +typedef enum { + ESP_BLE_MESH_CFG_SERVER_RECV_MSG_EVT, + ESP_BLE_MESH_CFG_SERVER_EVT_MAX, +} esp_ble_mesh_cfg_server_cb_event_t; + +/** + * @brief Bluetooth Mesh Config Client and Server Model functions. + */ + +/** @brief: event, event code of Config Client Model events; param, parameters of Config Client Model events */ +typedef void (* esp_ble_mesh_cfg_client_cb_t)(esp_ble_mesh_cfg_client_cb_event_t event, + esp_ble_mesh_cfg_client_cb_param_t *param); + +/** @brief: event, event code of Config Client Model events; param, parameters of Config Client Model events */ +typedef void (* esp_ble_mesh_cfg_server_cb_t)(esp_ble_mesh_cfg_server_cb_event_t event, + esp_ble_mesh_cfg_server_cb_param_t *param); + +/** + * @brief Register BLE Mesh Config Client Model callback. + * + * @param[in] callback: Pointer to the callback function. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_register_config_client_callback(esp_ble_mesh_cfg_client_cb_t callback); + +/** + * @brief Register BLE Mesh Config Server Model callback. + * + * @param[in] callback: Pointer to the callback function. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_register_config_server_callback(esp_ble_mesh_cfg_server_cb_t callback); + +/** + * @brief Get the value of Config Server Model states using the Config Client Model get messages. + * + * @note If you want to find the opcodes and corresponding meanings accepted by this API, + * please refer to (@ref esp_ble_mesh_opcode_config_client_get_t). + * + * @param[in] params: Pointer to BLE Mesh common client parameters. + * @param[in] get_state: Pointer to a union, each kind of opcode corresponds to one structure inside. + * Shall not be set to NULL. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_config_client_get_state(esp_ble_mesh_client_common_param_t *params, + esp_ble_mesh_cfg_client_get_state_t *get_state); + +/** + * @brief Set the value of the Configuration Server Model states using the Config Client Model set messages. + * + * @note If you want to find the opcodes and corresponding meanings accepted by this API, + * please refer to (@ref esp_ble_mesh_opcode_config_client_set_t). + * + * @param[in] params: Pointer to BLE Mesh common client parameters. + * @param[in] set_state: Pointer to a union, each kind of opcode corresponds to one structure inside. + * Shall not be set to NULL. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_config_client_set_state(esp_ble_mesh_client_common_param_t *params, + esp_ble_mesh_cfg_client_set_state_t *set_state); + +#endif /** _ESP_BLE_MESH_CONFIG_MODEL_API_H_ */ + diff --git a/components/bt/ble_mesh/api/models/include/esp_ble_mesh_generic_model_api.h b/components/bt/ble_mesh/api/models/include/esp_ble_mesh_generic_model_api.h new file mode 100644 index 0000000000..a8db2852f7 --- /dev/null +++ b/components/bt/ble_mesh/api/models/include/esp_ble_mesh_generic_model_api.h @@ -0,0 +1,478 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +/** @file + * @brief Bluetooth Mesh Generic Client Model APIs. + */ + +#ifndef _ESP_BLE_MESH_GENERIC_MODEL_API_H_ +#define _ESP_BLE_MESH_GENERIC_MODEL_API_H_ + +#include "generic_client.h" +#include "esp_ble_mesh_defs.h" + +/** @def ESP_BLE_MESH_MODEL_GEN_ONOFF_CLI + * + * @brief Define a new Generic OnOff Client Model. + * + * @note This API needs to be called for each element on which + * the application needs to have a Generic OnOff Client Model. + * + * @param cli_pub Pointer to the unique struct esp_ble_mesh_model_pub_t. + * @param cli_data Pointer to the unique struct esp_ble_mesh_client_t. + * + * @return New Generic OnOff Client Model instance. + */ +#define ESP_BLE_MESH_MODEL_GEN_ONOFF_CLI(cli_pub, cli_data) \ + ESP_BLE_MESH_SIG_MODEL(ESP_BLE_MESH_MODEL_ID_GEN_ONOFF_CLI, \ + NULL, cli_pub, cli_data) + +/** @def ESP_BLE_MESH_MODEL_GEN_LEVEL_CLI + * + * @brief Define a new Generic Level Client Model. + * + * @note This API needs to be called for each element on which + * the application needs to have a Generic Level Client Model. + * + * @param cli_pub Pointer to the unique struct esp_ble_mesh_model_pub_t. + * @param cli_data Pointer to the unique struct esp_ble_mesh_client_t. + * + * @return New Generic Level Client Model instance. + */ + +#define ESP_BLE_MESH_MODEL_GEN_LEVEL_CLI(cli_pub, cli_data) \ + ESP_BLE_MESH_SIG_MODEL(ESP_BLE_MESH_MODEL_ID_GEN_LEVEL_CLI, \ + NULL, cli_pub, cli_data) + +/** @def ESP_BLE_MESH_MODEL_GEN_DEF_TRANS_TIME_CLI + * + * @brief Define a new Generic Default Transition Time Client Model. + * + * @note This API needs to be called for each element on which + * the application needs to have a Generic Default Transition + * Time Client Model. + * + * @param cli_pub Pointer to the unique struct esp_ble_mesh_model_pub_t. + * @param cli_data Pointer to the unique struct esp_ble_mesh_client_t. + * + * @return New Generic Default Transition Time Client Model instance. + */ +#define ESP_BLE_MESH_MODEL_GEN_DEF_TRANS_TIME_CLI(cli_pub, cli_data) \ + ESP_BLE_MESH_SIG_MODEL(ESP_BLE_MESH_MODEL_ID_GEN_DEF_TRANS_TIME_CLI, \ + NULL, cli_pub, cli_data) + +/** @def ESP_BLE_MESH_MODEL_GEN_POWER_ONOFF_CLI + * + * @brief Define a new Generic Power OnOff Client Model. + * + * @note This API needs to be called for each element on which + * the application needs to have a Generic Power OnOff Client Model. + * + * @param cli_pub Pointer to the unique struct esp_ble_mesh_model_pub_t. + * @param cli_data Pointer to the unique struct esp_ble_mesh_client_t. + * + * @return New Generic Power OnOff Client Model instance. + */ +#define ESP_BLE_MESH_MODEL_GEN_POWER_ONOFF_CLI(cli_pub, cli_data) \ + ESP_BLE_MESH_SIG_MODEL(ESP_BLE_MESH_MODEL_ID_GEN_POWER_ONOFF_CLI, \ + NULL, cli_pub, cli_data) + +/** @def ESP_BLE_MESH_MODEL_GEN_POWER_LEVEL_CLI + * + * @brief Define a new Generic Power Level Client Model. + * + * @note This API needs to be called for each element on which + * the application needs to have a Generic Power Level Client Model. + * + * @param cli_pub Pointer to the unique struct esp_ble_mesh_model_pub_t. + * @param cli_data Pointer to the unique struct esp_ble_mesh_client_t. + * + * @return New Generic Power Level Client Model instance. + */ +#define ESP_BLE_MESH_MODEL_GEN_POWER_LEVEL_CLI(cli_pub, cli_data) \ + ESP_BLE_MESH_SIG_MODEL(ESP_BLE_MESH_MODEL_ID_GEN_POWER_LEVEL_CLI, \ + NULL, cli_pub, cli_data) + +/** @def ESP_BLE_MESH_MODEL_GEN_BATTERY_CLI + * + * @brief Define a new Generic Battery Client Model. + * + * @note This API needs to be called for each element on which + * the application needs to have a Generic Battery Client Model. + * + * @param cli_pub Pointer to the unique struct esp_ble_mesh_model_pub_t. + * @param cli_data Pointer to the unique struct esp_ble_mesh_client_t. + * + * @return New Generic Battery Client Model instance. + */ +#define ESP_BLE_MESH_MODEL_GEN_BATTERY_CLI(cli_pub, cli_data) \ + ESP_BLE_MESH_SIG_MODEL(ESP_BLE_MESH_MODEL_ID_GEN_BATTERY_CLI, \ + NULL, cli_pub, cli_data) + +/** @def ESP_BLE_MESH_MODEL_GEN_LOCATION_CLI + * + * @brief Define a new Generic Location Client Model. + * + * @note This API needs to be called for each element on which + * the application needs to have a Generic Location Client Model. + * + * @param cli_pub Pointer to the unique struct esp_ble_mesh_model_pub_t. + * @param cli_data Pointer to the unique struct esp_ble_mesh_client_t. + * + * @return New Generic Location Client Model instance. + */ +#define ESP_BLE_MESH_MODEL_GEN_LOCATION_CLI(cli_pub, cli_data) \ + ESP_BLE_MESH_SIG_MODEL(ESP_BLE_MESH_MODEL_ID_GEN_LOCATION_CLI, \ + NULL, cli_pub, cli_data) + +/** @def ESP_BLE_MESH_MODEL_GEN_PROPERTY_CLI + * + * @brief Define a new Generic Property Client Model. + * + * @note This API needs to be called for each element on which + * the application needs to have a Generic Property Client Model. + * + * @param cli_pub Pointer to the unique struct esp_ble_mesh_model_pub_t. + * @param cli_data Pointer to the unique struct esp_ble_mesh_client_t. + * + * @return New Generic Location Client Model instance. + */ +#define ESP_BLE_MESH_MODEL_GEN_PROPERTY_CLI(cli_pub, cli_data) \ + ESP_BLE_MESH_SIG_MODEL(ESP_BLE_MESH_MODEL_ID_GEN_PROP_CLI, \ + NULL, cli_pub, cli_data) + +/** + * @brief Bluetooth Mesh Generic Client Model Get and Set parameters structure. + */ + +typedef struct { + bool op_en; /* Indicate if optional parameters are included */ + uint8_t onoff; /* Target value of Generic OnOff state */ + uint8_t tid; /* Transaction ID */ + uint8_t trans_time; /* Time to complete state transition (optional) */ + uint8_t delay; /* Indicate message execution delay (C.1) */ +} esp_ble_mesh_gen_onoff_set_t; + +typedef struct { + bool op_en; /* Indicate if optional parameters are included */ + int16_t level; /* Target value of Generic Level state */ + uint8_t tid; /* Transaction ID */ + uint8_t trans_time; /* Time to complete state transition (optional) */ + uint8_t delay; /* Indicate message execution delay (C.1) */ +} esp_ble_mesh_gen_level_set_t; + +typedef struct { + bool op_en; /* Indicate if optional parameters are included */ + int32_t level; /* Delta change of Generic Level state */ + uint8_t tid; /* Transaction ID */ + uint8_t trans_time; /* Time to complete state transition (optional) */ + uint8_t delay; /* Indicate message execution delay (C.1) */ +} esp_ble_mesh_gen_delta_set_t; + +typedef struct { + bool op_en; /* Indicate if optional parameters are included */ + int16_t delta_level;/* Delta Level step to calculate Move speed for Generic Level state */ + uint8_t tid; /* Transaction ID */ + uint8_t trans_time; /* Time to complete state transition (optional) */ + uint8_t delay; /* Indicate message execution delay (C.1) */ +} esp_ble_mesh_gen_move_set_t; + +typedef struct { + uint8_t trans_time; /* The value of the Generic Default Transition Time state */ +} esp_ble_mesh_gen_def_trans_time_set_t; + +typedef struct { + uint8_t onpowerup; /* The value of the Generic OnPowerUp state */ +} esp_ble_mesh_gen_onpowerup_set_t; + +typedef struct { + bool op_en; /* Indicate if optional parameters are included */ + uint16_t power; /* Target value of Generic Power Actual state */ + uint8_t tid; /* Transaction ID */ + uint8_t trans_time; /* Time to complete state transition (optional) */ + uint8_t delay; /* Indicate message execution delay (C.1) */ +} esp_ble_mesh_gen_power_level_set_t; + +typedef struct { + uint16_t power; /* The value of the Generic Power Default state */ +} esp_ble_mesh_gen_power_default_set_t; + +typedef struct { + uint16_t range_min; /* Value of Range Min field of Generic Power Range state */ + uint16_t range_max; /* Value of Range Max field of Generic Power Range state */ +} esp_ble_mesh_gen_power_range_set_t; + +typedef struct { + int32_t global_latitude; /* Global Coordinates (Latitude) */ + int32_t global_longitude; /* Global Coordinates (Longitude) */ + int16_t global_altitude; /* Global Altitude */ +} esp_ble_mesh_gen_loc_global_set_t; + +typedef struct { + int16_t local_north; /* Local Coordinates (North) */ + int16_t local_east; /* Local Coordinates (East) */ + int16_t local_altitude; /* Local Altitude */ + uint8_t floor_number; /* Floor Number */ + uint16_t uncertainty; /* Uncertainty */ +} esp_ble_mesh_gen_loc_local_set_t; + +typedef struct { + uint16_t property_id; /* Property ID identifying a Generic User Property */ +} esp_ble_mesh_gen_user_property_get_t; + +typedef struct { + uint16_t property_id; /* Property ID identifying a Generic User Property */ + struct net_buf_simple *property_value; /* Raw value for the User Property */ +} esp_ble_mesh_gen_user_property_set_t; + +typedef struct { + uint16_t property_id; /* Property ID identifying a Generic Admin Property */ +} esp_ble_mesh_gen_admin_property_get_t; + +typedef struct { + uint16_t property_id; /* Property ID identifying a Generic Admin Property */ + uint8_t user_access; /* Enumeration indicating user access */ + struct net_buf_simple *property_value; /* Raw value for the Admin Property */ +} esp_ble_mesh_gen_admin_property_set_t; + +typedef struct { + uint16_t property_id; /* Property ID identifying a Generic Manufacturer Property */ +} esp_ble_mesh_gen_manufacturer_property_get_t; + +typedef struct { + uint16_t property_id; /* Property ID identifying a Generic Manufacturer Property */ + uint8_t user_access; /* Enumeration indicating user access */ +} esp_ble_mesh_gen_manufacturer_property_set_t; + +typedef struct { + uint16_t property_id; /* A starting Client Property ID present within an element */ +} esp_ble_mesh_gen_client_properties_get_t; + +typedef union { + esp_ble_mesh_gen_user_property_get_t user_property_get; /*!< For ESP_BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_GET */ + esp_ble_mesh_gen_admin_property_get_t admin_property_get; /*!< For ESP_BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_GET*/ + esp_ble_mesh_gen_manufacturer_property_get_t manufacturer_property_get; /*!< For ESP_BLE_MESH_MODEL_OP_GEN_MANUFACTURER_PROPERTY_SET */ + esp_ble_mesh_gen_client_properties_get_t client_properties_get; /*!< For ESP_BLE_MESH_MODEL_OP_GEN_CLIENT_PROPERTIES_GET */ +} esp_ble_mesh_generic_client_get_state_t; + +typedef union { + esp_ble_mesh_gen_onoff_set_t onoff_set; /*!< For ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET & ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK */ + esp_ble_mesh_gen_level_set_t level_set; /*!< For ESP_BLE_MESH_MODEL_OP_GEN_LEVEL_SET & ESP_BLE_MESH_MODEL_OP_GEN_LEVEL_SET_UNACK */ + esp_ble_mesh_gen_delta_set_t delta_set; /*!< For ESP_BLE_MESH_MODEL_OP_GEN_DELTA_SET & ESP_BLE_MESH_MODEL_OP_GEN_DELTA_SET_UNACK */ + esp_ble_mesh_gen_move_set_t move_set; /*!< For ESP_BLE_MESH_MODEL_OP_GEN_MOVE_SET & ESP_BLE_MESH_MODEL_OP_GEN_MOVE_SET_UNACK */ + esp_ble_mesh_gen_def_trans_time_set_t def_trans_time_set; /*!< For ESP_BLE_MESH_MODEL_OP_GEN_DEF_TRANS_TIME_SET & ESP_BLE_MESH_MODEL_OP_GEN_DEF_TRANS_TIME_SET_UNACK */ + esp_ble_mesh_gen_onpowerup_set_t power_set; /*!< For ESP_BLE_MESH_MODEL_OP_GEN_ONPOWERUP_SET & ESP_BLE_MESH_MODEL_OP_GEN_ONPOWERUP_SET_UNACK */ + esp_ble_mesh_gen_power_level_set_t power_level_set; /*!< For ESP_BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_SET & ESP_BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_SET_UNACK */ + esp_ble_mesh_gen_power_default_set_t power_default_set; /*!< For ESP_BLE_MESH_MODEL_OP_GEN_POWER_DEFAULT_SET & ESP_BLE_MESH_MODEL_OP_GEN_POWER_DEFAULT_SET_UNACK */ + esp_ble_mesh_gen_power_range_set_t power_range_set; /*!< For ESP_BLE_MESH_MODEL_OP_GEN_POWER_RANGE_SET & ESP_BLE_MESH_MODEL_OP_GEN_POWER_RANGE_SET_UNACK */ + esp_ble_mesh_gen_loc_global_set_t loc_global_set; /*!< For ESP_BLE_MESH_MODEL_OP_GEN_LOC_GLOBAL_SET & ESP_BLE_MESH_MODEL_OP_GEN_LOC_GLOBAL_SET_UNACK */ + esp_ble_mesh_gen_loc_local_set_t loc_local_set; /*!< For ESP_BLE_MESH_MODEL_OP_GEN_LOC_LOCAL_SET & ESP_BLE_MESH_MODEL_OP_GEN_LOC_LOCAL_SET_UNACK */ + esp_ble_mesh_gen_user_property_set_t user_property_set; /*!< For ESP_BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_SET & ESP_BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_SET_UNACK */ + esp_ble_mesh_gen_admin_property_set_t admin_property_set; /*!< For ESP_BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_SET & ESP_BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_SET_UNACK */ + esp_ble_mesh_gen_manufacturer_property_set_t manufacturer_property_set; /*!< For ESP_BLE_MESH_MODEL_OP_GEN_MANUFACTURER_PROPERTY_SET & ESP_BLE_MESH_MODEL_OP_GEN_MANUFACTURER_PROPERTY_SET_UNACK */ +} esp_ble_mesh_generic_client_set_state_t; + +/** + * @brief Bluetooth Mesh Generic Client Model Get and Set callback parameters structure. + */ + +typedef struct { + bool op_en; /* Indicate if optional parameters are included */ + uint8_t present_onoff; /* Current value of Generic OnOff state */ + uint8_t target_onoff; /* Target value of Generic OnOff state (optional) */ + uint8_t remain_time; /* Time to complete state transition (C.1) */ +} esp_ble_mesh_gen_onoff_status_cb_t; + +typedef struct { + bool op_en; /* Indicate if optional parameters are included */ + int16_t present_level; /* Current value of Generic Level state */ + int16_t target_level; /* Target value of the Generic Level state (optional) */ + uint8_t remain_time; /* Time to complete state transition (C.1) */ +} esp_ble_mesh_gen_level_status_cb_t; + +typedef struct { + uint8_t trans_time; /* The value of the Generic Default Transition Time state */ +} esp_ble_mesh_gen_def_trans_time_status_cb_t; + +typedef struct { + uint8_t onpowerup; /* The value of the Generic OnPowerUp state */ +} esp_ble_mesh_gen_onpowerup_status_cb_t; + +typedef struct { + bool op_en; /* Indicate if optional parameters are included */ + uint16_t present_power; /* Current value of Generic Power Actual state */ + uint16_t target_power; /* Target value of Generic Power Actual state (optional) */ + uint8_t remain_time; /* Time to complete state transition (C.1) */ +} esp_ble_mesh_gen_power_level_status_cb_t; + +typedef struct { + uint16_t power; /* The value of the Generic Power Last state */ +} esp_ble_mesh_gen_power_last_status_cb_t; + +typedef struct { + uint16_t power; /* The value of the Generic Default Last state */ +} esp_ble_mesh_gen_power_default_status_cb_t; + +typedef struct { + uint8_t status_code; /* Status Code for the request message */ + uint16_t range_min; /* Value of Range Min field of Generic Power Range state */ + uint16_t range_max; /* Value of Range Max field of Generic Power Range state */ +} esp_ble_mesh_gen_power_range_status_cb_t; + +typedef struct { + u32_t battery_level : 8; /* Value of Generic Battery Level state */ + u32_t time_to_discharge : 24; /* Value of Generic Battery Time to Discharge state */ + u32_t time_to_charge : 24; /* Value of Generic Battery Time to Charge state */ + u32_t flags : 8; /* Value of Generic Battery Flags state */ +} esp_ble_mesh_gen_battery_status_cb_t; + +typedef struct { + int32_t global_latitude; /* Global Coordinates (Latitude) */ + int32_t global_longitude; /* Global Coordinates (Longitude) */ + int16_t global_altitude; /* Global Altitude */ +} esp_ble_mesh_gen_loc_global_status_cb_t; + +typedef struct { + int16_t local_north; /* Local Coordinates (North) */ + int16_t local_east; /* Local Coordinates (East) */ + int16_t local_altitude; /* Local Altitude */ + uint8_t floor_number; /* Floor Number */ + uint16_t uncertainty; /* Uncertainty */ +} esp_ble_mesh_gen_loc_local_status_cb_t; + +typedef struct { + struct net_buf_simple *property_ids; /* Buffer contains a sequence of N User Property IDs */ +} esp_ble_mesh_gen_user_properties_status_cb_t; + +typedef struct { + bool op_en; /* Indicate if optional parameters are included */ + uint16_t property_id; /* Property ID identifying a Generic User Property */ + uint8_t user_access; /* Enumeration indicating user access (optional) */ + struct net_buf_simple *property_value; /* Raw value for the User Property (C.1) */ +} esp_ble_mesh_gen_user_property_status_cb_t; + +typedef struct { + struct net_buf_simple *property_ids; /* Buffer contains a sequence of N Admin Property IDs */ +} esp_ble_mesh_gen_admin_properties_status_cb_t; + +typedef struct { + bool op_en; /* Indicate if optional parameters are included */ + uint16_t property_id; /* Property ID identifying a Generic Admin Property */ + uint8_t user_access; /* Enumeration indicating user access (optional) */ + struct net_buf_simple *property_value; /* Raw value for the Admin Property (C.1) */ +} esp_ble_mesh_gen_admin_property_status_cb_t; + +typedef struct { + struct net_buf_simple *property_ids; /* Buffer contains a sequence of N Manufacturer Property IDs */ +} esp_ble_mesh_gen_manufacturer_properties_status_cb_t; + +typedef struct { + bool op_en; /* Indicate if optional parameters are included */ + uint16_t property_id; /* Property ID identifying a Generic Manufacturer Property */ + uint8_t user_access; /* Enumeration indicating user access (optional) */ + struct net_buf_simple *property_value; /* Raw value for the Manufacturer Property (C.1) */ +} esp_ble_mesh_gen_manufacturer_property_status_cb_t; + +typedef struct { + struct net_buf_simple *property_ids; /* Buffer contains a sequence of N Client Property IDs */ +} esp_ble_mesh_gen_client_properties_status_cb_t; + +typedef union { + esp_ble_mesh_gen_onoff_status_cb_t onoff_status; /*!< For ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_STATUS */ + esp_ble_mesh_gen_level_status_cb_t level_status; /*!< For ESP_BLE_MESH_MODEL_OP_GEN_LEVEL_STATUS */ + esp_ble_mesh_gen_def_trans_time_status_cb_t def_trans_time_status; /*!< For ESP_BLE_MESH_MODEL_OP_GEN_DEF_TRANS_TIME_STATUS */ + esp_ble_mesh_gen_onpowerup_status_cb_t onpowerup_status; /*!< For ESP_BLE_MESH_MODEL_OP_GEN_ONPOWERUP_STATUS */ + esp_ble_mesh_gen_power_level_status_cb_t power_level_status; /*!< For ESP_BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_STATUS */ + esp_ble_mesh_gen_power_last_status_cb_t power_last_status; /*!< For ESP_BLE_MESH_MODEL_OP_GEN_POWER_LAST_STATUS */ + esp_ble_mesh_gen_power_default_status_cb_t power_default_status; /*!< For ESP_BLE_MESH_MODEL_OP_GEN_POWER_DEFAULT_STATUS */ + esp_ble_mesh_gen_power_range_status_cb_t power_range_status; /*!< For ESP_BLE_MESH_MODEL_OP_GEN_POWER_RANGE_STATUS */ + esp_ble_mesh_gen_battery_status_cb_t battery_status; /*!< For ESP_BLE_MESH_MODEL_OP_GEN_BATTERY_STATUS */ + esp_ble_mesh_gen_loc_global_status_cb_t location_global_status; /*!< For ESP_BLE_MESH_MODEL_OP_GEN_LOC_GLOBAL_STATUS */ + esp_ble_mesh_gen_loc_local_status_cb_t location_local_status; /*!< ESP_BLE_MESH_MODEL_OP_GEN_LOC_LOCAL_STATUS */ + esp_ble_mesh_gen_user_properties_status_cb_t user_properties_status; /*!< ESP_BLE_MESH_MODEL_OP_GEN_USER_PROPERTIES_STATUS */ + esp_ble_mesh_gen_user_property_status_cb_t user_property_status; /*!< ESP_BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_STATUS */ + esp_ble_mesh_gen_admin_properties_status_cb_t admin_properties_status; /*!< ESP_BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTIES_STATUS */ + esp_ble_mesh_gen_admin_property_status_cb_t admin_property_status; /*!< ESP_BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_STATUS */ + esp_ble_mesh_gen_manufacturer_properties_status_cb_t manufacturer_properties_status; /*!< ESP_BLE_MESH_MODEL_OP_GEN_MANUFACTURER_PROPERTIES_STATUS */ + esp_ble_mesh_gen_manufacturer_property_status_cb_t manufacturer_property_status; /*!< ESP_BLE_MESH_MODEL_OP_GEN_MANUFACTURER_PROPERTY_STATUS */ + esp_ble_mesh_gen_client_properties_status_cb_t client_properties_status; /*!< ESP_BLE_MESH_MODEL_OP_GEN_CLIENT_PROPERTIES_STATUS */ +} esp_ble_mesh_gen_client_status_cb_t; + +typedef struct { + int error_code; /*!< Appropriate error code */ + esp_ble_mesh_client_common_param_t *params; /*!< The client common parameters. */ + esp_ble_mesh_gen_client_status_cb_t status_cb; /*!< The generic status message callback values */ +} esp_ble_mesh_generic_client_cb_param_t; + +typedef enum { + ESP_BLE_MESH_GENERIC_CLIENT_GET_STATE_EVT, + ESP_BLE_MESH_GENERIC_CLIENT_SET_STATE_EVT, + ESP_BLE_MESH_GENERIC_CLIENT_PUBLISH_EVT, + ESP_BLE_MESH_GENERIC_CLIENT_TIMEOUT_EVT, + ESP_BLE_MESH_GENERIC_CLIENT_EVT_MAX, +} esp_ble_mesh_generic_client_cb_event_t; + +/** + * @brief Bluetooth Mesh Generic Client Model function. + */ + +/** @brief: event, event code of Generic Client Model events; param, parameters of Generic Client Model events */ +typedef void (* esp_ble_mesh_generic_client_cb_t)(esp_ble_mesh_generic_client_cb_event_t event, + esp_ble_mesh_generic_client_cb_param_t *param); + +/** + * @brief Register BLE Mesh Generic Client Model callback. + * + * @param[in] callback: Pointer to the callback function. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_register_generic_client_callback(esp_ble_mesh_generic_client_cb_t callback); + +/** + * @brief Get the value of Generic Server Model states using the Generic Client Model get messages. + * + * @note If you want to find the opcodes and corresponding meanings accepted by this API, + * please refer to (@ref esp_ble_mesh_generic_message_opcode_t). + * + * @param[in] params: Pointer to BLE Mesh common client parameters. + * @param[in] get_state: Pointer to generic get message value. + * Shall not be set to NULL. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_generic_client_get_state(esp_ble_mesh_client_common_param_t *params, + esp_ble_mesh_generic_client_get_state_t *get_state); + +/** + * @brief Set the value of Generic Server Model states using the Generic Client Model set messages. + * + * @note If you want to find the opcodes and corresponding meanings accepted by this API, + * please refer to (@ref esp_ble_mesh_generic_message_opcode_t). + * + * @param[in] params: Pointer to BLE Mesh common client parameters. + * @param[in] set_state: Pointer to generic set message value. + * Shall not be set to NULL. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_generic_client_set_state(esp_ble_mesh_client_common_param_t *params, + esp_ble_mesh_generic_client_set_state_t *set_state); + + +#endif /* _ESP_BLE_MESH_GENERIC_MODEL_API_H_ */ + diff --git a/components/bt/ble_mesh/api/models/include/esp_ble_mesh_health_model_api.h b/components/bt/ble_mesh/api/models/include/esp_ble_mesh_health_model_api.h new file mode 100644 index 0000000000..687bdc604e --- /dev/null +++ b/components/bt/ble_mesh/api/models/include/esp_ble_mesh_health_model_api.h @@ -0,0 +1,261 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#ifndef _ESP_BLE_MESH_HEALTH_MODEL_API_H_ +#define _ESP_BLE_MESH_HEALTH_MODEL_API_H_ + +#include "esp_ble_mesh_defs.h" + +/** @def ESP_BLE_MESH_MODEL_HEALTH_SRV + * + * @brief Define a new Health Server Model. + * + * @note The Health Server Model can only be included by a Primary Element. + * + * @param srv Pointer to the unique struct esp_ble_mesh_health_srv_t. + * @param pub Pointer to the unique struct esp_ble_mesh_model_pub_t. + * + * @return New Health Server Model instance. + */ +#define ESP_BLE_MESH_MODEL_HEALTH_SRV(srv, pub) \ + ESP_BLE_MESH_SIG_MODEL(ESP_BLE_MESH_MODEL_ID_HEALTH_SRV, \ + NULL, pub, srv) + +/** @def ESP_BLE_MESH_MODEL_HEALTH_CLI + * + * @brief Define a new Health Client Model. + * + * @note This API needs to be called for each element on which + * the application needs to have a Health Client Model. + * + * @param cli_data Pointer to the unique struct esp_ble_mesh_client_t. + * + * @return New Health Client Model instance. + */ +#define ESP_BLE_MESH_MODEL_HEALTH_CLI(cli_data) \ + ESP_BLE_MESH_SIG_MODEL(ESP_BLE_MESH_MODEL_ID_HEALTH_CLI, \ + NULL, NULL, cli_data) + +typedef struct { + /* Fetch current faults */ + int (*fault_get_cur)(esp_ble_mesh_model_t *model, uint8_t *test_id, + uint16_t *company_id, uint8_t *faults, uint8_t *fault_count); + + /* Fetch registered faults */ + int (*fault_get_reg)(esp_ble_mesh_model_t *model, uint16_t company_id, + uint8_t *test_id, uint8_t *faults, uint8_t *fault_count); + + /* Clear registered faults */ + int (*fault_clear)(esp_ble_mesh_model_t *model, uint16_t company_id); + + /* Run a specific test */ + int (*fault_test)(esp_ble_mesh_model_t *model, uint8_t test_id, uint16_t company_id); + + /* Attention on */ + void (*attn_on)(esp_ble_mesh_model_t *model); + + /* Attention off */ + void (*attn_off)(esp_ble_mesh_model_t *model); +} esp_ble_mesh_health_srv_cb_t; + +/** ESP BLE Mesh Health Server Model Context */ +typedef struct { + esp_ble_mesh_model_t *model; + + /* Optional callback struct */ + const esp_ble_mesh_health_srv_cb_t *cb; + + /* Attention Timer state */ + struct k_delayed_work attn_timer; +} esp_ble_mesh_health_srv_t; + +/** BLE Mesh Health Client Model fault get Context */ +typedef struct { + uint16_t company_id; /*!< Bluetooth assigned 16-bit Company ID */ +} esp_ble_mesh_health_fault_get_t; + +/** Mesh Health Client Model attention set Context */ +typedef struct { + uint8_t attention; /*!< Value of the Attention Timer state */ +} esp_ble_mesh_health_attention_set_t; + +/** Mesh Health client Model period set Context */ +typedef struct { + uint8_t fast_period_divisor; /*!< Divider for the Publish Period */ +} esp_ble_mesh_health_period_set_t; + +/** BLE Mesh Health Client Model fault test Context */ +typedef struct { + uint16_t company_id; /*!< Bluetooth assigned 16-bit Company ID */ + uint8_t test_id; /*!< ID of a specific test to be performed */ +} esp_ble_mesh_health_fault_test_t; + +/** BLE Mesh Health Client Model fault clear Context */ +typedef struct { + uint16_t company_id; /*!< Bluetooth assigned 16-bit Company ID */ +} esp_ble_mesh_health_fault_clear_t; + +/** + * @brief For ESP_BLE_MESH_MODEL_OP_HEALTH_FAULT_GET + * ESP_BLE_MESH_MODEL_OP_ATTENTION_GET + * ESP_BLE_MESH_MODEL_OP_HEALTH_PERIOD_GET + * the get_state parameter in the esp_ble_mesh_health_client_get_state function should not be set to NULL. + */ +typedef union { + esp_ble_mesh_health_fault_get_t fault_get; /*!< For ESP_BLE_MESH_MODEL_OP_HEALTH_FAULT_GET. */ +} esp_ble_mesh_health_client_get_state_t; + +/** + * @brief For ESP_BLE_MESH_MODEL_OP_HEALTH_FAULT_CLEAR + * ESP_BLE_MESH_MODEL_OP_HEALTH_FAULT_CLEAR_UNACK + * ESP_BLE_MESH_MODEL_OP_HEALTH_FAULT_TEST + * ESP_BLE_MESH_MODEL_OP_HEALTH_FAULT_TEST_UNACK + * ESP_BLE_MESH_MODEL_OP_HEALTH_PERIOD_SET + * ESP_BLE_MESH_MODEL_OP_HEALTH_PERIOD_SET_UNACK + * ESP_BLE_MESH_MODEL_OP_ATTENTION_SET + * ESP_BLE_MESH_MODEL_OP_ATTENTION_SET_UNACK + * the set_state parameter in the esp_ble_mesh_health_client_set_state function should not be set to NULL. + */ +typedef union { + esp_ble_mesh_health_attention_set_t attention_set; /*!< For ESP_BLE_MESH_MODEL_OP_ATTENTION_SET or ESP_BLE_MESH_MODEL_OP_ATTENTION_SET_UNACK. */ + esp_ble_mesh_health_period_set_t period_set; /*!< For ESP_BLE_MESH_MODEL_OP_HEALTH_PERIOD_SET or ESP_BLE_MESH_MODEL_OP_HEALTH_PERIOD_SET_UNACK. */ + esp_ble_mesh_health_fault_test_t fault_test; /*!< For ESP_BLE_MESH_MODEL_OP_HEALTH_FAULT_TEST or ESP_BLE_MESH_MODEL_OP_HEALTH_FAULT_TEST_UNACK. */ + esp_ble_mesh_health_fault_clear_t fault_clear; /*!< For ESP_BLE_MESH_MODEL_OP_HEALTH_FAULT_CLEAR or ESP_BLE_MESH_MODEL_OP_HEALTH_FAULT_CLEAR_UNACK. */ +} esp_ble_mesh_health_client_set_state_t; + +typedef struct { + uint8_t test_id; /*!< ID of a most recently performed test */ + uint16_t company_id; /*!< Bluetooth assigned 16-bit Company ID */ + struct net_buf_simple *fault_array; /*!< FaultArray field contains a sequence of 1-octet fault values */ +} esp_ble_mesh_health_current_status_cb_t; + +typedef struct { + uint8_t test_id; /*!< ID of a most recently performed test */ + uint16_t company_id; /*!< Bluetooth assigned 16-bit Company ID */ + struct net_buf_simple *fault_array; /*!< FaultArray field contains a sequence of 1-octet fault values */ +} esp_ble_mesh_health_fault_status_cb_t; + +typedef struct { + uint8_t fast_period_divisor; /*!< Divider for the Publish Period */ +} esp_ble_mesh_health_period_status_cb_t; + +typedef struct { + uint8_t attention; /*!< Value of the Attention Timer state */ +} esp_ble_mesh_health_attention_status_cb_t; + +typedef union { + esp_ble_mesh_health_current_status_cb_t current_status; /*!< The health current status value */ + esp_ble_mesh_health_fault_status_cb_t fault_status; /*!< The health fault status value */ + esp_ble_mesh_health_period_status_cb_t period_status; /*!< The health period status value */ + esp_ble_mesh_health_attention_status_cb_t attention_status; /*!< The health attention status value */ +} esp_ble_mesh_health_client_common_cb_param_t; + +typedef struct { + int error_code; /*!< Appropriate error code */ + esp_ble_mesh_client_common_param_t *params; /*!< The client common parameters. */ + esp_ble_mesh_health_client_common_cb_param_t status_cb; /*!< The health message status callback values */ +} esp_ble_mesh_health_client_cb_param_t; + +typedef enum { + ESP_BLE_MESH_HEALTH_CLIENT_GET_STATE_EVT, + ESP_BLE_MESH_HEALTH_CLIENT_SET_STATE_EVT, + ESP_BLE_MESH_HEALTH_CLIENT_PUBLISH_EVT, + ESP_BLE_MESH_HEALTH_CLIENT_TIMEOUT_EVT, + ESP_BLE_MESH_HEALTH_CLIENT_EVT_MAX, +} esp_ble_mesh_health_client_cb_event_t; + +typedef struct { + int error_code; /*!< Appropriate error code */ +} esp_ble_mesh_health_server_cb_param_t; + +typedef enum { + ESP_BLE_MESH_HEALTH_SERVER_FAULT_UPDATE_COMPLETE_EVT, + ESP_BLE_MESH_HEALTH_SERVER_EVT_MAX, +} esp_ble_mesh_health_server_cb_event_t; + +/** + * @brief Bluetooth Mesh Health Client and Server Model function. + */ + +/** @brief: event, event code of Health Client Model event; param, parameters of Health Client Model event) */ +typedef void (* esp_ble_mesh_health_client_cb_t)(esp_ble_mesh_health_client_cb_event_t event, + esp_ble_mesh_health_client_cb_param_t *param); + +/** @brief: event, event code of Health Server Model event; param, parameters of Health Server Model event) */ +typedef void (* esp_ble_mesh_health_server_cb_t)(esp_ble_mesh_health_server_cb_event_t event, + esp_ble_mesh_health_server_cb_param_t *param); + +/** + * @brief Register BLE Mesh Health Model callback, the callback will report Health Client & Server Model events. + * + * @param[in] callback: Pointer to the callback function. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_register_health_client_callback(esp_ble_mesh_health_client_cb_t callback); + +/** + * @brief Register BLE Mesh Health Server Model callback. + * + * @param[in] callback: Pointer to the callback function. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_register_health_server_callback(esp_ble_mesh_health_server_cb_t callback); + +/** + * @brief This function is called to get the Health Server states using the Health Client Model get messages. + * + * @note If you want to find the opcodes and corresponding meanings accepted by this API, + * please refer to (@ref esp_ble_mesh_opcode_health_client_get_t). + * + * @param[in] params: Pointer to BLE Mesh common client parameters. + * @param[in] get_state: Pointer to a union, each kind of opcode corresponds to one structure inside. + * Shall not be set to NULL. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_health_client_get_state(esp_ble_mesh_client_common_param_t *params, + esp_ble_mesh_health_client_get_state_t *get_state); + +/** + * @brief This function is called to set the Health Server states using the Health Client Model set messages. + * + * @note If you want to find the opcodes and corresponding meanings accepted by this API, + * please refer to (@ref esp_ble_mesh_opcode_health_client_set_t). + * + * @param[in] params: Pointer to BLE Mesh common client parameters. + * @param[in] set_state: Pointer to a union, each kind of opcode corresponds to one structure inside. + * Shall not be set to NULL. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_health_client_set_state(esp_ble_mesh_client_common_param_t *params, + esp_ble_mesh_health_client_set_state_t *set_state); + +/** + * @brief This function is called by the Health Server Model to start to publish its Current Health Fault. + * + * @param[in] element: The element to which the Health Server Model belongs. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_health_server_fault_update(esp_ble_mesh_elem_t *element); + +#endif /** _ESP_BLE_MESH_HEALTH_MODEL_API_H_ */ diff --git a/components/bt/ble_mesh/api/models/include/esp_ble_mesh_lighting_model_api.h b/components/bt/ble_mesh/api/models/include/esp_ble_mesh_lighting_model_api.h new file mode 100644 index 0000000000..ab119a48bc --- /dev/null +++ b/components/bt/ble_mesh/api/models/include/esp_ble_mesh_lighting_model_api.h @@ -0,0 +1,526 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +/** @file + * @brief Bluetooth Mesh Light Client Model APIs. + */ + +#ifndef _ESP_BLE_MESH_LIGHTING_MODEL_API_H_ +#define _ESP_BLE_MESH_LIGHTING_MODEL_API_H_ + +#include "lighting_client.h" +#include "esp_ble_mesh_defs.h" + +/** @def ESP_BLE_MESH_MODEL_LIGHT_LIGHTNESS_CLI + * + * @brief Define a new Light Lightness Client Model. + * + * @note This API needs to be called for each element on which + * the application needs to have a Light Lightness Client Model. + * + * @param cli_pub Pointer to the unique struct esp_ble_mesh_model_pub_t. + * @param cli_data Pointer to the unique struct esp_ble_mesh_client_t. + * + * @return New Light Lightness Client Model instance. + */ +#define ESP_BLE_MESH_MODEL_LIGHT_LIGHTNESS_CLI(cli_pub, cli_data) \ + ESP_BLE_MESH_SIG_MODEL(ESP_BLE_MESH_MODEL_ID_LIGHT_LIGHTNESS_CLI, \ + NULL, cli_pub, cli_data) + +/** @def ESP_BLE_MESH_MODEL_LIGHT_CTL_CLI + * + * @brief Define a new Light CTL Client Model. + * + * @note This API needs to be called for each element on which + * the application needs to have a Light CTL Client Model. + * + * @param cli_pub Pointer to the unique struct esp_ble_mesh_model_pub_t. + * @param cli_data Pointer to the unique struct esp_ble_mesh_client_t. + * + * @return New Light CTL Client Model instance. + */ +#define ESP_BLE_MESH_MODEL_LIGHT_CTL_CLI(cli_pub, cli_data) \ + ESP_BLE_MESH_SIG_MODEL(ESP_BLE_MESH_MODEL_ID_LIGHT_CTL_CLI, \ + NULL, cli_pub, cli_data) + +/** @def ESP_BLE_MESH_MODEL_LIGHT_HSL_CLI + * + * @brief Define a new Light HSL Client Model. + * + * @note This API needs to be called for each element on which + * the application needs to have a Light HSL Client Model. + * + * @param cli_pub Pointer to the unique struct esp_ble_mesh_model_pub_t. + * @param cli_data Pointer to the unique struct esp_ble_mesh_client_t. + * + * @return New Light HSL Client Model instance. + */ +#define ESP_BLE_MESH_MODEL_LIGHT_HSL_CLI(cli_pub, cli_data) \ + ESP_BLE_MESH_SIG_MODEL(ESP_BLE_MESH_MODEL_ID_LIGHT_HSL_CLI, \ + NULL, cli_pub, cli_data) + +/** @def ESP_BLE_MESH_MODEL_LIGHT_XYL_CLI + * + * @brief Define a new Light xyL Client Model. + * + * @note This API needs to be called for each element on which + * the application needs to have a Light xyL Client Model. + * + * @param cli_pub Pointer to the unique struct esp_ble_mesh_model_pub_t. + * @param cli_data Pointer to the unique struct esp_ble_mesh_client_t. + * + * @return New Light xyL Client Model instance. + */ +#define ESP_BLE_MESH_MODEL_LIGHT_XYL_CLI(cli_pub, cli_data) \ + ESP_BLE_MESH_SIG_MODEL(ESP_BLE_MESH_MODEL_ID_LIGHT_XYL_CLI, \ + NULL, cli_pub, cli_data) + +/** @def ESP_BLE_MESH_MODEL_LIGHT_LC_CLI + * + * @brief Define a new Light LC Client Model. + * + * @note This API needs to be called for each element on which + * the application needs to have a Light LC Client Model. + * + * @param cli_pub Pointer to the unique struct esp_ble_mesh_model_pub_t. + * @param cli_data Pointer to the unique struct esp_ble_mesh_client_t. + * + * @return New Light LC Client Model instance. + */ +#define ESP_BLE_MESH_MODEL_LIGHT_LC_CLI(cli_pub, cli_data) \ + ESP_BLE_MESH_SIG_MODEL(ESP_BLE_MESH_MODEL_ID_LIGHT_LC_CLI, \ + NULL, cli_pub, cli_data) + +/** + * @brief Bluetooth Mesh Light Lightness Client Model Get and Set parameters structure. + */ + +typedef struct { + bool op_en; /* Indicate if optional parameters are included */ + u16_t lightness; /* Target value of light lightness actual state */ + u8_t tid; /* Transaction ID */ + u8_t trans_time; /* Time to complete state transition (optional) */ + u8_t delay; /* Indicate message execution delay (C.1) */ +} esp_ble_mesh_light_lightness_set_t; + +typedef struct { + bool op_en; /* Indicate if optional parameters are included */ + u16_t lightness; /* Target value of light lightness linear state */ + u8_t tid; /* Transaction ID */ + u8_t trans_time; /* Time to complete state transition (optional) */ + u8_t delay; /* Indicate message execution delay (C.1) */ +} esp_ble_mesh_light_lightness_linear_set_t; + +typedef struct { + u16_t lightness; /* The value of the Light Lightness Default state */ +} esp_ble_mesh_light_lightness_default_set_t; + +typedef struct { + u16_t range_min; /* Value of range min field of light lightness range state */ + u16_t range_max; /* Value of range max field of light lightness range state */ +} esp_ble_mesh_light_lightness_range_set_t; + +typedef struct { + bool op_en; /* Indicate if optional parameters are included */ + u16_t ctl_lightness; /* Target value of light ctl lightness state */ + u16_t ctl_temperatrue; /* Target value of light ctl temperature state */ + s16_t ctl_delta_uv; /* Target value of light ctl delta UV state */ + u8_t tid; /* Transaction ID */ + u8_t trans_time; /* Time to complete state transition (optional) */ + u8_t delay; /* Indicate message execution delay (C.1) */ +} esp_ble_mesh_light_ctl_set_t; + +typedef struct { + bool op_en; /* Indicate if optional parameters are included */ + u16_t ctl_temperatrue; /* Target value of light ctl temperature state */ + s16_t ctl_delta_uv; /* Target value of light ctl delta UV state */ + u8_t tid; /* Transaction ID */ + u8_t trans_time; /* Time to complete state transition (optional) */ + u8_t delay; /* Indicate message execution delay (C.1) */ +} esp_ble_mesh_light_ctl_temperature_set_t; + +typedef struct { + u16_t range_min; /* Value of temperature range min field of light ctl temperature range state */ + u16_t range_max; /* Value of temperature range max field of light ctl temperature range state */ +} esp_ble_mesh_light_ctl_temperature_range_set_t; + +typedef struct { + u16_t lightness; /* Value of light lightness default state */ + u16_t temperature; /* Value of light temperature default state */ + s16_t delta_uv; /* Value of light delta UV default state */ +} esp_ble_mesh_light_ctl_default_set_t; + +typedef struct { + bool op_en; /* Indicate if optional parameters are included */ + u16_t hsl_lightness; /* Target value of light hsl lightness state */ + u16_t hsl_hue; /* Target value of light hsl hue state */ + u16_t hsl_saturation; /* Target value of light hsl saturation state */ + u8_t tid; /* Transaction ID */ + u8_t trans_time; /* Time to complete state transition (optional) */ + u8_t delay; /* Indicate message execution delay (C.1) */ +} esp_ble_mesh_light_hsl_set_t; + +typedef struct { + bool op_en; /* Indicate if optional parameters are included */ + u16_t hue; /* Target value of light hsl hue state */ + u8_t tid; /* Transaction ID */ + u8_t trans_time; /* Time to complete state transition (optional) */ + u8_t delay; /* Indicate message execution delay (C.1) */ +} esp_ble_mesh_light_hsl_hue_set_t; + +typedef struct { + bool op_en; /* Indicate if optional parameters are included */ + u16_t saturation; /* Target value of light hsl hue state */ + u8_t tid; /* Transaction ID */ + u8_t trans_time; /* Time to complete state transition (optional) */ + u8_t delay; /* Indicate message execution delay (C.1) */ +} esp_ble_mesh_light_hsl_saturation_set_t; + +typedef struct { + u16_t lightness; /* Value of light lightness default state */ + u16_t hue; /* Value of light hue default state */ + u16_t saturation; /* Value of light saturation default state */ +} esp_ble_mesh_light_hsl_default_set_t; + +typedef struct { + u16_t hue_range_min; /* Value of hue range min field of light hsl hue range state */ + u16_t hue_range_max; /* Value of hue range max field of light hsl hue range state */ + u16_t saturation_range_min; /* Value of saturation range min field of light hsl saturation range state */ + u16_t saturation_range_max; /* Value of saturation range max field of light hsl saturation range state */ +} esp_ble_mesh_light_hsl_range_set_t; + +typedef struct { + bool op_en; /* Indicate whether optional parameters included */ + u16_t xyl_lightness; /* The target value of the Light xyL Lightness state */ + u16_t xyl_x; /* The target value of the Light xyL x state */ + u16_t xyl_y; /* The target value of the Light xyL y state */ + u8_t tid; /* Transaction Identifier */ + u8_t trans_time; /* Time to complete state transition (optional) */ + u8_t delay; /* Indicate message execution delay (C.1) */ +} esp_ble_mesh_light_xyl_set_t; + +typedef struct { + u16_t lightness; /* The value of the Light Lightness Default state */ + u16_t xyl_x; /* The value of the Light xyL x Default state */ + u16_t xyl_y; /* The value of the Light xyL y Default state */ +} esp_ble_mesh_light_xyl_default_set_t; + +typedef struct { + u16_t xyl_x_range_min; /* The value of the xyL x Range Min field of the Light xyL x Range state */ + u16_t xyl_x_range_max; /* The value of the xyL x Range Max field of the Light xyL x Range state */ + u16_t xyl_y_range_min; /* The value of the xyL y Range Min field of the Light xyL y Range state */ + u16_t xyl_y_range_max; /* The value of the xyL y Range Max field of the Light xyL y Range state */ +} esp_ble_mesh_light_xyl_range_set_t; + +typedef struct { + u8_t mode; /* The target value of the Light LC Mode state */ +} esp_ble_mesh_light_lc_mode_set_t; + +typedef struct { + u8_t mode; /* The target value of the Light LC Occupancy Mode state */ +} esp_ble_mesh_light_lc_om_set_t; + +typedef struct { + bool op_en; /* Indicate whether optional parameters included */ + u8_t light_onoff; /* The target value of the Light LC Light OnOff state */ + u8_t tid; /* Transaction Identifier */ + u8_t trans_time; /* Time to complete state transition (optional) */ + u8_t delay; /* Indicate message execution delay (C.1) */ +} esp_ble_mesh_light_lc_light_onoff_set_t; + +typedef struct { + u16_t property_id; /* Property ID identifying a Light LC Property */ +} esp_ble_mesh_light_lc_property_get_t; + +typedef struct { + u16_t property_id; /* Property ID identifying a Light LC Property */ + struct net_buf_simple *property_value; /* Raw value for the Light LC Property */ +} esp_ble_mesh_light_lc_property_set_t; + +typedef union { + esp_ble_mesh_light_lc_property_get_t lc_property_get; /*!< For ESP_BLE_MESH_MODEL_OP_LIGHT_LC_PROPERTY_GET */ +} esp_ble_mesh_light_client_get_state_t; + +typedef union { + esp_ble_mesh_light_lightness_set_t lightness_set; /*!< For ESP_BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_SET & ESP_BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_SET_UNACK */ + esp_ble_mesh_light_lightness_linear_set_t lightness_linear_set; /*!< For ESP_BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_SET & ESP_BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_SET_UNACK */ + esp_ble_mesh_light_lightness_default_set_t lightness_default_set; /*!< For ESP_BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_DEFAULT_SET & ESP_BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_DEFAULT_SET_UNACK */ + esp_ble_mesh_light_lightness_range_set_t lightness_range_set; /*!< For ESP_BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_RANGE_SET & ESP_BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_RANGE_SET_UNACK */ + esp_ble_mesh_light_ctl_set_t ctl_set; /*!< For ESP_BLE_MESH_MODEL_OP_LIGHT_CTL_SET & ESP_BLE_MESH_MODEL_OP_LIGHT_CTL_SET_UNACK */ + esp_ble_mesh_light_ctl_temperature_set_t ctl_temperature_set; /*!< For ESP_BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_SET & ESP_BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_SET_UNACK */ + esp_ble_mesh_light_ctl_temperature_range_set_t ctl_temperature_range_set; /*!< For ESP_BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_RANGE_SET & ESP_BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_RANGE_SET_UNACK */ + esp_ble_mesh_light_ctl_default_set_t ctl_default_set; /*!< For ESP_BLE_MESH_MODEL_OP_LIGHT_CTL_DEFAULT_SET & ESP_BLE_MESH_MODEL_OP_LIGHT_CTL_DEFAULT_SET_UNACK */ + esp_ble_mesh_light_hsl_set_t hsl_set; /*!< For ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_SET & ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_SET_UNACK */ + esp_ble_mesh_light_hsl_hue_set_t hsl_hue_set; /*!< For ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_SET & ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_SET_UNACK */ + esp_ble_mesh_light_hsl_saturation_set_t hsl_saturation_set; /*!< For ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_SET & ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_SET_UNACK */ + esp_ble_mesh_light_hsl_default_set_t hsl_default_set; /*!< For ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_DEFAULT_SET & ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_DEFAULT_SET_UNACK */ + esp_ble_mesh_light_hsl_range_set_t hsl_range_set; /*!< For ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_RANGE_SET & ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_RANGE_SET_UNACK */ + esp_ble_mesh_light_xyl_set_t xyl_set; /*!< For ESP_BLE_MESH_MODEL_OP_LIGHT_XYL_SET & ESP_BLE_MESH_MODEL_OP_LIGHT_XYL_SET_UNACK */ + esp_ble_mesh_light_xyl_default_set_t xyl_default_set; /*!< For ESP_BLE_MESH_MODEL_OP_LIGHT_XYL_DEFAULT_SET & ESP_BLE_MESH_MODEL_OP_LIGHT_XYL_DEFAULT_SET_UNACK */ + esp_ble_mesh_light_xyl_range_set_t xyl_range_set; /*!< For ESP_BLE_MESH_MODEL_OP_LIGHT_XYL_RANGE_SET & ESP_BLE_MESH_MODEL_OP_LIGHT_XYL_RANGE_SET_UNACK */ + esp_ble_mesh_light_lc_mode_set_t lc_mode_set; /*!< For ESP_BLE_MESH_MODEL_OP_LIGHT_LC_MODE_SET & ESP_BLE_MESH_MODEL_OP_LIGHT_LC_MODE_SET_UNACK */ + esp_ble_mesh_light_lc_om_set_t lc_om_set; /*!< For ESP_BLE_MESH_MODEL_OP_LIGHT_LC_OM_SET & ESP_BLE_MESH_MODEL_OP_LIGHT_LC_OM_SET_UNACK */ + esp_ble_mesh_light_lc_light_onoff_set_t lc_light_onoff_set; /*!< For ESP_BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_SET & ESP_BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_SET_UNACK */ + esp_ble_mesh_light_lc_property_set_t lc_property_set; /*!< For ESP_BLE_MESH_MODEL_OP_LIGHT_LC_PROPERTY_SET & ESP_BLE_MESH_MODEL_OP_LIGHT_LC_PROPERTY_SET_UNACK */ +} esp_ble_mesh_light_client_set_state_t; + +/** + * @brief Bluetooth Mesh Light Lightness Client Model Get and Set callback parameters structure. + */ + +typedef struct { + bool op_en; /* Indicate if optional parameters are included */ + u16_t present_lightness; /* Current value of light lightness actual state */ + u16_t target_lightness; /* Target value of light lightness actual state (optional) */ + u8_t remain_time; /* Time to complete state transition (C.1) */ +} esp_ble_mesh_light_lightness_status_cb_t; + +typedef struct { + bool op_en; /* Indicate if optional parameters are included */ + u16_t present_lightness; /* Current value of light lightness linear state */ + u16_t target_lightness; /* Target value of light lightness linear state (optional) */ + u8_t remain_time; /* Time to complete state transition (C.1) */ +} esp_ble_mesh_light_lightness_linear_status_cb_t; + +typedef struct { + u16_t lightness; /* The value of the Light Lightness Last state */ +} esp_ble_mesh_light_lightness_last_status_cb_t; + +typedef struct { + u16_t lightness; /* The value of the Light Lightness default State */ +} esp_ble_mesh_light_lightness_default_status_cb_t; + +typedef struct { + u8_t status_code; /* Status Code for the request message */ + u16_t range_min; /* Value of range min field of light lightness range state */ + u16_t range_max; /* Value of range max field of light lightness range state */ +} esp_ble_mesh_light_lightness_range_status_cb_t; + +typedef struct { + bool op_en; /* Indicate if optional parameters are included */ + u16_t present_ctl_lightness; /* Current value of light ctl lightness state */ + u16_t present_ctl_temperature; /* Current value of light ctl temperature state */ + u16_t target_ctl_lightness; /* Target value of light ctl lightness state (optional) */ + u16_t target_ctl_temperature; /* Target value of light ctl temperature state (C.1) */ + u8_t remain_time; /* Time to complete state transition (C.1) */ +} esp_ble_mesh_light_ctl_status_cb_t; + +typedef struct { + bool op_en; /* Indicate if optional parameters are included */ + u16_t present_ctl_temperature; /* Current value of light ctl temperature state */ + u16_t present_ctl_delta_uv; /* Current value of light ctl delta UV state */ + u16_t target_ctl_temperature; /* Target value of light ctl temperature state (optional) */ + u16_t target_ctl_delta_uv; /* Target value of light ctl delta UV state (C.1) */ + u8_t remain_time; /* Time to complete state transition (C.1) */ +} esp_ble_mesh_light_ctl_temperature_status_cb_t; + +typedef struct { + u8_t status_code; /* Status code for the request message */ + u16_t range_min; /* Value of temperature range min field of light ctl temperature range state */ + u16_t range_max; /* Value of temperature range max field of light ctl temperature range state */ +} esp_ble_mesh_light_ctl_temperature_range_status_cb_t; + +typedef struct { + u16_t lightness; /* Value of light lightness default state */ + u16_t temperature; /* Value of light temperature default state */ + s16_t delta_uv; /* Value of light delta UV default state */ +} esp_ble_mesh_light_ctl_default_status_cb_t; + +typedef struct { + bool op_en; /* Indicate if optional parameters are included */ + u16_t hsl_lightness; /* Current value of light hsl lightness state */ + u16_t hsl_hue; /* Current value of light hsl hue state */ + u16_t hsl_saturation; /* Current value of light hsl saturation state */ + u8_t remain_time; /* Time to complete state transition (optional) */ +} esp_ble_mesh_light_hsl_status_cb_t; + +typedef struct { + bool op_en; /* Indicate if optional parameters are included */ + u16_t hsl_lightness_target; /* Target value of light hsl lightness state */ + u16_t hsl_hue_target; /* Target value of light hsl hue state */ + u16_t hsl_saturation_target; /* Target value of light hsl saturation state */ + u8_t remain_time; /* Time to complete state transition (optional) */ +} esp_ble_mesh_light_hsl_target_status_cb_t; + +typedef struct { + bool op_en; /* Indicate if optional parameters are included */ + u16_t present_hue; /* Current value of light hsl hue state */ + u16_t target_hue; /* Target value of light hsl hue state (optional) */ + u8_t remain_time; /* Time to complete state transition (C.1) */ +} esp_ble_mesh_light_hsl_hue_status_cb_t; + +typedef struct { + bool op_en; /* Indicate if optional parameters are included */ + u16_t present_saturation; /* Current value of light hsl saturation state */ + u16_t target_saturation; /* Target value of light hsl saturation state (optional) */ + u8_t remain_time; /* Time to complete state transition (C.1) */ +} esp_ble_mesh_light_hsl_saturation_status_cb_t; + +typedef struct { + u16_t lightness; /* Value of light lightness default state */ + u16_t hue; /* Value of light hue default state */ + u16_t saturation; /* Value of light saturation default state */ +} esp_ble_mesh_light_hsl_default_status_cb_t; + +typedef struct { + u8_t status_code; /* Status code for the request message */ + u16_t hue_range_min; /* Value of hue range min field of light hsl hue range state */ + u16_t hue_range_max; /* Value of hue range max field of light hsl hue range state */ + u16_t saturation_range_min; /* Value of saturation range min field of light hsl saturation range state */ + u16_t saturation_range_max; /* Value of saturation range max field of light hsl saturation range state */ +} esp_ble_mesh_light_hsl_range_status_cb_t; + +typedef struct { + bool op_en; /* Indicate whether optional parameters included */ + u16_t xyl_lightness; /* The present value of the Light xyL Lightness state */ + u16_t xyl_x; /* The present value of the Light xyL x state */ + u16_t xyl_y; /* The present value of the Light xyL y state */ + u8_t remain_time; /* Time to complete state transition (optional) */ +} esp_ble_mesh_light_xyl_status_cb_t; + +typedef struct { + bool op_en; /* Indicate whether optional parameters included */ + u16_t target_xyl_lightness; /* The target value of the Light xyL Lightness state */ + u16_t target_xyl_x; /* The target value of the Light xyL x state */ + u16_t target_xyl_y; /* The target value of the Light xyL y state */ + u8_t remain_time; /* Time to complete state transition (optional) */ +} esp_ble_mesh_light_xyl_target_status_cb_t; + +typedef struct { + u16_t lightness; /* The value of the Light Lightness Default state */ + u16_t xyl_x; /* The value of the Light xyL x Default state */ + u16_t xyl_y; /* The value of the Light xyL y Default state */ +} esp_ble_mesh_light_xyl_default_status_cb_t; + +typedef struct { + u8_t status_code; /* Status Code for the requesting message */ + u16_t xyl_x_range_min; /* The value of the xyL x Range Min field of the Light xyL x Range state */ + u16_t xyl_x_range_max; /* The value of the xyL x Range Max field of the Light xyL x Range state */ + u16_t xyl_y_range_min; /* The value of the xyL y Range Min field of the Light xyL y Range state */ + u16_t xyl_y_range_max; /* The value of the xyL y Range Max field of the Light xyL y Range state */ +} esp_ble_mesh_light_xyl_range_status_cb_t; + +typedef struct { + u8_t mode; /* The present value of the Light LC Mode state */ +} esp_ble_mesh_light_lc_mode_status_cb_t; + +typedef struct { + u8_t mode; /* The present value of the Light LC Occupancy Mode state */ +} esp_ble_mesh_light_lc_om_status_cb_t; + +typedef struct { + bool op_en; /* Indicate whether optional parameters included */ + u8_t present_light_onoff; /* The present value of the Light LC Light OnOff state */ + u8_t target_light_onoff; /* The target value of the Light LC Light OnOff state (Optional) */ + u8_t remain_time; /* Time to complete state transition (C.1) */ +} esp_ble_mesh_light_lc_light_onoff_status_cb_t; + +typedef struct { + u16_t property_id; /* Property ID identifying a Light LC Property */ + struct net_buf_simple *property_value; /* Raw value for the Light LC Property */ +} esp_ble_mesh_light_lc_property_status_cb_t; + +typedef union { + esp_ble_mesh_light_lightness_status_cb_t lightness_status; /*!< For ESP_BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_STATUS */ + esp_ble_mesh_light_lightness_linear_status_cb_t lightness_linear_status; /*!< For ESP_BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_STATUS */ + esp_ble_mesh_light_lightness_last_status_cb_t lightness_last_status; /*!< For ESP_BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LAST_STATUS */ + esp_ble_mesh_light_lightness_default_status_cb_t lightness_default_status; /*!< For ESP_BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_DEFAULT_STATUS */ + esp_ble_mesh_light_lightness_range_status_cb_t lightness_range_status; /*!< For ESP_BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_RANGE_STATUS */ + esp_ble_mesh_light_ctl_status_cb_t ctl_status; /*!< For ESP_BLE_MESH_MODEL_OP_LIGHT_CTL_STATUS */ + esp_ble_mesh_light_ctl_temperature_status_cb_t ctl_temperature_status; /*!< For ESP_BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_STATUS */ + esp_ble_mesh_light_ctl_temperature_range_status_cb_t ctl_temperature_range_status; /*!< For ESP_BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_RANGE_STATUS */ + esp_ble_mesh_light_ctl_default_status_cb_t ctl_default_status; /*!< For ESP_BLE_MESH_MODEL_OP_LIGHT_CTL_DEFAULT_STATUS */ + esp_ble_mesh_light_hsl_status_cb_t hsl_status; /*!< For ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_STATUS */ + esp_ble_mesh_light_hsl_target_status_cb_t hsl_target_status; /*!< For ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_TARGET_STATUS */ + esp_ble_mesh_light_hsl_hue_status_cb_t hsl_hue_status; /*!< For ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_STATUS */ + esp_ble_mesh_light_hsl_saturation_status_cb_t hsl_saturation_status; /*!< For ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_STATUS */ + esp_ble_mesh_light_hsl_default_status_cb_t hsl_default_status; /*!< For ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_DEFAULT_STATUS */ + esp_ble_mesh_light_hsl_range_status_cb_t hsl_range_status; /*!< For ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_RANGE_STATUS */ + esp_ble_mesh_light_xyl_status_cb_t xyl_status; /*!< For ESP_BLE_MESH_MODEL_OP_LIGHT_XYL_STATUS */ + esp_ble_mesh_light_xyl_target_status_cb_t xyl_target_status; /*!< For ESP_BLE_MESH_MODEL_OP_LIGHT_XYL_TARGET_STATUS */ + esp_ble_mesh_light_xyl_default_status_cb_t xyl_default_status; /*!< For ESP_BLE_MESH_MODEL_OP_LIGHT_XYL_DEFAULT_STATUS */ + esp_ble_mesh_light_xyl_range_status_cb_t xyl_range_status; /*!< For ESP_BLE_MESH_MODEL_OP_LIGHT_XYL_RANGE_STATUS */ + esp_ble_mesh_light_lc_mode_status_cb_t lc_mode_status; /*!< For ESP_BLE_MESH_MODEL_OP_LIGHT_LC_MODE_STATUS */ + esp_ble_mesh_light_lc_om_status_cb_t lc_om_status; /*!< For ESP_BLE_MESH_MODEL_OP_LIGHT_LC_OM_STATUS */ + esp_ble_mesh_light_lc_light_onoff_status_cb_t lc_light_onoff_status; /*!< For ESP_BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_STATUS */ + esp_ble_mesh_light_lc_property_status_cb_t lc_property_status; /*!< For ESP_BLE_MESH_MODEL_OP_LIGHT_LC_PROPERTY_STATUS */ +} esp_ble_mesh_light_client_status_cb_t; + +typedef struct { + int error_code; /*!< Appropriate error code */ + esp_ble_mesh_client_common_param_t *params; /*!< The client common parameters. */ + esp_ble_mesh_light_client_status_cb_t status_cb; /*!< The light status message callback values */ +} esp_ble_mesh_light_client_cb_param_t; + +typedef enum { + ESP_BLE_MESH_LIGHT_CLIENT_GET_STATE_EVT, + ESP_BLE_MESH_LIGHT_CLIENT_SET_STATE_EVT, + ESP_BLE_MESH_LIGHT_CLIENT_PUBLISH_EVT, + ESP_BLE_MESH_LIGHT_CLIENT_TIMEOUT_EVT, + ESP_BLE_MESH_LIGHT_CLIENT_EVT_MAX, +} esp_ble_mesh_light_client_cb_event_t; + +/** + * @brief Bluetooth Mesh Light Client Model function. + */ + +/** @brief: event, event code of Light Client Model events; param, parameters of Light Client Model events */ +typedef void (* esp_ble_mesh_light_client_cb_t)(esp_ble_mesh_light_client_cb_event_t event, + esp_ble_mesh_light_client_cb_param_t *param); + +/** + * @brief Register BLE Mesh Light Client Model callback. + * + * @param[in] callback: pointer to the callback function. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_register_light_client_callback(esp_ble_mesh_light_client_cb_t callback); + +/** + * @brief Get the value of Light Server Model states using the Light Client Model get messages. + * + * @note If you want to know the opcodes and corresponding meanings accepted by this API, + * please refer to (@ref esp_ble_mesh_light_message_opcode_t). + * + * @param[in] params: Pointer to BLE Mesh common client parameters. + * @param[in] get_state: Pointer of light get message value. + * Shall not be set to NULL. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_light_client_get_state(esp_ble_mesh_client_common_param_t *params, + esp_ble_mesh_light_client_get_state_t *get_state); + +/** + * @brief Set the value of Light Server Model states using the Light Client Model set messages. + * + * @note If you want to know the opcodes and corresponding meanings accepted by this API, + * please refer to (@ref esp_ble_mesh_light_message_opcode_t). + * + * @param[in] params: Pointer to BLE Mesh common client parameters. + * @param[in] set_state: Pointer of generic set message value. + * Shall not be set to NULL. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_light_client_set_state(esp_ble_mesh_client_common_param_t *params, + esp_ble_mesh_light_client_set_state_t *set_state); + + +#endif /* _ESP_BLE_MESH_LIGHTING_MODEL_API_H_ */ + diff --git a/components/bt/ble_mesh/api/models/include/esp_ble_mesh_sensor_model_api.h b/components/bt/ble_mesh/api/models/include/esp_ble_mesh_sensor_model_api.h new file mode 100644 index 0000000000..553845cad8 --- /dev/null +++ b/components/bt/ble_mesh/api/models/include/esp_ble_mesh_sensor_model_api.h @@ -0,0 +1,230 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +/** @file + * @brief Bluetooth Mesh Sensor Client Model APIs. + */ + +#ifndef _ESP_BLE_MESH_SENSOR_MODEL_API_H_ +#define _ESP_BLE_MESH_SENSOR_MODEL_API_H_ + +#include "sensor_client.h" +#include "esp_ble_mesh_defs.h" + +/** @def ESP_BLE_MESH_MODEL_SENSOR_CLI + * + * @brief Define a new Sensor Client Model. + * + * @note This API needs to be called for each element on which + * the application needs to have a Sensor Client Model. + * + * @param cli_pub Pointer to the unique struct esp_ble_mesh_model_pub_t. + * @param cli_data Pointer to the unique struct esp_ble_mesh_client_t. + * + * @return New Sensor Client Model instance. + */ +#define ESP_BLE_MESH_MODEL_SENSOR_CLI(cli_pub, cli_data) \ + ESP_BLE_MESH_SIG_MODEL(ESP_BLE_MESH_MODEL_ID_SENSOR_CLI, \ + NULL, cli_pub, cli_data) + +/** + * @brief Bluetooth Mesh Sensor Client Model Get and Set parameters structure. + */ +typedef struct { + bool op_en; /* Indicate if optional parameters are included */ + u16_t property_id; /* Property ID of a sensor (optional) */ +} esp_ble_mesh_sensor_descriptor_get_t; + +typedef struct { + u16_t property_id; /* Property ID of a sensor */ +} esp_ble_mesh_sensor_cadence_get_t; + +typedef struct { + u16_t property_id; /* Property ID for the sensor */ + u8_t fast_cadence_period_divisor : 7, /* Divisor for the publish period */ + status_trigger_type : 1; /* The unit and format of the Status Trigger Delta fields */ + struct net_buf_simple *status_trigger_delta_down; /* Delta down value that triggers a status message */ + struct net_buf_simple *status_trigger_delta_up; /* Delta up value that triggers a status message */ + u8_t status_min_interval; /* Minimum interval between two consecutive Status messages */ + struct net_buf_simple *fast_cadence_low; /* Low value for the fast cadence range */ + struct net_buf_simple *fast_cadence_high; /* Fast value for the fast cadence range */ +} esp_ble_mesh_sensor_cadence_set_t; + +typedef struct { + u16_t sensor_property_id; /* Property ID of a sensor */ +} esp_ble_mesh_sensor_settings_get_t; + +typedef struct { + u16_t sensor_property_id; /* Property ID of a sensor */ + u16_t sensor_setting_property_id; /* Setting ID identifying a setting within a sensor */ +} esp_ble_mesh_sensor_setting_get_t; + +typedef struct { + u16_t sensor_property_id; /* Property ID identifying a sensor */ + u16_t sensor_setting_property_id; /* Setting ID identifying a setting within a sensor */ + struct net_buf_simple *sensor_setting_raw; /* Raw value for the setting */ +} esp_ble_mesh_sensor_setting_set_t; + +typedef struct { + bool op_en; /* Indicate if optional parameters are included */ + u16_t property_id; /* Property ID for the sensor (optional) */ +} esp_ble_mesh_sensor_get_t; + +typedef struct { + u16_t property_id; /* Property identifying a sensor */ + struct net_buf_simple *raw_value_x; /* Raw value identifying a column */ +} esp_ble_mesh_sensor_column_get_t; + +typedef struct { + bool op_en; /* Indicate if optional parameters are included */ + u16_t property_id; /* Property identifying a sensor */ + struct net_buf_simple *raw_value_x1; /* Raw value identifying a starting column (optional) */ + struct net_buf_simple *raw_value_x2; /* Raw value identifying an ending column (C.1) */ +} esp_ble_mesh_sensor_series_get_t; + +typedef union { + esp_ble_mesh_sensor_descriptor_get_t descriptor_get; /*!< For ESP_BLE_MESH_MODEL_OP_SENSOR_DESCRIPTOR_GET */ + esp_ble_mesh_sensor_cadence_get_t cadence_get; /*!< For ESP_BLE_MESH_MODEL_OP_SENSOR_CADENCE_GET */ + esp_ble_mesh_sensor_settings_get_t settings_get; /*!< For ESP_BLE_MESH_MODEL_OP_SENSOR_SETTINGS_GET */ + esp_ble_mesh_sensor_setting_get_t setting_get; /*!< For ESP_BLE_MESH_MODEL_OP_SENSOR_SETTING_GET */ + esp_ble_mesh_sensor_get_t sensor_get; /*!< For ESP_BLE_MESH_MODEL_OP_SENSOR_GET */ + esp_ble_mesh_sensor_column_get_t column_get; /*!< For ESP_BLE_MESH_MODEL_OP_SENSOR_COLUMN_GET */ + esp_ble_mesh_sensor_series_get_t series_get; /*!< For ESP_BLE_MESH_MODEL_OP_SENSOR_SERIES_GET */ +} esp_ble_mesh_sensor_client_get_state_t; + +typedef union { + esp_ble_mesh_sensor_cadence_set_t cadence_set; /*!< For ESP_BLE_MESH_MODEL_OP_SENSOR_CADENCE_SET & ESP_BLE_MESH_MODEL_OP_SENSOR_CADENCE_SET_UNACK */ + esp_ble_mesh_sensor_setting_set_t setting_set; /*!< For ESP_BLE_MESH_MODEL_OP_SENSOR_SETTING_SET & ESP_BLE_MESH_MODEL_OP_SENSOR_SETTING_SET_UNACK */ +} esp_ble_mesh_sensor_client_set_state_t; + +/** + * @brief Bluetooth Mesh Sensor Client Model Get and Set callback parameters structure. + */ + +typedef struct { + struct net_buf_simple *descriptor; /* Sequence of 8-octet sensor descriptors (optional) */ +} esp_ble_mesh_sensor_descriptor_status_cb_t; + +typedef struct { + u16_t property_id; /* Property for the sensor */ + struct net_buf_simple *sensor_cadence_value; /* Value of sensor cadence state */ +} esp_ble_mesh_sensor_cadence_status_cb_t; + +typedef struct { + u16_t sensor_property_id; /* Property ID identifying a sensor */ + struct net_buf_simple *sensor_setting_property_ids; /* A sequence of N sensor setting property IDs (optional) */ +} esp_ble_mesh_sensor_settings_status_cb_t; + +typedef struct { + bool op_en; /* Indicate id optional parameters are included */ + u16_t sensor_property_id; /* Property ID identifying a sensor */ + u16_t sensor_setting_property_id; /* Setting ID identifying a setting within a sensor */ + u8_t sensor_setting_access; /* Read/Write access rights for the setting (optional) */ + struct net_buf_simple *sensor_setting_raw; /* Raw value for the setting */ +} esp_ble_mesh_sensor_setting_status_cb_t; + +typedef struct { + struct net_buf_simple *marshalled_sensor_data; /* Value of sensor data state (optional) */ +} esp_ble_mesh_sensor_status_cb_t; + +typedef struct { + u16_t property_id; /* Property identifying a sensor and the Y axis */ + struct net_buf_simple *sensor_column_value; /* Left values of sensor column status */ +} esp_ble_mesh_sensor_column_status_cb_t; + +typedef struct { + u16_t property_id; /* Property identifying a sensor and the Y axis */ + struct net_buf_simple *sensor_series_value; /* Left values of sensor series status */ +} esp_ble_mesh_sensor_series_status_cb_t; + + +typedef union { + esp_ble_mesh_sensor_descriptor_status_cb_t descriptor_status; /*!< For ESP_BLE_MESH_MODEL_OP_SENSOR_DESCRIPTOR_STATUS */ + esp_ble_mesh_sensor_cadence_status_cb_t cadence_status; /*!< For ESP_BLE_MESH_MODEL_OP_SENSOR_CADENCE_STATUS */ + esp_ble_mesh_sensor_settings_status_cb_t settings_status; /*!< For ESP_BLE_MESH_MODEL_OP_SENSOR_SETTINGS_STATUS */ + esp_ble_mesh_sensor_setting_status_cb_t setting_status; /*!< For ESP_BLE_MESH_MODEL_OP_SENSOR_SETTING_STATUS */ + esp_ble_mesh_sensor_status_cb_t sensor_status; /*!< For ESP_BLE_MESH_MODEL_OP_SENSOR_STATUS */ + esp_ble_mesh_sensor_column_status_cb_t column_status; /*!< For ESP_BLE_MESH_MODEL_OP_SENSOR_COLUMN_STATUS */ + esp_ble_mesh_sensor_series_status_cb_t series_status; /*!< For ESP_BLE_MESH_MODEL_OP_SENSOR_SERIES_STATUS */ +} esp_ble_mesh_sensor_client_status_cb_t; + +typedef struct { + int error_code; /*!< 0: success, + * otherwise failure. For the error code values please refer to errno.h file. + * A negative sign is added to the standard error codes in errno.h. */ + esp_ble_mesh_client_common_param_t *params; /*!< The client common parameters. */ + esp_ble_mesh_sensor_client_status_cb_t status_cb; /*!< The sensor status message callback values */ +} esp_ble_mesh_sensor_client_cb_param_t; + +typedef enum { + ESP_BLE_MESH_SENSOR_CLIENT_GET_STATE_EVT, + ESP_BLE_MESH_SENSOR_CLIENT_SET_STATE_EVT, + ESP_BLE_MESH_SENSOR_CLIENT_PUBLISH_EVT, + ESP_BLE_MESH_SENSOR_CLIENT_TIMEOUT_EVT, + ESP_BLE_MESH_SENSOR_CLIENT_EVT_MAX, +} esp_ble_mesh_sensor_client_cb_event_t; + +/** + * @brief Bluetooth Mesh Sensor Client Model function. + */ + +/** @brief: event, event code of Sensor Client Model events; param, parameters of Sensor Client Model events */ +typedef void (* esp_ble_mesh_sensor_client_cb_t)(esp_ble_mesh_sensor_client_cb_event_t event, + esp_ble_mesh_sensor_client_cb_param_t *param); + +/** + * @brief Register BLE Mesh Sensor Client Model callback. + * + * @param[in] callback: Pointer to the callback function. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_register_sensor_client_callback(esp_ble_mesh_sensor_client_cb_t callback); + +/** + * @brief Get the value of Sensor Server Model states using the Sensor Client Model get messages. + * + * @note If you want to know the opcodes and corresponding meanings accepted by this API, + * please refer to (@ref esp_ble_mesh_sensor_message_opcode_t). + * + * @param[in] params: Pointer to BLE Mesh common client parameters. + * @param[in] get_state: Pointer to sensor get message value. + * Shall not be set to NULL. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_sensor_client_get_state(esp_ble_mesh_client_common_param_t *params, + esp_ble_mesh_sensor_client_get_state_t *get_state); + +/** + * @brief Set the value of Sensor Server Model states using the Sensor Client Model set messages. + * + * @note If you want to know the opcodes and corresponding meanings accepted by this API, + * please refer to (@ref esp_ble_mesh_sensor_message_opcode_t). + * + * @param[in] params: Pointer to BLE Mesh common client parameters. + * @param[in] set_state: Pointer to sensor set message value. + * Shall not be set to NULL. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_sensor_client_set_state(esp_ble_mesh_client_common_param_t *params, + esp_ble_mesh_sensor_client_set_state_t *set_state); + +#endif /* _ESP_BLE_MESH_SENSOR_MODEL_API_H_ */ + + diff --git a/components/bt/ble_mesh/api/models/include/esp_ble_mesh_time_scene_model_api.h b/components/bt/ble_mesh/api/models/include/esp_ble_mesh_time_scene_model_api.h new file mode 100644 index 0000000000..cdf55ef170 --- /dev/null +++ b/components/bt/ble_mesh/api/models/include/esp_ble_mesh_time_scene_model_api.h @@ -0,0 +1,292 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +/** @file + * @brief Bluetooth Mesh Time and Scene Client Model APIs. + */ + +#ifndef _ESP_BLE_MESH_TIME_SCENE_MODEL_API_H_ +#define _ESP_BLE_MESH_TIME_SCENE_MODEL_API_H_ + +#include "time_scene_client.h" +#include "esp_ble_mesh_defs.h" + +/** @def ESP_BLE_MESH_MODEL_TIME_CLI + * + * @brief Define a new Time Client Model. + * + * @note This API needs to be called for each element on which + * the application needs to have a Time Client Model. + * + * @param cli_pub Pointer to the unique struct esp_ble_mesh_model_pub_t. + * @param cli_data Pointer to the unique struct esp_ble_mesh_client_t. + * + * @return New Time Client Model instance. + */ +#define ESP_BLE_MESH_MODEL_TIME_CLI(cli_pub, cli_data) \ + ESP_BLE_MESH_SIG_MODEL(ESP_BLE_MESH_MODEL_ID_TIME_CLI, \ + NULL, cli_pub, cli_data) + +/** @def ESP_BLE_MESH_MODEL_SCENE_CLI + * + * @brief Define a new Scene Client Model. + * + * @note This API needs to be called for each element on which + * the application needs to have a Scene Client Model. + * + * @param cli_pub Pointer to the unique struct esp_ble_mesh_model_pub_t. + * @param cli_data Pointer to the unique struct esp_ble_mesh_client_t. + * + * @return New Scene Client Model instance. + */ +#define ESP_BLE_MESH_MODEL_SCENE_CLI(cli_pub, cli_data) \ + ESP_BLE_MESH_SIG_MODEL(ESP_BLE_MESH_MODEL_ID_SCENE_CLI, \ + NULL, cli_pub, cli_data) + +/** @def ESP_BLE_MESH_MODEL_SCHEDULER_CLI + * + * @brief Define a new Scheduler Client Model. + * + * @note This API needs to be called for each element on which + * the application needs to have a Scheduler Client Model. + * + * @param cli_pub Pointer to the unique struct esp_ble_mesh_model_pub_t. + * @param cli_data Pointer to the unique struct esp_ble_mesh_client_t. + * + * @return New Scheduler Client Model instance. + */ +#define ESP_BLE_MESH_MODEL_SCHEDULER_CLI(cli_pub, cli_data) \ + ESP_BLE_MESH_SIG_MODEL(ESP_BLE_MESH_MODEL_ID_SCHEDULER_CLI, \ + NULL, cli_pub, cli_data) + +/** + * @brief Bluetooth Mesh Time Scene Client Model Get and Set parameters structure. + */ + +typedef struct { + u8_t tai_seconds[5]; /* The current TAI time in seconds */ + u8_t sub_second; /* The sub-second time in units of 1/256 second */ + u8_t uncertainty; /* The estimated uncertainty in 10-millisecond steps */ + u16_t time_authority : 1; /* 0 = No Time Authority, 1 = Time Authority */ + u16_t tai_utc_delta : 15; /* Current difference between TAI and UTC in seconds */ + u8_t time_zone_offset; /* The local time zone offset in 15-minute increments */ +} esp_ble_mesh_time_set_t; + +typedef struct { + u8_t time_zone_offset_new; /* Upcoming local time zone offset */ + u8_t tai_zone_change[5]; /* TAI Seconds time of the upcoming Time Zone Offset change */ +} esp_ble_mesh_time_zone_set_t; + +typedef struct { + u16_t tai_utc_delta_new : 15; /* Upcoming difference between TAI and UTC in seconds */ + u16_t padding : 1; /* Always 0b0. Other values are Prohibited. */ + u8_t tai_delta_change[5]; /* TAI Seconds time of the upcoming TAI-UTC Delta change */ +} esp_ble_mesh_tai_utc_delta_set_t; + +typedef struct { + u8_t time_role; /* The Time Role for the element */ +} esp_ble_mesh_time_role_set_t; + +typedef struct { + u16_t scene_number; /* The number of scenes to be stored */ +} esp_ble_mesh_scene_store_t; + +typedef struct { + bool op_en; /* Indicate if optional parameters are included */ + u16_t scene_number; /* The number of scenes to be recalled */ + u8_t tid; /* Transaction ID */ + u8_t trans_time; /* Time to complete state transition (optional) */ + u8_t delay; /* Indicate message execution delay (C.1) */ +} esp_ble_mesh_scene_recall_t; + +typedef struct { + u16_t scene_number; /* The number of scenes to be deleted */ +} esp_ble_mesh_scene_delete_t; + +typedef struct { + u8_t index; /* Index of the Schedule Register entry to get */ +} esp_ble_mesh_scheduler_act_get_t; + +typedef struct { + u64_t index : 4; /* Index of the Schedule Register entry to set */ + u64_t year : 7; /* Scheduled year for the action */ + u64_t month : 12; /* Scheduled month for the action */ + u64_t day : 5; /* Scheduled day of the month for the action */ + u64_t hour : 5; /* Scheduled hour for the action */ + u64_t minute : 6; /* Scheduled minute for the action */ + u64_t second : 6; /* Scheduled second for the action */ + u64_t day_of_week : 7; /* Schedule days of the week for the action */ + u64_t action : 4; /* Action to be performed at the scheduled time */ + u64_t trans_time : 8; /* Transition time for this action */ + u16_t scene_number; /* Transition time for this action */ +} esp_ble_mesh_scheduler_act_set_t; + +/** + * @brief For + * + * the get_state parameter in the esp_ble_mesh_time_scene_client_get_state function should be set to NULL. + */ +typedef union { + esp_ble_mesh_scheduler_act_get_t scheduler_act_get; /*!< For ESP_BLE_MESH_MODEL_OP_SCHEDULER_ACT_GET */ +} esp_ble_mesh_time_scene_client_get_state_t; + +typedef union { + esp_ble_mesh_time_set_t time_set; /*!< For ESP_BLE_MESH_MODEL_OP_TIME_SET */ + esp_ble_mesh_time_zone_set_t time_zone_set; /*!< For ESP_BLE_MESH_MODEL_OP_TIME_ZONE_SET */ + esp_ble_mesh_tai_utc_delta_set_t tai_utc_delta_set; /*!< For ESP_BLE_MESH_MODEL_OP_TAI_UTC_DELTA_SET */ + esp_ble_mesh_time_role_set_t time_role_set; /*!< For ESP_BLE_MESH_MODEL_OP_TIME_ROLE_SET */ + esp_ble_mesh_scene_store_t scene_store; /*!< For ESP_BLE_MESH_MODEL_OP_SCENE_STORE & ESP_BLE_MESH_MODEL_OP_SCENE_STORE_UNACK */ + esp_ble_mesh_scene_recall_t scene_recall; /*!< For ESP_BLE_MESH_MODEL_OP_SCENE_RECALL & ESP_BLE_MESH_MODEL_OP_SCENE_RECALL_UNACK */ + esp_ble_mesh_scene_delete_t scene_delete; /*!< For ESP_BLE_MESH_MODEL_OP_SCENE_DELETE & ESP_BLE_MESH_MODEL_OP_SCENE_DELETE_UNACK */ + esp_ble_mesh_scheduler_act_set_t scheduler_act_set; /*!< For ESP_BLE_MESH_MODEL_OP_SCHEDULER_ACT_SET & ESP_BLE_MESH_MODEL_OP_SCHEDULER_ACT_SET_UNACK */ +} esp_ble_mesh_time_scene_client_set_state_t; + +/** + * @brief Bluetooth Mesh Time Scene Client Model Get and Set callback parameters structure. + */ + +typedef struct { + u8_t tai_seconds[5]; /* The current TAI time in seconds */ + u8_t sub_second; /* The sub-second time in units of 1/256 second */ + u8_t uncertainty; /* The estimated uncertainty in 10-millisecond steps */ + u16_t time_authority : 1; /* 0 = No Time Authority, 1 = Time Authority */ + u16_t tai_utc_delta : 15; /* Current difference between TAI and UTC in seconds */ + u8_t time_zone_offset; /* The local time zone offset in 15-minute increments */ +} esp_ble_mesh_time_status_cb_t; + +typedef struct { + u8_t time_zone_offset_curr; /* Current local time zone offset */ + u8_t time_zone_offset_new; /* Upcoming local time zone offset */ + u8_t tai_zone_change[5]; /* TAI Seconds time of the upcoming Time Zone Offset change */ +} esp_ble_mesh_time_zone_status_cb_t; + +typedef struct { + u16_t tai_utc_delta_curr : 15; /* Current difference between TAI and UTC in seconds */ + u16_t padding_1 : 1; /* Always 0b0. Other values are Prohibited. */ + u16_t tai_utc_delta_new : 15; /* Upcoming difference between TAI and UTC in seconds */ + u16_t padding_2 : 1; /* Always 0b0. Other values are Prohibited. */ + u8_t tai_delta_change[5]; /* TAI Seconds time of the upcoming TAI-UTC Delta change */ +} esp_ble_mesh_tai_utc_delta_status_cb_t; + +typedef struct { + u8_t time_role; /* The Time Role for the element */ +} esp_ble_mesh_time_role_status_cb_t; + +typedef struct { + bool op_en; /* Indicate if optional parameters are included */ + u8_t status_code; /* Status code of the last operation */ + u16_t current_scene; /* Scene Number of the current scene */ + u16_t target_scene; /* Scene Number of the target scene (optional) */ + u8_t remain_time; /* Time to complete state transition (C.1) */ +} esp_ble_mesh_scene_status_cb_t; + +typedef struct { + u8_t status_code; /* Status code for the previous operation */ + u16_t current_scene; /* Scene Number of the current scene */ + struct net_buf_simple *scenes; /* A list of scenes stored within an element */ +} esp_ble_mesh_scene_register_status_cb_t; + +typedef struct { + u16_t schedules; /* Bit field indicating defined Actions in the Schedule Register */ +} esp_ble_mesh_scheduler_status_cb_t; + +typedef struct { + u64_t index : 4; /* Enumerates (selects) a Schedule Register entry */ + u64_t year : 7; /* Scheduled year for the action */ + u64_t month : 12; /* Scheduled month for the action */ + u64_t day : 5; /* Scheduled day of the month for the action */ + u64_t hour : 5; /* Scheduled hour for the action */ + u64_t minute : 6; /* Scheduled minute for the action */ + u64_t second : 6; /* Scheduled second for the action */ + u64_t day_of_week : 7; /* Schedule days of the week for the action */ + u64_t action : 4; /* Action to be performed at the scheduled time */ + u64_t trans_time : 8; /* Transition time for this action */ + u16_t scene_number; /* Transition time for this action */ +} esp_ble_mesh_scheduler_act_status_cb_t; + +typedef union { + esp_ble_mesh_time_status_cb_t time_status; /*!< For ESP_BLE_MESH_MODEL_OP_TIME_STATUS */ + esp_ble_mesh_time_zone_status_cb_t time_zone_status; /*!< For ESP_BLE_MESH_MODEL_OP_TIME_ZONE_STATUS */ + esp_ble_mesh_tai_utc_delta_status_cb_t tai_utc_delta_status; /*!< For ESP_BLE_MESH_MODEL_OP_TAI_UTC_DELTA_STATUS */ + esp_ble_mesh_time_role_status_cb_t time_role_status; /*!< For ESP_BLE_MESH_MODEL_OP_TIME_ROLE_STATUS */ + esp_ble_mesh_scene_status_cb_t scene_status; /*!< For ESP_BLE_MESH_MODEL_OP_SCENE_STATUS */ + esp_ble_mesh_scene_register_status_cb_t scene_register_status; /*!< For ESP_BLE_MESH_MODEL_OP_SCENE_REGISTER_STATUS */ + esp_ble_mesh_scheduler_status_cb_t scheduler_status; /*!< For ESP_BLE_MESH_MODEL_OP_SCHEDULER_STATUS */ + esp_ble_mesh_scheduler_act_status_cb_t scheduler_act_status; /*!< For ESP_BLE_MESH_MODEL_OP_SCHEDULER_ACT_STATUS */ +} esp_ble_mesh_time_scene_client_status_cb_t; + +typedef struct { + int error_code; /*!< Appropriate error code */ + esp_ble_mesh_client_common_param_t *params; /*!< The client common parameters. */ + esp_ble_mesh_time_scene_client_status_cb_t status_cb; /*!< The scene status message callback values */ +} esp_ble_mesh_time_scene_client_cb_param_t; + +typedef enum { + ESP_BLE_MESH_TIME_SCENE_CLIENT_GET_STATE_EVT, + ESP_BLE_MESH_TIME_SCENE_CLIENT_SET_STATE_EVT, + ESP_BLE_MESH_TIME_SCENE_CLIENT_PUBLISH_EVT, + ESP_BLE_MESH_TIME_SCENE_CLIENT_TIMEOUT_EVT, + ESP_BLE_MESH_TIME_SCENE_CLIENT_EVT_MAX, +} esp_ble_mesh_time_scene_client_cb_event_t; + +/** + * @brief Bluetooth Mesh Time Scene Client Model function. + */ + +/** @brief: event, event code of Time Scene Client Model events; param, parameters of Time Scene Client Model events */ +typedef void (* esp_ble_mesh_time_scene_client_cb_t)(esp_ble_mesh_time_scene_client_cb_event_t event, + esp_ble_mesh_time_scene_client_cb_param_t *param); + +/** + * @brief Register BLE Mesh Time Scene Client Model callback. + * + * @param[in] callback: Pointer to the callback function. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_register_time_scene_client_callback(esp_ble_mesh_time_scene_client_cb_t callback); + +/** + * @brief Get the value of Time Scene Server Model states using the Time Scene Client Model get messages. + * + * @note If you want to know the opcodes and corresponding meanings accepted by this API, + * please refer to (@ref esp_ble_mesh_time_scene_message_opcode_t). + * + * @param[in] params: Pointer to BLE Mesh common client parameters. + * @param[in] get_state: Pointer to time scene get message value. + * Shall not be set to NULL. + * + * @return ESP_OK on success or error code otherwise. + */ +esp_err_t esp_ble_mesh_time_scene_client_get_state(esp_ble_mesh_client_common_param_t *params, + esp_ble_mesh_time_scene_client_get_state_t *get_state); + +/** + * @brief Set the value of Time Scene Server Model states using the Time Scene Client Model set messages. + * + * @note If you want to know the opcodes and corresponding meanings accepted by this API, + * please refer to (@ref esp_ble_mesh_time_scene_message_opcode_t). + * + * @param[in] params: Pointer to BLE Mesh common client parameters. + * @param[in] set_state: Pointer to time scene set message value. + * Shall not be set to NULL. + * + * @return ESP_OK on success or error code otherwise. + */ +esp_err_t esp_ble_mesh_time_scene_client_set_state(esp_ble_mesh_client_common_param_t *params, + esp_ble_mesh_time_scene_client_set_state_t *set_state); + +#endif /* _ESP_BLE_MESH_TIME_SCENE_MODEL_API_H_ */ + diff --git a/components/bt/ble_mesh/btc/btc_ble_mesh_config_model.c b/components/bt/ble_mesh/btc/btc_ble_mesh_config_model.c new file mode 100644 index 0000000000..7fe0e0595d --- /dev/null +++ b/components/bt/ble_mesh/btc/btc_ble_mesh_config_model.c @@ -0,0 +1,725 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 + +#include "btc/btc_manage.h" +#include "osi/allocator.h" + +#include "cfg_cli.h" +#include "mesh_common.h" +#include "btc_ble_mesh_config_model.h" +#include "esp_ble_mesh_config_model_api.h" + +#define CID_NVAL 0xffff + +extern s32_t config_msg_timeout; + +static inline void btc_ble_mesh_cfg_client_cb_to_app(esp_ble_mesh_cfg_client_cb_event_t event, + esp_ble_mesh_cfg_client_cb_param_t *param) +{ + esp_ble_mesh_cfg_client_cb_t btc_mesh_cb = (esp_ble_mesh_cfg_client_cb_t)btc_profile_cb_get(BTC_PID_CFG_CLIENT); + if (btc_mesh_cb) { + btc_mesh_cb(event, param); + } +} + +static inline void btc_ble_mesh_cfg_server_cb_to_app(esp_ble_mesh_cfg_server_cb_event_t event, + esp_ble_mesh_cfg_server_cb_param_t *param) +{ + esp_ble_mesh_cfg_server_cb_t btc_mesh_cb = (esp_ble_mesh_cfg_server_cb_t)btc_profile_cb_get(BTC_PID_CFG_SERVER); + if (btc_mesh_cb) { + btc_mesh_cb(event, param); + } +} + +void btc_ble_mesh_cfg_client_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) +{ + btc_ble_mesh_cfg_client_args_t *dst = (btc_ble_mesh_cfg_client_args_t *)p_dest; + btc_ble_mesh_cfg_client_args_t *src = (btc_ble_mesh_cfg_client_args_t *)p_src; + + if (!msg || !dst || !src) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + switch (msg->act) { + case BTC_BLE_MESH_ACT_CONFIG_CLIENT_GET_STATE: { + dst->cfg_client_get_state.params = (esp_ble_mesh_client_common_param_t *)osi_malloc(sizeof(esp_ble_mesh_client_common_param_t)); + dst->cfg_client_get_state.get_state = (esp_ble_mesh_cfg_client_get_state_t *)osi_malloc(sizeof(esp_ble_mesh_cfg_client_get_state_t)); + if (dst->cfg_client_get_state.params && dst->cfg_client_get_state.get_state) { + memcpy(dst->cfg_client_get_state.params, src->cfg_client_get_state.params, + sizeof(esp_ble_mesh_client_common_param_t)); + memcpy(dst->cfg_client_get_state.get_state, src->cfg_client_get_state.get_state, + sizeof(esp_ble_mesh_cfg_client_get_state_t)); + } else { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + } + break; + } + case BTC_BLE_MESH_ACT_CONFIG_CLIENT_SET_STATE: { + dst->cfg_client_set_state.params = (esp_ble_mesh_client_common_param_t *)osi_malloc(sizeof(esp_ble_mesh_client_common_param_t)); + dst->cfg_client_set_state.set_state = (esp_ble_mesh_cfg_client_set_state_t *)osi_malloc(sizeof(esp_ble_mesh_cfg_client_set_state_t)); + if (dst->cfg_client_set_state.params && dst->cfg_client_set_state.set_state) { + memcpy(dst->cfg_client_set_state.params, src->cfg_client_set_state.params, + sizeof(esp_ble_mesh_client_common_param_t)); + memcpy(dst->cfg_client_set_state.set_state, src->cfg_client_set_state.set_state, + sizeof(esp_ble_mesh_cfg_client_set_state_t)); + } else { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + } + break; + } + default: + LOG_DEBUG("%s, Unknown deep copy act %d", __func__, msg->act); + break; + } +} + +static void btc_ble_mesh_cfg_client_copy_req_data(btc_msg_t *msg, void *p_dest, void *p_src) +{ + esp_ble_mesh_cfg_client_cb_param_t *p_dest_data = (esp_ble_mesh_cfg_client_cb_param_t *)p_dest; + esp_ble_mesh_cfg_client_cb_param_t *p_src_data = (esp_ble_mesh_cfg_client_cb_param_t *)p_src; + u32_t opcode; + u16_t length; + + if (!msg || !p_src_data || !p_dest_data) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + switch (msg->act) { + case ESP_BLE_MESH_CFG_CLIENT_GET_STATE_EVT: + case ESP_BLE_MESH_CFG_CLIENT_SET_STATE_EVT: + case ESP_BLE_MESH_CFG_CLIENT_PUBLISH_EVT: + if (p_src_data->params) { + opcode = p_src_data->params->opcode; + switch (opcode) { + case OP_DEV_COMP_DATA_GET: + case OP_DEV_COMP_DATA_STATUS: + if (p_src_data->status_cb.comp_data_status.composition_data) { + length = p_src_data->status_cb.comp_data_status.composition_data->len; + p_dest_data->status_cb.comp_data_status.composition_data = bt_mesh_alloc_buf(length); + if (!p_dest_data->status_cb.comp_data_status.composition_data) { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + return; + } + net_buf_simple_add_mem(p_dest_data->status_cb.comp_data_status.composition_data, + p_src_data->status_cb.comp_data_status.composition_data->data, + p_src_data->status_cb.comp_data_status.composition_data->len); + } + break; + case OP_MOD_SUB_GET: + case OP_MOD_SUB_GET_VND: + case OP_MOD_SUB_LIST: + case OP_MOD_SUB_LIST_VND: + if (p_src_data->status_cb.model_sub_list.sub_addr) { + length = p_src_data->status_cb.model_sub_list.sub_addr->len; + p_dest_data->status_cb.model_sub_list.sub_addr = bt_mesh_alloc_buf(length); + if (!p_dest_data->status_cb.model_sub_list.sub_addr) { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + return; + } + net_buf_simple_add_mem(p_dest_data->status_cb.model_sub_list.sub_addr, + p_src_data->status_cb.model_sub_list.sub_addr->data, + p_src_data->status_cb.model_sub_list.sub_addr->len); + } + break; + case OP_NET_KEY_GET: + case OP_NET_KEY_LIST: + if (p_src_data->status_cb.netkey_list.net_idx) { + length = p_src_data->status_cb.netkey_list.net_idx->len; + p_dest_data->status_cb.netkey_list.net_idx = bt_mesh_alloc_buf(length); + if (!p_dest_data->status_cb.netkey_list.net_idx) { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + return; + } + net_buf_simple_add_mem(p_dest_data->status_cb.netkey_list.net_idx, + p_src_data->status_cb.netkey_list.net_idx->data, + p_src_data->status_cb.netkey_list.net_idx->len); + } + break; + case OP_APP_KEY_GET: + case OP_APP_KEY_LIST: + if (p_src_data->status_cb.appkey_list.app_idx) { + length = p_src_data->status_cb.appkey_list.app_idx->len; + p_dest_data->status_cb.appkey_list.app_idx = bt_mesh_alloc_buf(length); + if (!p_dest_data->status_cb.appkey_list.app_idx) { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + return; + } + net_buf_simple_add_mem(p_dest_data->status_cb.appkey_list.app_idx, + p_src_data->status_cb.appkey_list.app_idx->data, + p_src_data->status_cb.appkey_list.app_idx->len); + } + break; + case OP_SIG_MOD_APP_GET: + case OP_VND_MOD_APP_GET: + case OP_SIG_MOD_APP_LIST: + case OP_VND_MOD_APP_LIST: + if (p_src_data->status_cb.model_app_list.app_idx) { + length = p_src_data->status_cb.model_app_list.app_idx->len; + p_dest_data->status_cb.model_app_list.app_idx = bt_mesh_alloc_buf(length); + if (!p_dest_data->status_cb.model_app_list.app_idx) { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + return; + } + net_buf_simple_add_mem(p_dest_data->status_cb.model_app_list.app_idx, + p_src_data->status_cb.model_app_list.app_idx->data, + p_src_data->status_cb.model_app_list.app_idx->len); + } + break; + default: + break; + } + } + case ESP_BLE_MESH_CFG_CLIENT_TIMEOUT_EVT: + if (p_src_data->params) { + p_dest_data->params = osi_malloc(sizeof(esp_ble_mesh_client_common_param_t)); + if (p_dest_data->params) { + memcpy(p_dest_data->params, p_src_data->params, sizeof(esp_ble_mesh_client_common_param_t)); + } else { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + } + } + break; + default: + break; + } +} + +static void btc_ble_mesh_cfg_client_free_req_data(btc_msg_t *msg) +{ + esp_ble_mesh_cfg_client_cb_param_t *arg = NULL; + u32_t opcode; + + if (!msg || !msg->arg) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + arg = (esp_ble_mesh_cfg_client_cb_param_t *)(msg->arg); + + switch (msg->act) { + case ESP_BLE_MESH_CFG_CLIENT_GET_STATE_EVT: + case ESP_BLE_MESH_CFG_CLIENT_SET_STATE_EVT: + case ESP_BLE_MESH_CFG_CLIENT_PUBLISH_EVT: + if (arg->params) { + opcode = arg->params->opcode; + switch (opcode) { + case OP_DEV_COMP_DATA_GET: + case OP_DEV_COMP_DATA_STATUS: + bt_mesh_free_buf(arg->status_cb.comp_data_status.composition_data); + break; + case OP_MOD_SUB_GET: + case OP_MOD_SUB_GET_VND: + case OP_MOD_SUB_LIST: + case OP_MOD_SUB_LIST_VND: + bt_mesh_free_buf(arg->status_cb.model_sub_list.sub_addr); + break; + case OP_NET_KEY_GET: + case OP_NET_KEY_LIST: + bt_mesh_free_buf(arg->status_cb.netkey_list.net_idx); + break; + case OP_APP_KEY_GET: + case OP_APP_KEY_LIST: + bt_mesh_free_buf(arg->status_cb.appkey_list.app_idx); + break; + case OP_SIG_MOD_APP_GET: + case OP_VND_MOD_APP_GET: + case OP_SIG_MOD_APP_LIST: + case OP_VND_MOD_APP_LIST: + bt_mesh_free_buf(arg->status_cb.model_app_list.app_idx); + break; + default: + break; + } + } + case ESP_BLE_MESH_CFG_CLIENT_TIMEOUT_EVT: + if (arg->params) { + osi_free(arg->params); + } + break; + default: + break; + } +} + +void btc_ble_mesh_cfg_client_arg_deep_free(btc_msg_t *msg) +{ + btc_ble_mesh_cfg_client_args_t *arg = NULL; + + if (!msg || !msg->arg) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + arg = (btc_ble_mesh_cfg_client_args_t *)(msg->arg); + + switch (msg->act) { + case BTC_BLE_MESH_ACT_CONFIG_CLIENT_GET_STATE: + if (arg->cfg_client_get_state.params) { + osi_free(arg->cfg_client_get_state.params); + } + if (arg->cfg_client_get_state.get_state) { + osi_free(arg->cfg_client_get_state.get_state); + } + break; + case BTC_BLE_MESH_ACT_CONFIG_CLIENT_SET_STATE: + if (arg->cfg_client_set_state.params) { + osi_free(arg->cfg_client_set_state.params); + } + if (arg->cfg_client_set_state.set_state) { + osi_free(arg->cfg_client_set_state.set_state); + } + break; + default: + break; + } + + return; +} + +static void btc_mesh_cfg_client_callback(esp_ble_mesh_cfg_client_cb_param_t *cb_params, uint8_t act) +{ + btc_msg_t msg = {0}; + + LOG_DEBUG("%s", __func__); + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_CFG_CLIENT; + msg.act = act; + + btc_transfer_context(&msg, cb_params, + sizeof(esp_ble_mesh_cfg_client_cb_param_t), btc_ble_mesh_cfg_client_copy_req_data); +} + +void bt_mesh_callback_config_status_to_btc(u32_t opcode, u8_t evt_type, + struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + const u8_t *val, size_t len) +{ + esp_ble_mesh_cfg_client_cb_param_t cb_params = {0}; + esp_ble_mesh_client_common_param_t params = {0}; + size_t length; + uint8_t act; + + if (!model || !ctx) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + switch (evt_type) { + case 0x00: + act = ESP_BLE_MESH_CFG_CLIENT_GET_STATE_EVT; + break; + case 0x01: + act = ESP_BLE_MESH_CFG_CLIENT_SET_STATE_EVT; + break; + case 0x02: + act = ESP_BLE_MESH_CFG_CLIENT_PUBLISH_EVT; + break; + case 0x03: + act = ESP_BLE_MESH_CFG_CLIENT_TIMEOUT_EVT; + break; + default: + LOG_ERROR("%s, Unknown config client event type %d", __func__, evt_type); + return; + } + + params.opcode = opcode; + params.model = (esp_ble_mesh_model_t *)model; + params.ctx.net_idx = ctx->net_idx; + params.ctx.app_idx = ctx->app_idx; + params.ctx.addr = ctx->addr; + params.ctx.recv_ttl = ctx->recv_ttl; + params.ctx.recv_op = ctx->recv_op; + params.ctx.recv_dst = ctx->recv_dst; + + cb_params.error_code = 0; + cb_params.params = ¶ms; + + if (val && len) { + length = (len <= sizeof(cb_params.status_cb)) ? len : sizeof(cb_params.status_cb); + memcpy(&cb_params.status_cb, val, length); + } + + btc_mesh_cfg_client_callback(&cb_params, act); +} + + +void btc_mesh_cfg_client_publish_callback(u32_t opcode, struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) +{ + if (!model || !ctx || !buf) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + bt_mesh_callback_config_status_to_btc(opcode, 0x02, model, ctx, buf->data, buf->len); +} + +void btc_mesh_cfg_client_call_handler(btc_msg_t *msg) +{ + esp_ble_mesh_cfg_client_cb_param_t cfg_client_cb = {0}; + btc_ble_mesh_cfg_client_args_t *arg = NULL; + bt_mesh_role_param_t role_param = {0}; + + if (!msg || !msg->arg) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + arg = (btc_ble_mesh_cfg_client_args_t *)(msg->arg); + + switch (msg->act) { + case BTC_BLE_MESH_ACT_CONFIG_CLIENT_GET_STATE: { + cfg_client_cb.params = arg->cfg_client_get_state.params; + role_param.model = (struct bt_mesh_model *)cfg_client_cb.params->model; + role_param.role = cfg_client_cb.params->msg_role; + if (bt_mesh_set_model_role(&role_param)) { + LOG_ERROR("%s, Failed to set model role", __func__); + return; + } + btc_ble_mesh_config_client_get_state(arg->cfg_client_get_state.params, + arg->cfg_client_get_state.get_state, + &cfg_client_cb); + if (cfg_client_cb.error_code) { + btc_mesh_cfg_client_callback(&cfg_client_cb, ESP_BLE_MESH_CFG_CLIENT_GET_STATE_EVT); + } + break; + } + case BTC_BLE_MESH_ACT_CONFIG_CLIENT_SET_STATE: { + cfg_client_cb.params = arg->cfg_client_set_state.params; + role_param.model = (struct bt_mesh_model *)cfg_client_cb.params->model; + role_param.role = cfg_client_cb.params->msg_role; + if (bt_mesh_set_model_role(&role_param)) { + LOG_ERROR("%s, Failed to set model role", __func__); + return; + } + btc_ble_mesh_config_client_set_state(arg->cfg_client_set_state.params, + arg->cfg_client_set_state.set_state, + &cfg_client_cb); + if (cfg_client_cb.error_code) { + btc_mesh_cfg_client_callback(&cfg_client_cb, ESP_BLE_MESH_CFG_CLIENT_SET_STATE_EVT); + } + break; + } + default: + break; + } + + btc_ble_mesh_cfg_client_arg_deep_free(msg); +} + +void btc_mesh_cfg_client_cb_handler(btc_msg_t *msg) +{ + esp_ble_mesh_cfg_client_cb_param_t *param = NULL; + + if (!msg || !msg->arg) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + param = (esp_ble_mesh_cfg_client_cb_param_t *)(msg->arg); + + if (msg->act < ESP_BLE_MESH_CFG_CLIENT_EVT_MAX) { + btc_ble_mesh_cfg_client_cb_to_app(msg->act, param); + } else { + LOG_ERROR("%s, Unknown msg->act = %d", __func__, msg->act); + } + + btc_ble_mesh_cfg_client_free_req_data(msg); +} + +int btc_ble_mesh_config_client_get_state(esp_ble_mesh_client_common_param_t *params, + esp_ble_mesh_cfg_client_get_state_t *get_state, + esp_ble_mesh_cfg_client_cb_param_t *cfg_client_cb) +{ + struct bt_mesh_msg_ctx ctx = {0}; + + if (!params || !cfg_client_cb) { + LOG_ERROR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + ctx.net_idx = params->ctx.net_idx; + ctx.app_idx = BLE_MESH_KEY_DEV; + ctx.addr = params->ctx.addr; + ctx.send_rel = params->ctx.send_rel; + ctx.send_ttl = params->ctx.send_ttl; + + config_msg_timeout = params->msg_timeout; + + switch (params->opcode) { + case ESP_BLE_MESH_MODEL_OP_BEACON_GET: + return (cfg_client_cb->error_code = bt_mesh_cfg_beacon_get(&ctx)); + case ESP_BLE_MESH_MODEL_OP_DEFAULT_TTL_GET: + return (cfg_client_cb->error_code = bt_mesh_cfg_ttl_get(&ctx)); + case ESP_BLE_MESH_MODEL_OP_FRIEND_GET: + return (cfg_client_cb->error_code = bt_mesh_cfg_friend_get(&ctx)); + case ESP_BLE_MESH_MODEL_OP_GATT_PROXY_GET: + return (cfg_client_cb->error_code = bt_mesh_cfg_gatt_proxy_get(&ctx)); + case ESP_BLE_MESH_MODEL_OP_RELAY_GET: + return (cfg_client_cb->error_code = bt_mesh_cfg_relay_get(&ctx)); + case ESP_BLE_MESH_MODEL_OP_MODEL_PUB_GET: + return (cfg_client_cb->error_code = + bt_mesh_cfg_mod_pub_get(&ctx, get_state->model_pub_get.element_addr, get_state->model_pub_get.model_id, + get_state->model_pub_get.company_id)); + case ESP_BLE_MESH_MODEL_OP_HEARTBEAT_PUB_GET: + return (cfg_client_cb->error_code = bt_mesh_cfg_hb_pub_get(&ctx)); + case ESP_BLE_MESH_MODEL_OP_HEARTBEAT_SUB_GET: + return (cfg_client_cb->error_code = bt_mesh_cfg_hb_sub_get(&ctx)); + case ESP_BLE_MESH_MODEL_OP_COMPOSITION_DATA_GET: + return (cfg_client_cb->error_code = bt_mesh_cfg_comp_data_get(&ctx, get_state->comp_data_get.page)); + case ESP_BLE_MESH_MODEL_OP_SIG_MODEL_SUB_GET: + return (cfg_client_cb->error_code = + bt_mesh_cfg_mod_sub_get(&ctx, get_state->sig_model_sub_get.element_addr, get_state->sig_model_sub_get.model_id)); + case ESP_BLE_MESH_MODEL_OP_VENDOR_MODEL_SUB_GET: + return (cfg_client_cb->error_code = + bt_mesh_cfg_mod_sub_get_vnd(&ctx, get_state->vnd_model_sub_get.element_addr, + get_state->vnd_model_sub_get.model_id, get_state->vnd_model_sub_get.company_id)); + case ESP_BLE_MESH_MODEL_OP_NET_KEY_GET: + return (cfg_client_cb->error_code = bt_mesh_cfg_net_key_get(&ctx)); + case ESP_BLE_MESH_MODEL_OP_APP_KEY_GET: + return (cfg_client_cb->error_code = bt_mesh_cfg_app_key_get(&ctx, get_state->app_key_get.net_idx)); + case ESP_BLE_MESH_MODEL_OP_NODE_IDENTITY_GET: + return (cfg_client_cb->error_code = bt_mesh_cfg_node_identity_get(&ctx, get_state->node_identity_get.net_idx)); + case ESP_BLE_MESH_MODEL_OP_SIG_MODEL_APP_GET: + return (cfg_client_cb->error_code = + bt_mesh_cfg_mod_app_get(&ctx, get_state->sig_model_app_get.element_addr, get_state->sig_model_app_get.model_id)); + case ESP_BLE_MESH_MODEL_OP_VENDOR_MODEL_APP_GET: + return (cfg_client_cb->error_code = + bt_mesh_cfg_mod_app_get_vnd(&ctx, get_state->vnd_model_app_get.element_addr, + get_state->vnd_model_app_get.model_id, get_state->vnd_model_app_get.company_id)); + case ESP_BLE_MESH_MODEL_OP_KEY_REFRESH_PHASE_GET: + return (cfg_client_cb->error_code = bt_mesh_cfg_kr_phase_get(&ctx, get_state->kr_phase_get.net_idx)); + case ESP_BLE_MESH_MODEL_OP_LPN_POLLTIMEOUT_GET: + return (cfg_client_cb->error_code = bt_mesh_cfg_lpn_timeout_get(&ctx, get_state->lpn_pollto_get.lpn_addr)); + case ESP_BLE_MESH_MODEL_OP_NETWORK_TRANSMIT_GET: + return (cfg_client_cb->error_code = bt_mesh_cfg_net_transmit_get(&ctx)); + default: + BT_WARN("%s, Invalid opcode 0x%x", __func__, params->opcode); + return (cfg_client_cb->error_code = -EINVAL); + } + + return 0; +} + +int btc_ble_mesh_config_client_set_state(esp_ble_mesh_client_common_param_t *params, + esp_ble_mesh_cfg_client_set_state_t *set_state, + esp_ble_mesh_cfg_client_cb_param_t *cfg_client_cb) +{ + struct bt_mesh_msg_ctx ctx = {0}; + + if (!params || !set_state || !cfg_client_cb) { + LOG_ERROR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + ctx.net_idx = params->ctx.net_idx; + ctx.app_idx = BLE_MESH_KEY_DEV; + ctx.addr = params->ctx.addr; + ctx.send_rel = params->ctx.send_rel; + ctx.send_ttl = params->ctx.send_ttl; + + config_msg_timeout = params->msg_timeout; + + switch (params->opcode) { + case ESP_BLE_MESH_MODEL_OP_BEACON_SET: + return (cfg_client_cb->error_code = bt_mesh_cfg_beacon_set(&ctx, set_state->beacon_set.beacon)); + case ESP_BLE_MESH_MODEL_OP_DEFAULT_TTL_SET: + return (cfg_client_cb->error_code = bt_mesh_cfg_ttl_set(&ctx, set_state->default_ttl_set.ttl)); + case ESP_BLE_MESH_MODEL_OP_FRIEND_SET: + return (cfg_client_cb->error_code = bt_mesh_cfg_friend_set(&ctx, set_state->friend_set.friend_state)); + case ESP_BLE_MESH_MODEL_OP_GATT_PROXY_SET: + return (cfg_client_cb->error_code = bt_mesh_cfg_gatt_proxy_set(&ctx, set_state->gatt_proxy_set.gatt_proxy)); + case ESP_BLE_MESH_MODEL_OP_RELAY_SET: + return (cfg_client_cb->error_code = + bt_mesh_cfg_relay_set(&ctx, set_state->relay_set.relay, set_state->relay_set.relay_retransmit)); + case ESP_BLE_MESH_MODEL_OP_NET_KEY_ADD: + return (cfg_client_cb->error_code = + bt_mesh_cfg_net_key_add(&ctx, set_state->net_key_add.net_idx, &set_state->net_key_add.net_key[0])); + case ESP_BLE_MESH_MODEL_OP_APP_KEY_ADD: + return (cfg_client_cb->error_code = + bt_mesh_cfg_app_key_add(&ctx, set_state->app_key_add.net_idx, + set_state->app_key_add.app_idx, &set_state->app_key_add.app_key[0])); + case ESP_BLE_MESH_MODEL_OP_MODEL_APP_BIND: + return (cfg_client_cb->error_code = + bt_mesh_cfg_mod_app_bind(&ctx, set_state->model_app_bind.element_addr, set_state->model_app_bind.model_app_idx, + set_state->model_app_bind.model_id, set_state->model_app_bind.company_id)); + case ESP_BLE_MESH_MODEL_OP_MODEL_PUB_SET: { + struct bt_mesh_cfg_mod_pub model_pub = { + .addr = set_state->model_pub_set.publish_addr, + .app_idx = set_state->model_pub_set.publish_app_idx, + .cred_flag = set_state->model_pub_set.cred_flag, + .ttl = set_state->model_pub_set.publish_ttl, + .period = set_state->model_pub_set.publish_period, + .transmit = set_state->model_pub_set.publish_retransmit, + }; + return (cfg_client_cb->error_code = + bt_mesh_cfg_mod_pub_set(&ctx, set_state->model_pub_set.element_addr, set_state->model_pub_set.model_id, + set_state->model_pub_set.company_id, &model_pub)); + } + case ESP_BLE_MESH_MODEL_OP_MODEL_SUB_ADD: + return (cfg_client_cb->error_code = + bt_mesh_cfg_mod_sub_add(&ctx, set_state->model_sub_add.element_addr, set_state->model_sub_add.sub_addr, + set_state->model_sub_add.model_id, set_state->model_sub_add.company_id)); + case ESP_BLE_MESH_MODEL_OP_MODEL_SUB_DELETE: + return (cfg_client_cb->error_code = + bt_mesh_cfg_mod_sub_del(&ctx, set_state->model_sub_delete.element_addr, set_state->model_sub_delete.sub_addr, + set_state->model_sub_delete.model_id, set_state->model_sub_delete.company_id)); + case ESP_BLE_MESH_MODEL_OP_MODEL_SUB_OVERWRITE: + return (cfg_client_cb->error_code = + bt_mesh_cfg_mod_sub_overwrite(&ctx, set_state->model_sub_overwrite.element_addr, set_state->model_sub_overwrite.sub_addr, + set_state->model_sub_overwrite.model_id, set_state->model_sub_overwrite.company_id)); + case ESP_BLE_MESH_MODEL_OP_MODEL_SUB_VIRTUAL_ADDR_ADD: + return (cfg_client_cb->error_code = + bt_mesh_cfg_mod_sub_va_add(&ctx, set_state->model_sub_va_add.element_addr, &set_state->model_sub_va_add.label_uuid[0], + set_state->model_sub_va_add.model_id, set_state->model_sub_va_add.company_id)); + case ESP_BLE_MESH_MODEL_OP_MODEL_SUB_VIRTUAL_ADDR_OVERWRITE: + return (cfg_client_cb->error_code = + bt_mesh_cfg_mod_sub_va_overwrite(&ctx, set_state->model_sub_va_overwrite.element_addr, &set_state->model_sub_va_overwrite.label_uuid[0], + set_state->model_sub_va_overwrite.model_id, set_state->model_sub_va_overwrite.company_id)); + case ESP_BLE_MESH_MODEL_OP_MODEL_SUB_VIRTUAL_ADDR_DELETE: + return (cfg_client_cb->error_code = + bt_mesh_cfg_mod_sub_va_del(&ctx, set_state->model_sub_va_delete.element_addr, &set_state->model_sub_va_delete.label_uuid[0], + set_state->model_sub_va_delete.model_id, set_state->model_sub_va_delete.company_id)); + case ESP_BLE_MESH_MODEL_OP_HEARTBEAT_SUB_SET: + return (cfg_client_cb->error_code = + bt_mesh_cfg_hb_sub_set(&ctx, (struct bt_mesh_cfg_hb_sub *)&set_state->heartbeat_sub_set)); + case ESP_BLE_MESH_MODEL_OP_HEARTBEAT_PUB_SET: + return (cfg_client_cb->error_code = + bt_mesh_cfg_hb_pub_set(&ctx, (const struct bt_mesh_cfg_hb_pub *)&set_state->heartbeat_pub_set)); + case ESP_BLE_MESH_MODEL_OP_NODE_RESET: + return (cfg_client_cb->error_code = bt_mesh_cfg_node_reset(&ctx)); + case ESP_BLE_MESH_MODEL_OP_MODEL_PUB_VIRTUAL_ADDR_SET: { + struct bt_mesh_cfg_mod_pub model_pub = { + .app_idx = set_state->model_pub_va_set.publish_app_idx, + .cred_flag = set_state->model_pub_va_set.cred_flag, + .ttl = set_state->model_pub_va_set.publish_ttl, + .period = set_state->model_pub_va_set.publish_period, + .transmit = set_state->model_pub_va_set.publish_retransmit, + }; + return (cfg_client_cb->error_code = + bt_mesh_cfg_mod_pub_va_set(&ctx, set_state->model_pub_va_set.element_addr, set_state->model_pub_va_set.model_id, + set_state->model_pub_va_set.company_id, set_state->model_pub_va_set.label_uuid, &model_pub)); + } + case ESP_BLE_MESH_MODEL_OP_MODEL_SUB_DELETE_ALL: + return (cfg_client_cb->error_code = + bt_mesh_cfg_mod_sub_del_all(&ctx, set_state->model_sub_delete_all.element_addr, + set_state->model_sub_delete_all.model_id, set_state->model_sub_delete_all.company_id)); + case ESP_BLE_MESH_MODEL_OP_NET_KEY_UPDATE: + return (cfg_client_cb->error_code = + bt_mesh_cfg_net_key_update(&ctx, set_state->net_key_update.net_idx, set_state->net_key_update.net_key)); + case ESP_BLE_MESH_MODEL_OP_NET_KEY_DELETE: + return (cfg_client_cb->error_code = + bt_mesh_cfg_net_key_delete(&ctx, set_state->net_key_delete.net_idx)); + case ESP_BLE_MESH_MODEL_OP_APP_KEY_UPDATE: + return (cfg_client_cb->error_code = + bt_mesh_cfg_app_key_update(&ctx, set_state->app_key_update.net_idx, set_state->app_key_update.app_idx, + set_state->app_key_update.app_key)); + case ESP_BLE_MESH_MODEL_OP_APP_KEY_DELETE: + return (cfg_client_cb->error_code = + bt_mesh_cfg_app_key_delete(&ctx, set_state->app_key_delete.net_idx, set_state->app_key_delete.app_idx)); + case ESP_BLE_MESH_MODEL_OP_NODE_IDENTITY_SET: + return (cfg_client_cb->error_code = + bt_mesh_cfg_node_identity_set(&ctx, set_state->node_identity_set.net_idx, set_state->node_identity_set.identity)); + case ESP_BLE_MESH_MODEL_OP_MODEL_APP_UNBIND: + return (cfg_client_cb->error_code = + bt_mesh_cfg_mod_app_unbind(&ctx, set_state->model_app_unbind.element_addr, set_state->model_app_unbind.model_app_idx, + set_state->model_app_unbind.model_id, set_state->model_app_unbind.company_id)); + case ESP_BLE_MESH_MODEL_OP_KEY_REFRESH_PHASE_SET: + return (cfg_client_cb->error_code = + bt_mesh_cfg_kr_phase_set(&ctx, set_state->kr_phase_set.net_idx, set_state->kr_phase_set.transition)); + case ESP_BLE_MESH_MODEL_OP_NETWORK_TRANSMIT_SET: + return (cfg_client_cb->error_code = + bt_mesh_cfg_net_transmit_set(&ctx, set_state->net_transmit_set.net_transmit)); + default: + BT_WARN("%s, Invalid opcode 0x%x", __func__, params->opcode); + return (cfg_client_cb->error_code = -EINVAL); + } + + return 0; +} + +static void btc_mesh_cfg_server_callback(esp_ble_mesh_cfg_server_cb_param_t *cb_params, uint8_t act) +{ + btc_msg_t msg = {0}; + + LOG_DEBUG("%s", __func__); + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_CFG_SERVER; + msg.act = act; + + btc_transfer_context(&msg, cb_params, sizeof(esp_ble_mesh_cfg_server_cb_param_t), NULL); +} + +void bt_mesh_callback_cfg_server_event_to_btc(u8_t evt_type, struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + const u8_t *val, size_t len) +{ + esp_ble_mesh_cfg_server_cb_param_t cb_params = {0}; + size_t length; + uint8_t act; + + if (!model || !ctx) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + switch (evt_type) { + case 0x00: + act = ESP_BLE_MESH_CFG_SERVER_RECV_MSG_EVT; + break; + default: + LOG_ERROR("%s, Unknown config server event type %d", __func__, evt_type); + return; + } + + cb_params.model = (esp_ble_mesh_model_t *)model; + cb_params.ctx.net_idx = ctx->net_idx; + cb_params.ctx.app_idx = ctx->app_idx; + cb_params.ctx.addr = ctx->addr; + cb_params.ctx.recv_ttl = ctx->recv_ttl; + cb_params.ctx.recv_op = ctx->recv_op; + cb_params.ctx.recv_dst = ctx->recv_dst; + + if (val && len) { + length = (len <= sizeof(cb_params.status_cb)) ? len : sizeof(cb_params.status_cb); + memcpy(&cb_params.status_cb, val, length); + } + + btc_mesh_cfg_server_callback(&cb_params, act); +} + +void btc_mesh_cfg_server_cb_handler(btc_msg_t *msg) +{ + esp_ble_mesh_cfg_server_cb_param_t *param = NULL; + + if (!msg || !msg->arg) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + param = (esp_ble_mesh_cfg_server_cb_param_t *)(msg->arg); + + if (msg->act < ESP_BLE_MESH_CFG_SERVER_EVT_MAX) { + btc_ble_mesh_cfg_server_cb_to_app(msg->act, param); + } else { + LOG_ERROR("%s, Unknown msg->act = %d", __func__, msg->act); + } +} diff --git a/components/bt/ble_mesh/btc/btc_ble_mesh_generic_model.c b/components/bt/ble_mesh/btc/btc_ble_mesh_generic_model.c new file mode 100644 index 0000000000..b8fc338169 --- /dev/null +++ b/components/bt/ble_mesh/btc/btc_ble_mesh_generic_model.c @@ -0,0 +1,540 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 + +#include "btc/btc_manage.h" +#include "osi/allocator.h" + +#include "cfg_cli.h" +#include "btc_ble_mesh_generic_model.h" +#include "esp_ble_mesh_generic_model_api.h" + +static inline void btc_ble_mesh_cb_to_app(esp_ble_mesh_generic_client_cb_event_t event, + esp_ble_mesh_generic_client_cb_param_t *param) +{ + esp_ble_mesh_generic_client_cb_t btc_mesh_cb = (esp_ble_mesh_generic_client_cb_t)btc_profile_cb_get(BTC_PID_GENERIC_CLIENT); + if (btc_mesh_cb) { + btc_mesh_cb(event, param); + } +} + +void btc_ble_mesh_generic_client_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) +{ + btc_ble_mesh_generic_client_args_t *dst = (btc_ble_mesh_generic_client_args_t *)p_dest; + btc_ble_mesh_generic_client_args_t *src = (btc_ble_mesh_generic_client_args_t *)p_src; + u32_t opcode; + u16_t length; + + if (!msg || !dst || !src) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + switch (msg->act) { + case BTC_BLE_MESH_ACT_GENERIC_CLIENT_GET_STATE: { + dst->generic_client_get_state.params = (esp_ble_mesh_client_common_param_t *)osi_malloc(sizeof(esp_ble_mesh_client_common_param_t)); + dst->generic_client_get_state.get_state = (esp_ble_mesh_generic_client_get_state_t *)osi_malloc(sizeof(esp_ble_mesh_generic_client_get_state_t)); + if (dst->generic_client_get_state.params && dst->generic_client_get_state.get_state) { + memcpy(dst->generic_client_get_state.params, src->generic_client_get_state.params, + sizeof(esp_ble_mesh_client_common_param_t)); + memcpy(dst->generic_client_get_state.get_state, src->generic_client_get_state.get_state, + sizeof(esp_ble_mesh_generic_client_get_state_t)); + } else { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + } + break; + } + case BTC_BLE_MESH_ACT_GENERIC_CLIENT_SET_STATE: { + dst->generic_client_set_state.params = (esp_ble_mesh_client_common_param_t *)osi_malloc(sizeof(esp_ble_mesh_client_common_param_t)); + dst->generic_client_set_state.set_state = (esp_ble_mesh_generic_client_set_state_t *)osi_malloc(sizeof(esp_ble_mesh_generic_client_set_state_t)); + if (dst->generic_client_set_state.params && dst->generic_client_set_state.set_state) { + memcpy(dst->generic_client_set_state.params, src->generic_client_set_state.params, + sizeof(esp_ble_mesh_client_common_param_t)); + memcpy(dst->generic_client_set_state.set_state, src->generic_client_set_state.set_state, + sizeof(esp_ble_mesh_generic_client_set_state_t)); + + opcode = src->generic_client_set_state.params->opcode; + switch (opcode) { + case ESP_BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_SET: + if (src->generic_client_set_state.set_state->user_property_set.property_value) { + length = src->generic_client_set_state.set_state->user_property_set.property_value->len; + dst->generic_client_set_state.set_state->user_property_set.property_value = bt_mesh_alloc_buf(length); + if (!dst->generic_client_set_state.set_state->user_property_set.property_value) { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + return; + } + net_buf_simple_add_mem(dst->generic_client_set_state.set_state->user_property_set.property_value, + src->generic_client_set_state.set_state->user_property_set.property_value->data, + src->generic_client_set_state.set_state->user_property_set.property_value->len); + } + break; + case ESP_BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_SET: + if (src->generic_client_set_state.set_state->admin_property_set.property_value) { + length = src->generic_client_set_state.set_state->admin_property_set.property_value->len; + dst->generic_client_set_state.set_state->admin_property_set.property_value = bt_mesh_alloc_buf(length); + if (!dst->generic_client_set_state.set_state->admin_property_set.property_value) { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + return; + } + net_buf_simple_add_mem(dst->generic_client_set_state.set_state->admin_property_set.property_value, + src->generic_client_set_state.set_state->admin_property_set.property_value->data, + src->generic_client_set_state.set_state->admin_property_set.property_value->len); + } + break; + default: + break; + } + } else { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + } + break; + } + default: + LOG_DEBUG("%s, Unknown deep copy act %d", __func__, msg->act); + break; + } +} + +static void btc_ble_mesh_copy_req_data(btc_msg_t *msg, void *p_dest, void *p_src) +{ + esp_ble_mesh_generic_client_cb_param_t *p_dest_data = (esp_ble_mesh_generic_client_cb_param_t *)p_dest; + esp_ble_mesh_generic_client_cb_param_t *p_src_data = (esp_ble_mesh_generic_client_cb_param_t *)p_src; + u32_t opcode; + u16_t length; + + if (!msg || !p_src_data || !p_dest_data) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + switch (msg->act) { + case ESP_BLE_MESH_GENERIC_CLIENT_GET_STATE_EVT: + case ESP_BLE_MESH_GENERIC_CLIENT_SET_STATE_EVT: + case ESP_BLE_MESH_GENERIC_CLIENT_PUBLISH_EVT: + if (p_src_data->params) { + opcode = p_src_data->params->opcode; + switch (opcode) { + case ESP_BLE_MESH_MODEL_OP_GEN_USER_PROPERTIES_GET: + case ESP_BLE_MESH_MODEL_OP_GEN_USER_PROPERTIES_STATUS: + if (p_src_data->status_cb.user_properties_status.property_ids) { + length = p_src_data->status_cb.user_properties_status.property_ids->len; + p_dest_data->status_cb.user_properties_status.property_ids = bt_mesh_alloc_buf(length); + if (!p_dest_data->status_cb.user_properties_status.property_ids) { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + return; + } + net_buf_simple_add_mem(p_dest_data->status_cb.user_properties_status.property_ids, + p_src_data->status_cb.user_properties_status.property_ids->data, + p_src_data->status_cb.user_properties_status.property_ids->len); + } + break; + case ESP_BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_GET: + case ESP_BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_SET: + case ESP_BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_STATUS: + if (p_src_data->status_cb.user_property_status.property_value) { + length = p_src_data->status_cb.user_property_status.property_value->len; + p_dest_data->status_cb.user_property_status.property_value = bt_mesh_alloc_buf(length); + if (!p_dest_data->status_cb.user_property_status.property_value) { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + return; + } + net_buf_simple_add_mem(p_dest_data->status_cb.user_property_status.property_value, + p_src_data->status_cb.user_property_status.property_value->data, + p_src_data->status_cb.user_property_status.property_value->len); + } + break; + case ESP_BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTIES_GET: + case ESP_BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTIES_STATUS: + if (p_src_data->status_cb.admin_properties_status.property_ids) { + length = p_src_data->status_cb.admin_properties_status.property_ids->len; + p_dest_data->status_cb.admin_properties_status.property_ids = bt_mesh_alloc_buf(length); + if (!p_dest_data->status_cb.admin_properties_status.property_ids) { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + return; + } + net_buf_simple_add_mem(p_dest_data->status_cb.admin_properties_status.property_ids, + p_src_data->status_cb.admin_properties_status.property_ids->data, + p_src_data->status_cb.admin_properties_status.property_ids->len); + } + break; + case ESP_BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_GET: + case ESP_BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_SET: + case ESP_BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_STATUS: + if (p_src_data->status_cb.admin_property_status.property_value) { + length = p_src_data->status_cb.admin_property_status.property_value->len; + p_dest_data->status_cb.admin_property_status.property_value = bt_mesh_alloc_buf(length); + if (!p_dest_data->status_cb.admin_property_status.property_value) { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + return; + } + net_buf_simple_add_mem(p_dest_data->status_cb.admin_property_status.property_value, + p_src_data->status_cb.admin_property_status.property_value->data, + p_src_data->status_cb.admin_property_status.property_value->len); + } + break; + case ESP_BLE_MESH_MODEL_OP_GEN_MANUFACTURER_PROPERTIES_GET: + case ESP_BLE_MESH_MODEL_OP_GEN_MANUFACTURER_PROPERTIES_STATUS: + if (p_src_data->status_cb.manufacturer_properties_status.property_ids) { + length = p_src_data->status_cb.manufacturer_properties_status.property_ids->len; + p_dest_data->status_cb.manufacturer_properties_status.property_ids = bt_mesh_alloc_buf(length); + if (!p_dest_data->status_cb.manufacturer_properties_status.property_ids) { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + return; + } + net_buf_simple_add_mem(p_dest_data->status_cb.manufacturer_properties_status.property_ids, + p_src_data->status_cb.manufacturer_properties_status.property_ids->data, + p_src_data->status_cb.manufacturer_properties_status.property_ids->len); + } + break; + case ESP_BLE_MESH_MODEL_OP_GEN_MANUFACTURER_PROPERTY_GET: + case ESP_BLE_MESH_MODEL_OP_GEN_MANUFACTURER_PROPERTY_SET: + case ESP_BLE_MESH_MODEL_OP_GEN_MANUFACTURER_PROPERTY_STATUS: + if (p_src_data->status_cb.manufacturer_property_status.property_value) { + length = p_src_data->status_cb.manufacturer_property_status.property_value->len; + p_dest_data->status_cb.manufacturer_property_status.property_value = bt_mesh_alloc_buf(length); + if (!p_dest_data->status_cb.manufacturer_property_status.property_value) { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + return; + } + net_buf_simple_add_mem(p_dest_data->status_cb.manufacturer_property_status.property_value, + p_src_data->status_cb.manufacturer_property_status.property_value->data, + p_src_data->status_cb.manufacturer_property_status.property_value->len); + } + break; + case ESP_BLE_MESH_MODEL_OP_GEN_CLIENT_PROPERTIES_GET: + case ESP_BLE_MESH_MODEL_OP_GEN_CLIENT_PROPERTIES_STATUS: + if (p_src_data->status_cb.client_properties_status.property_ids) { + length = p_src_data->status_cb.client_properties_status.property_ids->len; + p_dest_data->status_cb.client_properties_status.property_ids = bt_mesh_alloc_buf(length); + if (!p_dest_data->status_cb.client_properties_status.property_ids) { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + return; + } + net_buf_simple_add_mem(p_dest_data->status_cb.client_properties_status.property_ids, + p_src_data->status_cb.client_properties_status.property_ids->data, + p_src_data->status_cb.client_properties_status.property_ids->len); + } + break; + default: + break; + } + } + case ESP_BLE_MESH_GENERIC_CLIENT_TIMEOUT_EVT: + if (p_src_data->params) { + p_dest_data->params = osi_malloc(sizeof(esp_ble_mesh_client_common_param_t)); + if (p_dest_data->params) { + memcpy(p_dest_data->params, p_src_data->params, sizeof(esp_ble_mesh_client_common_param_t)); + } else { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + } + } + break; + default: + break; + } +} + +static void btc_ble_mesh_free_req_data(btc_msg_t *msg) +{ + esp_ble_mesh_generic_client_cb_param_t *arg = NULL; + u32_t opcode; + + if (!msg || !msg->arg) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + arg = (esp_ble_mesh_generic_client_cb_param_t *)(msg->arg); + + switch (msg->act) { + case ESP_BLE_MESH_GENERIC_CLIENT_GET_STATE_EVT: + case ESP_BLE_MESH_GENERIC_CLIENT_SET_STATE_EVT: + case ESP_BLE_MESH_GENERIC_CLIENT_PUBLISH_EVT: + if (arg->params) { + opcode = arg->params->opcode; + switch (opcode) { + case ESP_BLE_MESH_MODEL_OP_GEN_USER_PROPERTIES_GET: + case ESP_BLE_MESH_MODEL_OP_GEN_USER_PROPERTIES_STATUS: + bt_mesh_free_buf(arg->status_cb.user_properties_status.property_ids); + break; + case ESP_BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_GET: + case ESP_BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_SET: + case ESP_BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_STATUS: + bt_mesh_free_buf(arg->status_cb.user_property_status.property_value); + break; + case ESP_BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTIES_GET: + case ESP_BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTIES_STATUS: + bt_mesh_free_buf(arg->status_cb.admin_properties_status.property_ids); + break; + case ESP_BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_GET: + case ESP_BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_SET: + case ESP_BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_STATUS: + bt_mesh_free_buf(arg->status_cb.admin_property_status.property_value); + break; + case ESP_BLE_MESH_MODEL_OP_GEN_MANUFACTURER_PROPERTIES_GET: + case ESP_BLE_MESH_MODEL_OP_GEN_MANUFACTURER_PROPERTIES_STATUS: + bt_mesh_free_buf(arg->status_cb.manufacturer_properties_status.property_ids); + break; + case ESP_BLE_MESH_MODEL_OP_GEN_MANUFACTURER_PROPERTY_GET: + case ESP_BLE_MESH_MODEL_OP_GEN_MANUFACTURER_PROPERTY_SET: + case ESP_BLE_MESH_MODEL_OP_GEN_MANUFACTURER_PROPERTY_STATUS: + bt_mesh_free_buf(arg->status_cb.manufacturer_property_status.property_value); + break; + case ESP_BLE_MESH_MODEL_OP_GEN_CLIENT_PROPERTIES_GET: + case ESP_BLE_MESH_MODEL_OP_GEN_CLIENT_PROPERTIES_STATUS: + bt_mesh_free_buf(arg->status_cb.client_properties_status.property_ids); + break; + default: + break; + } + } + case ESP_BLE_MESH_GENERIC_CLIENT_TIMEOUT_EVT: + if (arg->params) { + osi_free(arg->params); + } + break; + default: + break; + } +} + +void btc_ble_mesh_generic_client_arg_deep_free(btc_msg_t *msg) +{ + btc_ble_mesh_generic_client_args_t *arg = NULL; + u32_t opcode = 0; + + if (!msg || !msg->arg) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + arg = (btc_ble_mesh_generic_client_args_t *)(msg->arg); + + switch (msg->act) { + case BTC_BLE_MESH_ACT_GENERIC_CLIENT_GET_STATE: + if (arg->generic_client_get_state.params) { + osi_free(arg->generic_client_get_state.params); + } + if (arg->generic_client_get_state.get_state) { + osi_free(arg->generic_client_get_state.get_state); + } + break; + case BTC_BLE_MESH_ACT_GENERIC_CLIENT_SET_STATE: + if (arg->generic_client_set_state.params) { + opcode = arg->generic_client_set_state.params->opcode; + osi_free(arg->generic_client_set_state.params); + } + if (arg->generic_client_set_state.set_state) { + if (opcode) { + switch (opcode) { + case ESP_BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_SET: + bt_mesh_free_buf(arg->generic_client_set_state.set_state->user_property_set.property_value); + break; + case ESP_BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_SET: + bt_mesh_free_buf(arg->generic_client_set_state.set_state->admin_property_set.property_value); + break; + default: + break; + } + } + osi_free(arg->generic_client_set_state.set_state); + } + break; + default: + break; + } + + return; +} + +static void btc_mesh_generic_client_callback(esp_ble_mesh_generic_client_cb_param_t *cb_params, uint8_t act) +{ + btc_msg_t msg = {0}; + + LOG_DEBUG("%s", __func__); + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_GENERIC_CLIENT; + msg.act = act; + + btc_transfer_context(&msg, cb_params, + sizeof(esp_ble_mesh_generic_client_cb_param_t), btc_ble_mesh_copy_req_data); +} + +void bt_mesh_callback_generic_status_to_btc(u32_t opcode, u8_t evt_type, + struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + const u8_t *val, size_t len) +{ + esp_ble_mesh_generic_client_cb_param_t cb_params = {0}; + esp_ble_mesh_client_common_param_t params = {0}; + size_t length; + uint8_t act; + + if (!model || !ctx) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + switch (evt_type) { + case 0x00: + act = ESP_BLE_MESH_GENERIC_CLIENT_GET_STATE_EVT; + break; + case 0x01: + act = ESP_BLE_MESH_GENERIC_CLIENT_SET_STATE_EVT; + break; + case 0x02: + act = ESP_BLE_MESH_GENERIC_CLIENT_PUBLISH_EVT; + break; + case 0x03: + act = ESP_BLE_MESH_GENERIC_CLIENT_TIMEOUT_EVT; + break; + default: + LOG_ERROR("%s, Unknown generic client event type %d", __func__, evt_type); + return; + } + + params.opcode = opcode; + params.model = (esp_ble_mesh_model_t *)model; + params.ctx.net_idx = ctx->net_idx; + params.ctx.app_idx = ctx->app_idx; + params.ctx.addr = ctx->addr; + params.ctx.recv_ttl = ctx->recv_ttl; + params.ctx.recv_op = ctx->recv_op; + params.ctx.recv_dst = ctx->recv_dst; + + cb_params.error_code = 0; + cb_params.params = ¶ms; + + if (val && len) { + length = (len <= sizeof(cb_params.status_cb)) ? len : sizeof(cb_params.status_cb); + memcpy(&cb_params.status_cb, val, length); + } + + btc_mesh_generic_client_callback(&cb_params, act); +} + +void btc_mesh_generic_client_publish_callback(u32_t opcode, struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) +{ + if (!model || !ctx || !buf) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + bt_mesh_callback_generic_status_to_btc(opcode, 0x02, model, ctx, buf->data, buf->len); +} + +void btc_mesh_generic_client_call_handler(btc_msg_t *msg) +{ + esp_ble_mesh_generic_client_cb_param_t generic_client_cb = {0}; + esp_ble_mesh_client_common_param_t *params = NULL; + btc_ble_mesh_generic_client_args_t *arg = NULL; + struct bt_mesh_common_param common = {0}; + bt_mesh_role_param_t role_param = {0}; + + if (!msg || !msg->arg) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + arg = (btc_ble_mesh_generic_client_args_t *)(msg->arg); + + switch (msg->act) { + case BTC_BLE_MESH_ACT_GENERIC_CLIENT_GET_STATE: { + params = arg->generic_client_get_state.params; + role_param.model = (struct bt_mesh_model *)params->model; + role_param.role = params->msg_role; + if (bt_mesh_set_model_role(&role_param)) { + LOG_ERROR("%s, Failed to set model role", __func__); + return; + } + common.opcode = params->opcode; + common.model = (struct bt_mesh_model *)params->model; + common.ctx.net_idx = params->ctx.net_idx; + common.ctx.app_idx = params->ctx.app_idx; + common.ctx.addr = params->ctx.addr; + common.ctx.send_rel = params->ctx.send_rel; + common.ctx.send_ttl = params->ctx.send_ttl; + common.msg_timeout = params->msg_timeout; + + generic_client_cb.params = arg->generic_client_get_state.params; + generic_client_cb.error_code = + bt_mesh_generic_client_get_state(&common, + (void *)arg->generic_client_get_state.get_state, + (void *)&generic_client_cb.status_cb); + if (generic_client_cb.error_code) { + /* If send failed, callback error_code to app layer immediately */ + btc_mesh_generic_client_callback(&generic_client_cb, + ESP_BLE_MESH_GENERIC_CLIENT_GET_STATE_EVT); + } + break; + } + case BTC_BLE_MESH_ACT_GENERIC_CLIENT_SET_STATE: { + params = arg->generic_client_set_state.params; + role_param.model = (struct bt_mesh_model *)params->model; + role_param.role = params->msg_role; + if (bt_mesh_set_model_role(&role_param)) { + LOG_ERROR("%s, Failed to set model role", __func__); + return; + } + common.opcode = params->opcode; + common.model = (struct bt_mesh_model *)params->model; + common.ctx.net_idx = params->ctx.net_idx; + common.ctx.app_idx = params->ctx.app_idx; + common.ctx.addr = params->ctx.addr; + common.ctx.send_rel = params->ctx.send_rel; + common.ctx.send_ttl = params->ctx.send_ttl; + common.msg_timeout = params->msg_timeout; + + generic_client_cb.params = arg->generic_client_set_state.params; + generic_client_cb.error_code = + bt_mesh_generic_client_set_state(&common, + (void *)arg->generic_client_set_state.set_state, + (void *)&generic_client_cb.status_cb); + if (generic_client_cb.error_code) { + /* If send failed, callback error_code to app layer immediately */ + btc_mesh_generic_client_callback(&generic_client_cb, + ESP_BLE_MESH_GENERIC_CLIENT_SET_STATE_EVT); + } + break; + } + default: + break; + } + + btc_ble_mesh_generic_client_arg_deep_free(msg); +} + +void btc_mesh_generic_client_cb_handler(btc_msg_t *msg) +{ + esp_ble_mesh_generic_client_cb_param_t *param = NULL; + + if (!msg || !msg->arg) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + param = (esp_ble_mesh_generic_client_cb_param_t *)(msg->arg); + + if (msg->act < ESP_BLE_MESH_GENERIC_CLIENT_EVT_MAX) { + btc_ble_mesh_cb_to_app(msg->act, param); + } else { + LOG_ERROR("%s, Unknown msg->act = %d", __func__, msg->act); + } + + btc_ble_mesh_free_req_data(msg); +} diff --git a/components/bt/ble_mesh/btc/btc_ble_mesh_health_model.c b/components/bt/ble_mesh/btc/btc_ble_mesh_health_model.c new file mode 100644 index 0000000000..6651f59936 --- /dev/null +++ b/components/bt/ble_mesh/btc/btc_ble_mesh_health_model.c @@ -0,0 +1,592 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 + +#include "btc/btc_manage.h" +#include "btc/btc_task.h" +#include "osi/allocator.h" + +#include "health_srv.h" +#include "health_cli.h" +#include "mesh_common.h" + +#include "btc_ble_mesh_health_model.h" +#include "esp_ble_mesh_defs.h" + +extern s32_t health_msg_timeout; + +static inline void btc_ble_mesh_health_client_cb_to_app(esp_ble_mesh_health_client_cb_event_t event, + esp_ble_mesh_health_client_cb_param_t *param) +{ + esp_ble_mesh_health_client_cb_t btc_mesh_cb = (esp_ble_mesh_health_client_cb_t)btc_profile_cb_get(BTC_PID_HEALTH_CLIENT); + if (btc_mesh_cb) { + btc_mesh_cb(event, param); + } +} + +static inline void btc_ble_mesh_health_server_cb_to_app(esp_ble_mesh_health_server_cb_event_t event, + esp_ble_mesh_health_server_cb_param_t *param) +{ + esp_ble_mesh_health_server_cb_t btc_mesh_cb = (esp_ble_mesh_health_server_cb_t)btc_profile_cb_get(BTC_PID_HEALTH_SERVER); + if (btc_mesh_cb) { + btc_mesh_cb(event, param); + } +} + +void btc_ble_mesh_health_client_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) +{ + btc_ble_mesh_health_client_args_t *dst = (btc_ble_mesh_health_client_args_t *)p_dest; + btc_ble_mesh_health_client_args_t *src = (btc_ble_mesh_health_client_args_t *)p_src; + + if (!msg || !dst || !src) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + switch (msg->act) { + case BTC_BLE_MESH_ACT_HEALTH_CLIENT_GET_STATE: { + dst->health_client_get_state.params = (esp_ble_mesh_client_common_param_t *)osi_malloc(sizeof(esp_ble_mesh_client_common_param_t)); + dst->health_client_get_state.get_state = (esp_ble_mesh_health_client_get_state_t *)osi_malloc(sizeof(esp_ble_mesh_health_client_get_state_t)); + if (dst->health_client_get_state.params && dst->health_client_get_state.get_state) { + memcpy(dst->health_client_get_state.params, src->health_client_get_state.params, + sizeof(esp_ble_mesh_client_common_param_t)); + memcpy(dst->health_client_get_state.get_state, src->health_client_get_state.get_state, + sizeof(esp_ble_mesh_health_client_get_state_t)); + } else { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + } + break; + } + case BTC_BLE_MESH_ACT_HEALTH_CLIENT_SET_STATE: { + dst->health_client_set_state.params = (esp_ble_mesh_client_common_param_t *)osi_malloc(sizeof(esp_ble_mesh_client_common_param_t)); + dst->health_client_set_state.set_state = (esp_ble_mesh_health_client_set_state_t *)osi_malloc(sizeof(esp_ble_mesh_health_client_set_state_t)); + if (dst->health_client_set_state.params && dst->health_client_set_state.set_state) { + memcpy(dst->health_client_set_state.params, src->health_client_set_state.params, + sizeof(esp_ble_mesh_client_common_param_t)); + memcpy(dst->health_client_set_state.set_state, src->health_client_set_state.set_state, + sizeof(esp_ble_mesh_health_client_set_state_t)); + } else { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + } + break; + } + default: + LOG_DEBUG("%s, Unknown deep copy act %d", __func__, msg->act); + break; + } +} + +static void btc_ble_mesh_health_client_arg_deep_free(btc_msg_t *msg) +{ + btc_ble_mesh_health_client_args_t *arg = NULL; + + if (!msg || !msg->arg) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + arg = (btc_ble_mesh_health_client_args_t *)(msg->arg); + + switch (msg->act) { + case BTC_BLE_MESH_ACT_HEALTH_CLIENT_GET_STATE: + if (arg->health_client_get_state.params) { + osi_free(arg->health_client_get_state.params); + } + if (arg->health_client_get_state.get_state) { + osi_free(arg->health_client_get_state.get_state); + } + break; + case BTC_BLE_MESH_ACT_HEALTH_CLIENT_SET_STATE: + if (arg->health_client_set_state.params) { + osi_free(arg->health_client_set_state.params); + } + if (arg->health_client_set_state.set_state) { + osi_free(arg->health_client_set_state.set_state); + } + break; + default: + break; + } + + return; +} + +void btc_ble_mesh_health_server_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) +{ + if (!msg) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + switch (msg->act) { + case BTC_BLE_MESH_ACT_HEALTH_SERVER_FAULT_UPDATE: + break; + default: + break; + } +} + +static void btc_ble_mesh_health_server_arg_deep_free(btc_msg_t *msg) +{ + if (!msg) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + switch (msg->act) { + case BTC_BLE_MESH_ACT_HEALTH_SERVER_FAULT_UPDATE: + break; + default: + break; + } +} + +static void btc_ble_mesh_health_client_copy_req_data(btc_msg_t *msg, void *p_dest, void *p_src) +{ + esp_ble_mesh_health_client_cb_param_t *p_dest_data = (esp_ble_mesh_health_client_cb_param_t *)p_dest; + esp_ble_mesh_health_client_cb_param_t *p_src_data = (esp_ble_mesh_health_client_cb_param_t *)p_src; + u32_t opcode; + u16_t length; + + if (!msg || !p_src_data || !p_dest_data) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + switch (msg->act) { + case ESP_BLE_MESH_HEALTH_CLIENT_GET_STATE_EVT: + case ESP_BLE_MESH_HEALTH_CLIENT_SET_STATE_EVT: + case ESP_BLE_MESH_HEALTH_CLIENT_PUBLISH_EVT: + if (p_src_data->params) { + opcode = p_src_data->params->opcode; + switch (opcode) { + case OP_HEALTH_CURRENT_STATUS: + if (p_src_data->status_cb.current_status.fault_array) { + length = p_src_data->status_cb.current_status.fault_array->len; + p_dest_data->status_cb.current_status.fault_array = bt_mesh_alloc_buf(length); + if (!p_dest_data->status_cb.current_status.fault_array) { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + return; + } + net_buf_simple_add_mem(p_dest_data->status_cb.current_status.fault_array, + p_src_data->status_cb.current_status.fault_array->data, + p_src_data->status_cb.current_status.fault_array->len); + } + break; + case OP_HEALTH_FAULT_GET: + case OP_HEALTH_FAULT_CLEAR: + case OP_HEALTH_FAULT_TEST: + case OP_HEALTH_FAULT_STATUS: + if (p_src_data->status_cb.fault_status.fault_array) { + length = p_src_data->status_cb.fault_status.fault_array->len; + p_dest_data->status_cb.fault_status.fault_array = bt_mesh_alloc_buf(length); + if (!p_dest_data->status_cb.fault_status.fault_array) { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + return; + } + net_buf_simple_add_mem(p_dest_data->status_cb.fault_status.fault_array, + p_src_data->status_cb.fault_status.fault_array->data, + p_src_data->status_cb.fault_status.fault_array->len); + } + break; + default: + break; + } + } + case ESP_BLE_MESH_HEALTH_CLIENT_TIMEOUT_EVT: + if (p_src_data->params) { + p_dest_data->params = osi_malloc(sizeof(esp_ble_mesh_client_common_param_t)); + if (p_dest_data->params) { + memcpy(p_dest_data->params, p_src_data->params, sizeof(esp_ble_mesh_client_common_param_t)); + } else { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + } + } + break; + default: + break; + } +} + +static void btc_ble_mesh_health_client_free_req_data(btc_msg_t *msg) +{ + esp_ble_mesh_health_client_cb_param_t *arg = NULL; + u32_t opcode; + + if (!msg || !msg->arg) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + arg = (esp_ble_mesh_health_client_cb_param_t *)(msg->arg); + + switch (msg->act) { + case ESP_BLE_MESH_HEALTH_CLIENT_GET_STATE_EVT: + case ESP_BLE_MESH_HEALTH_CLIENT_SET_STATE_EVT: + case ESP_BLE_MESH_HEALTH_CLIENT_PUBLISH_EVT: + if (arg->params) { + opcode = arg->params->opcode; + switch (opcode) { + case OP_HEALTH_CURRENT_STATUS: + bt_mesh_free_buf(arg->status_cb.current_status.fault_array); + break; + case OP_HEALTH_FAULT_GET: + case OP_HEALTH_FAULT_CLEAR: + case OP_HEALTH_FAULT_TEST: + case OP_HEALTH_FAULT_STATUS: + bt_mesh_free_buf(arg->status_cb.fault_status.fault_array); + break; + default: + break; + } + } + case ESP_BLE_MESH_HEALTH_CLIENT_TIMEOUT_EVT: + if (arg->params) { + osi_free(arg->params); + } + break; + default: + break; + } +} + +static void btc_ble_mesh_health_server_copy_req_data(btc_msg_t *msg, void *p_dest, void *p_src) +{ + if (!msg) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + switch (msg->act) { + case ESP_BLE_MESH_HEALTH_SERVER_FAULT_UPDATE_COMPLETE_EVT: + break; + default: + break; + } +} + +static void btc_ble_mesh_health_server_free_req_data(btc_msg_t *msg) +{ + if (!msg) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + switch (msg->act) { + case ESP_BLE_MESH_HEALTH_SERVER_FAULT_UPDATE_COMPLETE_EVT: + break; + default: + break; + } +} + +static void btc_mesh_health_client_callback(esp_ble_mesh_health_client_cb_param_t *cb_params, uint8_t act) +{ + btc_msg_t msg = {0}; + + LOG_DEBUG("%s", __func__); + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_HEALTH_CLIENT; + msg.act = act; + + btc_transfer_context(&msg, cb_params, + sizeof(esp_ble_mesh_health_client_cb_param_t), btc_ble_mesh_health_client_copy_req_data); +} + +static void btc_mesh_health_server_callback(esp_ble_mesh_health_server_cb_param_t *cb_params, uint8_t act) +{ + btc_msg_t msg = {0}; + + LOG_DEBUG("%s", __func__); + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_HEALTH_SERVER; + msg.act = act; + + btc_transfer_context(&msg, cb_params, + sizeof(esp_ble_mesh_health_server_cb_param_t), btc_ble_mesh_health_server_copy_req_data); +} + +int btc_ble_mesh_health_client_get_state(esp_ble_mesh_client_common_param_t *params, + esp_ble_mesh_health_client_get_state_t *get_state, + esp_ble_mesh_health_client_cb_param_t *client_cb) +{ + struct bt_mesh_msg_ctx ctx = {0}; + + if (!params || !client_cb) { + LOG_ERROR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + ctx.net_idx = params->ctx.net_idx; + ctx.app_idx = params->ctx.app_idx; + ctx.addr = params->ctx.addr; + ctx.send_rel = params->ctx.send_rel; + ctx.send_ttl = params->ctx.send_ttl; + + health_msg_timeout = params->msg_timeout; + + switch (params->opcode) { + case ESP_BLE_MESH_MODEL_OP_ATTENTION_GET: + return (client_cb->error_code = bt_mesh_health_attention_get(&ctx)); + case ESP_BLE_MESH_MODEL_OP_HEALTH_PERIOD_GET: + return (client_cb->error_code = bt_mesh_health_period_get(&ctx)); + case ESP_BLE_MESH_MODEL_OP_HEALTH_FAULT_GET: + return (client_cb->error_code = bt_mesh_health_fault_get(&ctx, get_state->fault_get.company_id)); + default: + BT_WARN("%s, invalid opcode 0x%x", __func__, params->opcode); + return (client_cb->error_code = -EINVAL); + } + + return 0; +} + +int btc_ble_mesh_health_client_set_state(esp_ble_mesh_client_common_param_t *params, + esp_ble_mesh_health_client_set_state_t *set_state, + esp_ble_mesh_health_client_cb_param_t *client_cb) +{ + struct bt_mesh_msg_ctx ctx = {0}; + + if (!params || !set_state || !client_cb) { + LOG_ERROR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + ctx.net_idx = params->ctx.net_idx; + ctx.app_idx = params->ctx.app_idx; + ctx.addr = params->ctx.addr; + ctx.send_rel = params->ctx.send_rel; + ctx.send_ttl = params->ctx.send_ttl; + + health_msg_timeout = params->msg_timeout; + + switch (params->opcode) { + case ESP_BLE_MESH_MODEL_OP_ATTENTION_SET: + return (client_cb->error_code = + bt_mesh_health_attention_set(&ctx, set_state->attention_set.attention, true)); + case ESP_BLE_MESH_MODEL_OP_ATTENTION_SET_UNACK: + return (client_cb->error_code = + bt_mesh_health_attention_set(&ctx, set_state->attention_set.attention, false)); + case ESP_BLE_MESH_MODEL_OP_HEALTH_PERIOD_SET: + return (client_cb->error_code = + bt_mesh_health_period_set(&ctx, set_state->period_set.fast_period_divisor, true)); + case ESP_BLE_MESH_MODEL_OP_HEALTH_PERIOD_SET_UNACK: + return (client_cb->error_code = + bt_mesh_health_period_set(&ctx, set_state->period_set.fast_period_divisor, false)); + case ESP_BLE_MESH_MODEL_OP_HEALTH_FAULT_TEST: + return (client_cb->error_code = + bt_mesh_health_fault_test(&ctx, set_state->fault_test.company_id, set_state->fault_test.test_id, true)); + case ESP_BLE_MESH_MODEL_OP_HEALTH_FAULT_TEST_UNACK: + return (client_cb->error_code = + bt_mesh_health_fault_test(&ctx, set_state->fault_test.company_id, set_state->fault_test.test_id, false)); + case ESP_BLE_MESH_MODEL_OP_HEALTH_FAULT_CLEAR: + return (client_cb->error_code = + bt_mesh_health_fault_clear(&ctx, set_state->fault_clear.company_id, true)); + case ESP_BLE_MESH_MODEL_OP_HEALTH_FAULT_CLEAR_UNACK: + return (client_cb->error_code = + bt_mesh_health_fault_clear(&ctx, set_state->fault_clear.company_id, false)); + default: + BT_WARN("%s, Invalid opcode 0x%x", __func__, params->opcode); + return (client_cb->error_code = -EINVAL); + } + + return 0; +} + +void bt_mesh_callback_health_status_to_btc(u32_t opcode, u8_t evt_type, + struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + const u8_t *val, u16_t len) +{ + esp_ble_mesh_health_client_cb_param_t cb_params = {0}; + esp_ble_mesh_client_common_param_t params = {0}; + size_t length; + uint8_t act; + + if (!model || !ctx) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + switch (evt_type) { + case 0x00: + act = ESP_BLE_MESH_HEALTH_CLIENT_GET_STATE_EVT; + break; + case 0x01: + act = ESP_BLE_MESH_HEALTH_CLIENT_SET_STATE_EVT; + break; + case 0x02: + act = ESP_BLE_MESH_HEALTH_CLIENT_PUBLISH_EVT; + break; + case 0x03: + act = ESP_BLE_MESH_HEALTH_CLIENT_TIMEOUT_EVT; + break; + default: + LOG_ERROR("%s, Unknown health client event type %d", __func__, evt_type); + return; + } + + params.opcode = opcode; + params.model = (esp_ble_mesh_model_t *)model; + params.ctx.net_idx = ctx->net_idx; + params.ctx.app_idx = ctx->app_idx; + params.ctx.addr = ctx->addr; + params.ctx.recv_ttl = ctx->recv_ttl; + params.ctx.recv_op = ctx->recv_op; + params.ctx.recv_dst = ctx->recv_dst; + + cb_params.error_code = 0; + cb_params.params = ¶ms; + + if (val && len) { + length = (len <= sizeof(cb_params.status_cb)) ? len : sizeof(cb_params.status_cb); + memcpy(&cb_params.status_cb, val, length); + } + + btc_mesh_health_client_callback(&cb_params, act); +} + +void btc_mesh_health_publish_callback(u32_t opcode, struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) +{ + if (!model || !ctx || !buf) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + bt_mesh_callback_health_status_to_btc(opcode, 0x02, model, ctx, buf->data, buf->len); +} + +void btc_mesh_health_client_call_handler(btc_msg_t *msg) +{ + btc_ble_mesh_health_client_args_t *arg = NULL; + esp_ble_mesh_health_client_cb_param_t health_client_cb = {0}; + bt_mesh_role_param_t role_param = {0}; + + if (!msg || !msg->arg) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + arg = (btc_ble_mesh_health_client_args_t *)(msg->arg); + + switch (msg->act) { + case BTC_BLE_MESH_ACT_HEALTH_CLIENT_GET_STATE: { + health_client_cb.params = arg->health_client_get_state.params; + role_param.model = (struct bt_mesh_model *)health_client_cb.params->model; + role_param.role = health_client_cb.params->msg_role; + if (bt_mesh_set_model_role(&role_param)) { + LOG_ERROR("%s, Failed to set model role", __func__); + return; + } + btc_ble_mesh_health_client_get_state(arg->health_client_get_state.params, + arg->health_client_get_state.get_state, + &health_client_cb); + if (health_client_cb.error_code) { + /* If send failed, callback error_code to app layer immediately */ + btc_mesh_health_client_callback(&health_client_cb, ESP_BLE_MESH_HEALTH_CLIENT_GET_STATE_EVT); + } + break; + } + case BTC_BLE_MESH_ACT_HEALTH_CLIENT_SET_STATE: { + health_client_cb.params = arg->health_client_set_state.params; + role_param.model = (struct bt_mesh_model *)health_client_cb.params->model; + role_param.role = health_client_cb.params->msg_role; + if (bt_mesh_set_model_role(&role_param)) { + LOG_ERROR("%s, Failed to set model role", __func__); + return; + } + btc_ble_mesh_health_client_set_state(arg->health_client_set_state.params, + arg->health_client_set_state.set_state, + &health_client_cb); + if (health_client_cb.error_code) { + /* If send failed, callback error_code to app layer immediately */ + btc_mesh_health_client_callback(&health_client_cb, ESP_BLE_MESH_HEALTH_CLIENT_SET_STATE_EVT); + } + break; + } + default: + break; + } + + btc_ble_mesh_health_client_arg_deep_free(msg); + return; +} + +void btc_mesh_health_client_cb_handler(btc_msg_t *msg) +{ + esp_ble_mesh_health_client_cb_param_t *param = NULL; + + if (!msg || !msg->arg) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + param = (esp_ble_mesh_health_client_cb_param_t *)(msg->arg); + + if (msg->act < ESP_BLE_MESH_HEALTH_CLIENT_EVT_MAX) { + btc_ble_mesh_health_client_cb_to_app(msg->act, param); + } else { + LOG_ERROR("%s, Unknown msg->act = %d", __func__, msg->act); + } + + btc_ble_mesh_health_client_free_req_data(msg); +} + +void btc_mesh_health_server_call_handler(btc_msg_t *msg) +{ + esp_ble_mesh_health_server_cb_param_t health_server_cb = {0}; + btc_ble_mesh_health_server_args_t *arg = NULL; + + if (!msg || !msg->arg) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + arg = (btc_ble_mesh_health_server_args_t *)(msg->arg); + + switch (msg->act) { + case BTC_BLE_MESH_ACT_HEALTH_SERVER_FAULT_UPDATE: { + health_server_cb.error_code = bt_mesh_fault_update((struct bt_mesh_elem *)arg->fault_update.element); + btc_mesh_health_server_callback(&health_server_cb, ESP_BLE_MESH_HEALTH_SERVER_FAULT_UPDATE_COMPLETE_EVT); + } + default: + break; + } + + btc_ble_mesh_health_server_arg_deep_free(msg); +} + +void btc_mesh_health_server_cb_handler(btc_msg_t *msg) +{ + esp_ble_mesh_health_server_cb_param_t *param = NULL; + + if (!msg || !msg->arg) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + param = (esp_ble_mesh_health_server_cb_param_t *)(msg->arg); + + if (msg->act < ESP_BLE_MESH_HEALTH_SERVER_EVT_MAX) { + btc_ble_mesh_health_server_cb_to_app(msg->act, param); + } else { + LOG_ERROR("%s, Unknown msg->act = %d", __func__, msg->act); + } + + btc_ble_mesh_health_server_free_req_data(msg); +} diff --git a/components/bt/ble_mesh/btc/btc_ble_mesh_lighting_model.c b/components/bt/ble_mesh/btc/btc_ble_mesh_lighting_model.c new file mode 100644 index 0000000000..1b89c51713 --- /dev/null +++ b/components/bt/ble_mesh/btc/btc_ble_mesh_lighting_model.c @@ -0,0 +1,381 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 + +#include "btc/btc_manage.h" +#include "osi/allocator.h" + +#include "lighting_client.h" +#include "btc_ble_mesh_lighting_model.h" +#include "esp_ble_mesh_lighting_model_api.h" + +static inline void btc_ble_mesh_cb_to_app(esp_ble_mesh_light_client_cb_event_t event, + esp_ble_mesh_light_client_cb_param_t *param) +{ + esp_ble_mesh_light_client_cb_t btc_mesh_cb = (esp_ble_mesh_light_client_cb_t)btc_profile_cb_get(BTC_PID_LIGHT_CLIENT); + if (btc_mesh_cb) { + btc_mesh_cb(event, param); + } +} + +void btc_ble_mesh_light_client_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) +{ + btc_ble_mesh_light_client_args_t *dst = (btc_ble_mesh_light_client_args_t *)p_dest; + btc_ble_mesh_light_client_args_t *src = (btc_ble_mesh_light_client_args_t *)p_src; + + if (!msg || !dst || !src) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + switch (msg->act) { + case BTC_BLE_MESH_ACT_LIGHT_CLIENT_GET_STATE: { + dst->light_client_get_state.params = (esp_ble_mesh_client_common_param_t *)osi_malloc(sizeof(esp_ble_mesh_client_common_param_t)); + dst->light_client_get_state.get_state = (esp_ble_mesh_light_client_get_state_t *)osi_malloc(sizeof(esp_ble_mesh_light_client_get_state_t)); + if (dst->light_client_get_state.params && dst->light_client_get_state.get_state) { + memcpy(dst->light_client_get_state.params, src->light_client_get_state.params, + sizeof(esp_ble_mesh_client_common_param_t)); + memcpy(dst->light_client_get_state.get_state, src->light_client_get_state.get_state, + sizeof(esp_ble_mesh_light_client_get_state_t)); + } else { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + } + break; + } + case BTC_BLE_MESH_ACT_LIGHT_CLIENT_SET_STATE: { + dst->light_client_set_state.params = (esp_ble_mesh_client_common_param_t *)osi_malloc(sizeof(esp_ble_mesh_client_common_param_t)); + dst->light_client_set_state.set_state = (esp_ble_mesh_light_client_set_state_t *)osi_malloc(sizeof(esp_ble_mesh_light_client_set_state_t)); + if (dst->light_client_set_state.params && dst->light_client_set_state.set_state) { + memcpy(dst->light_client_set_state.params, src->light_client_set_state.params, + sizeof(esp_ble_mesh_client_common_param_t)); + memcpy(dst->light_client_set_state.set_state, src->light_client_set_state.set_state, + sizeof(esp_ble_mesh_light_client_set_state_t)); + } else { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + } + break; + } + default: + LOG_DEBUG("%s, Unknown deep copy act %d", __func__, msg->act); + break; + } +} + +static void btc_ble_mesh_copy_req_data(btc_msg_t *msg, void *p_dest, void *p_src) +{ + esp_ble_mesh_light_client_cb_param_t *p_dest_data = (esp_ble_mesh_light_client_cb_param_t *)p_dest; + esp_ble_mesh_light_client_cb_param_t *p_src_data = (esp_ble_mesh_light_client_cb_param_t *)p_src; + u32_t opcode; + u16_t length; + + if (!msg || !p_src_data || !p_dest_data) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + switch (msg->act) { + case ESP_BLE_MESH_LIGHT_CLIENT_GET_STATE_EVT: + case ESP_BLE_MESH_LIGHT_CLIENT_SET_STATE_EVT: + case ESP_BLE_MESH_LIGHT_CLIENT_PUBLISH_EVT: + if (p_src_data->params) { + opcode = p_src_data->params->opcode; + switch (opcode) { + case ESP_BLE_MESH_MODEL_OP_LIGHT_LC_PROPERTY_GET: + case ESP_BLE_MESH_MODEL_OP_LIGHT_LC_PROPERTY_SET: + case ESP_BLE_MESH_MODEL_OP_LIGHT_LC_PROPERTY_STATUS: + if (p_src_data->status_cb.lc_property_status.property_value) { + length = p_src_data->status_cb.lc_property_status.property_value->len; + p_dest_data->status_cb.lc_property_status.property_value = bt_mesh_alloc_buf(length); + if (!p_dest_data->status_cb.lc_property_status.property_value) { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + return; + } + net_buf_simple_add_mem(p_dest_data->status_cb.lc_property_status.property_value, + p_src_data->status_cb.lc_property_status.property_value->data, + p_src_data->status_cb.lc_property_status.property_value->len); + } + break; + default: + break; + } + } + case ESP_BLE_MESH_LIGHT_CLIENT_TIMEOUT_EVT: + if (p_src_data->params) { + p_dest_data->params = osi_malloc(sizeof(esp_ble_mesh_client_common_param_t)); + if (p_dest_data->params) { + memcpy(p_dest_data->params, p_src_data->params, sizeof(esp_ble_mesh_client_common_param_t)); + } else { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + } + } + break; + default: + break; + } +} + +static void btc_ble_mesh_free_req_data(btc_msg_t *msg) +{ + esp_ble_mesh_light_client_cb_param_t *arg = NULL; + u32_t opcode; + + if (!msg || !msg->arg) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + arg = (esp_ble_mesh_light_client_cb_param_t *)(msg->arg); + + switch (msg->act) { + case ESP_BLE_MESH_LIGHT_CLIENT_GET_STATE_EVT: + case ESP_BLE_MESH_LIGHT_CLIENT_SET_STATE_EVT: + case ESP_BLE_MESH_LIGHT_CLIENT_PUBLISH_EVT: + if (arg->params) { + opcode = arg->params->opcode; + switch (opcode) { + case ESP_BLE_MESH_MODEL_OP_LIGHT_LC_PROPERTY_GET: + case ESP_BLE_MESH_MODEL_OP_LIGHT_LC_PROPERTY_SET: + case ESP_BLE_MESH_MODEL_OP_LIGHT_LC_PROPERTY_STATUS: + bt_mesh_free_buf(arg->status_cb.lc_property_status.property_value); + break; + default: + break; + } + } + case ESP_BLE_MESH_LIGHT_CLIENT_TIMEOUT_EVT: + if (arg->params) { + osi_free(arg->params); + } + break; + default: + break; + } +} + +void btc_ble_mesh_light_client_arg_deep_free(btc_msg_t *msg) +{ + btc_ble_mesh_light_client_args_t *arg = NULL; + + if (!msg || !msg->arg) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + arg = (btc_ble_mesh_light_client_args_t *)(msg->arg); + + switch (msg->act) { + case BTC_BLE_MESH_ACT_LIGHT_CLIENT_GET_STATE: + if (arg->light_client_get_state.params) { + osi_free(arg->light_client_get_state.params); + } + if (arg->light_client_get_state.get_state) { + osi_free(arg->light_client_get_state.get_state); + } + break; + case BTC_BLE_MESH_ACT_LIGHT_CLIENT_SET_STATE: + if (arg->light_client_set_state.params) { + osi_free(arg->light_client_set_state.params); + } + if (arg->light_client_set_state.set_state) { + osi_free(arg->light_client_set_state.set_state); + } + break; + default: + break; + } + + return; +} + +static void btc_mesh_light_client_callback(esp_ble_mesh_light_client_cb_param_t *cb_params, uint8_t act) +{ + btc_msg_t msg = {0}; + + LOG_DEBUG("%s", __func__); + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_LIGHT_CLIENT; + msg.act = act; + + btc_transfer_context(&msg, cb_params, + sizeof(esp_ble_mesh_light_client_cb_param_t), btc_ble_mesh_copy_req_data); +} + +void bt_mesh_callback_light_status_to_btc(u32_t opcode, u8_t evt_type, + struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + const u8_t *val, size_t len) +{ + esp_ble_mesh_light_client_cb_param_t cb_params = {0}; + esp_ble_mesh_client_common_param_t params = {0}; + size_t length; + uint8_t act; + + if (!model || !ctx) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + switch (evt_type) { + case 0x00: + act = ESP_BLE_MESH_LIGHT_CLIENT_GET_STATE_EVT; + break; + case 0x01: + act = ESP_BLE_MESH_LIGHT_CLIENT_SET_STATE_EVT; + break; + case 0x02: + act = ESP_BLE_MESH_LIGHT_CLIENT_PUBLISH_EVT; + break; + case 0x03: + act = ESP_BLE_MESH_LIGHT_CLIENT_TIMEOUT_EVT; + break; + default: + LOG_ERROR("%s, Unknown lighting client event type", __func__); + return; + } + + params.opcode = opcode; + params.model = (esp_ble_mesh_model_t *)model; + params.ctx.net_idx = ctx->net_idx; + params.ctx.app_idx = ctx->app_idx; + params.ctx.addr = ctx->addr; + params.ctx.recv_ttl = ctx->recv_ttl; + params.ctx.recv_op = ctx->recv_op; + params.ctx.recv_dst = ctx->recv_dst; + + cb_params.error_code = 0; + cb_params.params = ¶ms; + + if (val && len) { + length = (len <= sizeof(cb_params.status_cb)) ? len : sizeof(cb_params.status_cb); + memcpy(&cb_params.status_cb, val, length); + } + + btc_mesh_light_client_callback(&cb_params, act); +} + +void btc_mesh_light_client_publish_callback(u32_t opcode, struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) +{ + if (!model || !ctx || !buf) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + bt_mesh_callback_light_status_to_btc(opcode, 0x02, model, ctx, buf->data, buf->len); +} + +void btc_mesh_light_client_call_handler(btc_msg_t *msg) +{ + esp_ble_mesh_light_client_cb_param_t light_client_cb = {0}; + esp_ble_mesh_client_common_param_t *params = NULL; + btc_ble_mesh_light_client_args_t *arg = NULL; + struct bt_mesh_common_param common = {0}; + bt_mesh_role_param_t role_param = {0}; + + if (!msg || !msg->arg) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + arg = (btc_ble_mesh_light_client_args_t *)(msg->arg); + + switch (msg->act) { + case BTC_BLE_MESH_ACT_LIGHT_CLIENT_GET_STATE: { + params = arg->light_client_get_state.params; + role_param.model = (struct bt_mesh_model *)params->model; + role_param.role = params->msg_role; + if (bt_mesh_set_model_role(&role_param)) { + LOG_ERROR("%s, Failed to set model role", __func__); + return; + } + common.opcode = params->opcode; + common.model = (struct bt_mesh_model *)params->model; + common.ctx.net_idx = params->ctx.net_idx; + common.ctx.app_idx = params->ctx.app_idx; + common.ctx.addr = params->ctx.addr; + common.ctx.send_rel = params->ctx.send_rel; + common.ctx.send_ttl = params->ctx.send_ttl; + common.msg_timeout = params->msg_timeout; + + light_client_cb.params = arg->light_client_get_state.params; + light_client_cb.error_code = + bt_mesh_light_client_get_state(&common, + (void *)arg->light_client_get_state.get_state, + (void *)&light_client_cb.status_cb); + if (light_client_cb.error_code) { + /* If send failed, callback error_code to app layer immediately */ + btc_mesh_light_client_callback(&light_client_cb, + ESP_BLE_MESH_LIGHT_CLIENT_GET_STATE_EVT); + } + break; + } + case BTC_BLE_MESH_ACT_LIGHT_CLIENT_SET_STATE: { + params = arg->light_client_set_state.params; + role_param.model = (struct bt_mesh_model *)params->model; + role_param.role = params->msg_role; + if (bt_mesh_set_model_role(&role_param)) { + LOG_ERROR("%s, Failed to set model role", __func__); + return; + } + common.opcode = params->opcode; + common.model = (struct bt_mesh_model *)params->model; + common.ctx.net_idx = params->ctx.net_idx; + common.ctx.app_idx = params->ctx.app_idx; + common.ctx.addr = params->ctx.addr; + common.ctx.send_rel = params->ctx.send_rel; + common.ctx.send_ttl = params->ctx.send_ttl; + common.msg_timeout = params->msg_timeout; + + light_client_cb.params = arg->light_client_set_state.params; + light_client_cb.error_code = + bt_mesh_light_client_set_state(&common, + (void *)arg->light_client_set_state.set_state, + (void *)&light_client_cb.status_cb); + if (light_client_cb.error_code) { + /* If send failed, callback error_code to app layer immediately */ + btc_mesh_light_client_callback(&light_client_cb, + ESP_BLE_MESH_LIGHT_CLIENT_SET_STATE_EVT); + } + break; + } + default: + break; + } + + btc_ble_mesh_light_client_arg_deep_free(msg); +} + +void btc_mesh_light_client_cb_handler(btc_msg_t *msg) +{ + esp_ble_mesh_light_client_cb_param_t *param = NULL; + + if (!msg || !msg->arg) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + param = (esp_ble_mesh_light_client_cb_param_t *)(msg->arg); + + if (msg->act < ESP_BLE_MESH_LIGHT_CLIENT_EVT_MAX) { + btc_ble_mesh_cb_to_app(msg->act, param); + } else { + LOG_ERROR("%s, Unknown msg->act = %d", __func__, msg->act); + } + + btc_ble_mesh_free_req_data(msg); +} + diff --git a/components/bt/ble_mesh/btc/btc_ble_mesh_prov.c b/components/bt/ble_mesh/btc/btc_ble_mesh_prov.c new file mode 100644 index 0000000000..d53b0d8cb4 --- /dev/null +++ b/components/bt/ble_mesh/btc/btc_ble_mesh_prov.c @@ -0,0 +1,1528 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "freertos/semphr.h" + +#include "btc/btc_manage.h" +#include "osi/allocator.h" + +#include "sdkconfig.h" + +#include "mesh_util.h" +#include "mesh_main.h" +#include "mesh_access.h" +#include "mesh_proxy.h" +#include "cfg_cli.h" +#include "health_cli.h" + +#include "mesh.h" +#include "access.h" +#include "transport.h" +#include "proxy.h" +#include "prov.h" +#include "provisioner_prov.h" +#include "provisioner_main.h" + +#include "generic_client.h" +#include "lighting_client.h" +#include "sensor_client.h" +#include "time_scene_client.h" +#include "model_common.h" + +#include "btc_ble_mesh_prov.h" +#include "btc_ble_mesh_config_model.h" +#include "btc_ble_mesh_health_model.h" +#include "btc_ble_mesh_generic_model.h" +#include "btc_ble_mesh_time_scene_model.h" +#include "btc_ble_mesh_sensor_model.h" +#include "btc_ble_mesh_lighting_model.h" + +#include "esp_ble_mesh_defs.h" +#include "esp_ble_mesh_common_api.h" +#include "esp_ble_mesh_provisioning_api.h" +#include "esp_ble_mesh_networking_api.h" + +#define BLE_MESH_MAX_DATA_SIZE 379 + +void btc_ble_mesh_prov_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) +{ + btc_ble_mesh_model_args_t *dst = (btc_ble_mesh_model_args_t *)p_dest; + btc_ble_mesh_model_args_t *src = (btc_ble_mesh_model_args_t *)p_src; + + if (!msg || !dst || !src) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + switch (msg->act) { + case BTC_BLE_MESH_ACT_SERVER_MODEL_SEND: + case BTC_BLE_MESH_ACT_CLIENT_MODEL_SEND: { + LOG_DEBUG("%s, BTC_BLE_MESH_ACT_MODEL_SEND, src->model_send.length = %d", __func__, src->model_send.length); + dst->model_send.data = src->model_send.length ? (uint8_t *)osi_malloc(src->model_send.length) : NULL; + dst->model_send.ctx = osi_malloc(sizeof(esp_ble_mesh_msg_ctx_t)); + if (src->model_send.length) { + if (dst->model_send.data) { + memcpy(dst->model_send.data, src->model_send.data, src->model_send.length); + } else { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + } + } + if (dst->model_send.ctx) { + memcpy(dst->model_send.ctx, src->model_send.ctx, sizeof(esp_ble_mesh_msg_ctx_t)); + } else { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + } + break; + } + default: + LOG_DEBUG("%s, Unknown deep copy act %d", __func__, msg->act); + break; + } +} + +void btc_ble_mesh_prov_arg_deep_free(btc_msg_t *msg) +{ + btc_ble_mesh_model_args_t *arg = NULL; + + if (!msg || !msg->arg) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + arg = (btc_ble_mesh_model_args_t *)(msg->arg); + + switch (msg->act) { + case BTC_BLE_MESH_ACT_SERVER_MODEL_SEND: + case BTC_BLE_MESH_ACT_CLIENT_MODEL_SEND: + if (arg->model_send.data) { + osi_free(arg->model_send.data); + } + if (arg->model_send.ctx) { + osi_free(arg->model_send.ctx); + } + break; + default: + break; + } + + return; +} + +static void btc_ble_mesh_copy_req_data(btc_msg_t *msg, void *p_dest, void *p_src) +{ + esp_ble_mesh_model_cb_param_t *p_dest_data = (esp_ble_mesh_model_cb_param_t *)p_dest; + esp_ble_mesh_model_cb_param_t *p_src_data = (esp_ble_mesh_model_cb_param_t *)p_src; + + if (!msg || !p_src_data || !p_dest_data) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + switch (msg->act) { + case ESP_BLE_MESH_MODEL_OPERATION_EVT: { + if (p_src_data->model_operation.ctx && p_src_data->model_operation.msg) { + p_dest_data->model_operation.ctx = osi_malloc(sizeof(esp_ble_mesh_msg_ctx_t)); + p_dest_data->model_operation.msg = p_src_data->model_operation.length ? (uint8_t *)osi_malloc(p_src_data->model_operation.length) : NULL; + if (p_dest_data->model_operation.ctx) { + memcpy(p_dest_data->model_operation.ctx, p_src_data->model_operation.ctx, sizeof(esp_ble_mesh_msg_ctx_t)); + } else { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + } + if (p_src_data->model_operation.length) { + if (p_dest_data->model_operation.msg) { + memcpy(p_dest_data->model_operation.msg, p_src_data->model_operation.msg, p_src_data->model_operation.length); + } else { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + } + } + } + break; + } + case ESP_BLE_MESH_CLIENT_MODEL_RECV_PUBLISH_MSG_EVT: { + if (p_src_data->client_recv_publish_msg.ctx && p_src_data->client_recv_publish_msg.msg) { + p_dest_data->client_recv_publish_msg.ctx = osi_malloc(sizeof(esp_ble_mesh_msg_ctx_t)); + p_dest_data->client_recv_publish_msg.msg = p_src_data->client_recv_publish_msg.length ? (uint8_t *)osi_malloc(p_src_data->client_recv_publish_msg.length) : NULL; + if (p_dest_data->client_recv_publish_msg.ctx) { + memcpy(p_dest_data->client_recv_publish_msg.ctx, p_src_data->client_recv_publish_msg.ctx, sizeof(esp_ble_mesh_msg_ctx_t)); + } else { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + } + if (p_src_data->client_recv_publish_msg.length) { + if (p_dest_data->client_recv_publish_msg.msg) { + memcpy(p_dest_data->client_recv_publish_msg.msg, p_src_data->client_recv_publish_msg.msg, p_src_data->client_recv_publish_msg.length); + } else { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + } + } + } + break; + } + case ESP_BLE_MESH_MODEL_SEND_COMP_EVT: { + if (p_src_data->model_send_comp.ctx) { + p_dest_data->model_send_comp.ctx = osi_malloc(sizeof(esp_ble_mesh_msg_ctx_t)); + if (p_dest_data->model_send_comp.ctx) { + memcpy(p_dest_data->model_send_comp.ctx, p_src_data->model_send_comp.ctx, sizeof(esp_ble_mesh_msg_ctx_t)); + } else { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + } + } + break; + } + case ESP_BLE_MESH_CLIENT_MODEL_SEND_TIMEOUT_EVT: { + if (p_src_data->client_send_timeout.ctx) { + p_dest_data->client_send_timeout.ctx = osi_malloc(sizeof(esp_ble_mesh_msg_ctx_t)); + if (p_dest_data->client_send_timeout.ctx) { + memcpy(p_dest_data->client_send_timeout.ctx, p_src_data->client_send_timeout.ctx, sizeof(esp_ble_mesh_msg_ctx_t)); + } else { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + } + } + break; + } + default: + break; + } +} + +static void btc_ble_mesh_free_req_data(btc_msg_t *msg) +{ + esp_ble_mesh_model_cb_param_t *arg = NULL; + + if (!msg || !msg->arg) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + arg = (esp_ble_mesh_model_cb_param_t *)(msg->arg); + + switch (msg->act) { + case ESP_BLE_MESH_MODEL_OPERATION_EVT: { + if (arg->model_operation.msg) { + osi_free(arg->model_operation.msg); + } + if (arg->model_operation.ctx) { + osi_free(arg->model_operation.ctx); + } + break; + } + case ESP_BLE_MESH_CLIENT_MODEL_RECV_PUBLISH_MSG_EVT: { + if (arg->client_recv_publish_msg.msg) { + osi_free(arg->client_recv_publish_msg.msg); + } + if (arg->client_recv_publish_msg.ctx) { + osi_free(arg->client_recv_publish_msg.ctx); + } + break; + } + case ESP_BLE_MESH_MODEL_SEND_COMP_EVT: { + if (arg->model_send_comp.ctx) { + osi_free(arg->model_send_comp.ctx); + } + break; + } + case ESP_BLE_MESH_CLIENT_MODEL_SEND_TIMEOUT_EVT: { + if (arg->client_send_timeout.ctx) { + osi_free(arg->client_send_timeout.ctx); + } + break; + } + default: + break; + } +} + +static inline void btc_ble_mesh_cb_to_app(esp_ble_mesh_prov_cb_event_t event, + esp_ble_mesh_prov_cb_param_t *param) +{ + esp_ble_mesh_prov_cb_t btc_mesh_cb = (esp_ble_mesh_prov_cb_t)btc_profile_cb_get(BTC_PID_PROV); + if (btc_mesh_cb) { + btc_mesh_cb(event, param); + } +} + +static inline void btc_ble_mesh_model_cb_to_app(esp_ble_mesh_model_cb_event_t event, + esp_ble_mesh_model_cb_param_t *param) +{ + esp_ble_mesh_model_cb_t btc_mesh_cb = (esp_ble_mesh_model_cb_t)btc_profile_cb_get(BTC_PID_MODEL); + if (btc_mesh_cb) { + btc_mesh_cb(event, param); + } +} + +extern u32_t mesh_opcode; + +static void btc_ble_mesh_server_model_op_cb(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + esp_ble_mesh_model_cb_param_t mesh_param = {0}; + btc_msg_t msg = {0}; + bt_status_t ret; + + mesh_param.model_operation.opcode = mesh_opcode; + mesh_param.model_operation.model = (esp_ble_mesh_model_t *)model; + mesh_param.model_operation.ctx = (esp_ble_mesh_msg_ctx_t *)ctx; + mesh_param.model_operation.length = buf->len; + mesh_param.model_operation.msg = buf->data; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_MODEL; + msg.act = ESP_BLE_MESH_MODEL_OPERATION_EVT; + ret = btc_transfer_context(&msg, &mesh_param, + sizeof(esp_ble_mesh_model_cb_param_t), btc_ble_mesh_copy_req_data); + + if (ret != BT_STATUS_SUCCESS) { + LOG_ERROR("%s btc_transfer_context failed", __func__); + } + return; +} + +static void btc_ble_mesh_client_model_op_cb(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + esp_ble_mesh_model_cb_param_t mesh_param = {0}; + bt_mesh_client_common_t *client_param = NULL; + bt_mesh_internal_data_t *data = NULL; + bt_mesh_client_node_t *node = NULL; + btc_msg_t msg = {0}; + bt_status_t ret; + + if (!model || !model->user_data || !ctx || !buf) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + client_param = (bt_mesh_client_common_t *)model->user_data; + data = (bt_mesh_internal_data_t *)client_param->internal_data; + if (!data) { + LOG_ERROR("%s, Client internal_data is NULL", __func__); + return; + } + + node = bt_mesh_is_model_message_publish(model, ctx, buf, false); + if (node == NULL) { + msg.act = ESP_BLE_MESH_CLIENT_MODEL_RECV_PUBLISH_MSG_EVT; + mesh_param.client_recv_publish_msg.opcode = mesh_opcode; + mesh_param.client_recv_publish_msg.model = (esp_ble_mesh_model_t *)model; + mesh_param.client_recv_publish_msg.ctx = (esp_ble_mesh_msg_ctx_t *)ctx; + mesh_param.client_recv_publish_msg.length = buf->len; + mesh_param.client_recv_publish_msg.msg = buf->data; + } else { + msg.act = ESP_BLE_MESH_MODEL_OPERATION_EVT; + mesh_param.model_operation.opcode = mesh_opcode; + mesh_param.model_operation.model = (esp_ble_mesh_model_t *)model; + mesh_param.model_operation.ctx = (esp_ble_mesh_msg_ctx_t *)ctx; + mesh_param.model_operation.length = buf->len; + mesh_param.model_operation.msg = buf->data; + } + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_MODEL; + if (msg.act != ESP_BLE_MESH_CLIENT_MODEL_RECV_PUBLISH_MSG_EVT) { + // Don't forget to release the node at the end. + bt_mesh_client_free_node(&data->queue, node); + } + ret = btc_transfer_context(&msg, &mesh_param, + sizeof(esp_ble_mesh_model_cb_param_t), btc_ble_mesh_copy_req_data); + + if (ret != BT_STATUS_SUCCESS) { + LOG_ERROR("%s, btc_transfer_context failed", __func__); + } + return; +} + +static void btc_ble_mesh_model_send_comp_cb(esp_ble_mesh_model_t *model, esp_ble_mesh_msg_ctx_t *ctx, u32_t opcode, int err) +{ + esp_ble_mesh_model_cb_param_t mesh_param = {0}; + btc_msg_t msg = {0}; + bt_status_t ret; + + mesh_param.model_send_comp.err_code = err; + mesh_param.model_send_comp.opcode = opcode; + mesh_param.model_send_comp.model = model; + mesh_param.model_send_comp.ctx = ctx; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_MODEL; + msg.act = ESP_BLE_MESH_MODEL_SEND_COMP_EVT; + ret = btc_transfer_context(&msg, &mesh_param, + sizeof(esp_ble_mesh_model_cb_param_t), btc_ble_mesh_copy_req_data); + + if (ret != BT_STATUS_SUCCESS) { + LOG_ERROR("%s btc_transfer_context failed", __func__); + } + return; +} + +static void btc_ble_mesh_model_publish_comp_cb(esp_ble_mesh_model_t *model, int err) +{ + esp_ble_mesh_model_cb_param_t mesh_param = {0}; + btc_msg_t msg = {0}; + bt_status_t ret; + + mesh_param.model_publish_comp.err_code = err; + mesh_param.model_publish_comp.model = model; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_MODEL; + msg.act = ESP_BLE_MESH_MODEL_PUBLISH_COMP_EVT; + ret = btc_transfer_context(&msg, &mesh_param, + sizeof(esp_ble_mesh_model_cb_param_t), NULL); + + if (ret != BT_STATUS_SUCCESS) { + LOG_ERROR("%s btc_transfer_context failed", __func__); + } + return; +} + +#if defined(CONFIG_BLE_MESH_NODE) +static void btc_oob_pub_key_cb(void) +{ + btc_msg_t msg = {0}; + + LOG_DEBUG("%s", __func__); + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_PROV; + msg.act = ESP_BLE_MESH_NODE_PROV_OOB_PUB_KEY_EVT; + + if (btc_transfer_context(&msg, NULL, 0, NULL) != BT_STATUS_SUCCESS) { + LOG_ERROR("%s btc_transfer_context failed", __func__); + } + return; +} + +static int btc_output_number_cb(bt_mesh_output_action_t act, u32_t num) +{ + esp_ble_mesh_prov_cb_param_t mesh_param = {0}; + btc_msg_t msg = {0}; + bt_status_t ret; + + LOG_DEBUG("%s", __func__); + + mesh_param.node_prov_output_num.action = (esp_ble_mesh_output_action_t)act; + mesh_param.node_prov_output_num.number = num; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_PROV; + msg.act = ESP_BLE_MESH_NODE_PROV_OUTPUT_NUMBER_EVT; + ret = btc_transfer_context(&msg, &mesh_param, + sizeof(esp_ble_mesh_prov_cb_param_t), NULL); + + if (ret != BT_STATUS_SUCCESS) { + LOG_ERROR("%s btc_transfer_context failed", __func__); + return -1; + } + return 0; +} + +static int btc_output_string_cb(const char *str) +{ + esp_ble_mesh_prov_cb_param_t mesh_param = {0}; + btc_msg_t msg = {0}; + bt_status_t ret; + + LOG_DEBUG("%s", __func__); + + strncpy(mesh_param.node_prov_output_str.string, str, strlen(str)); + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_PROV; + msg.act = ESP_BLE_MESH_NODE_PROV_OUTPUT_STRING_EVT; + ret = btc_transfer_context(&msg, &mesh_param, + sizeof(esp_ble_mesh_prov_cb_param_t), NULL); + + if (ret != BT_STATUS_SUCCESS) { + LOG_ERROR("%s btc_transfer_context failed", __func__); + return -1; + } + return 0; +} + +static int btc_input_cb(bt_mesh_input_action_t act, u8_t size) +{ + esp_ble_mesh_prov_cb_param_t mesh_param = {0}; + btc_msg_t msg = {0}; + bt_status_t ret; + + LOG_DEBUG("%s", __func__); + + mesh_param.node_prov_input.action = (esp_ble_mesh_input_action_t)act; + mesh_param.node_prov_input.size = size; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_PROV; + msg.act = ESP_BLE_MESH_NODE_PROV_INPUT_EVT; + ret = btc_transfer_context(&msg, &mesh_param, + sizeof(esp_ble_mesh_prov_cb_param_t), NULL); + + if (ret != BT_STATUS_SUCCESS) { + LOG_ERROR("%s btc_transfer_context failed", __func__); + return -1; + } + return 0; +} + +static void btc_link_open_cb(bt_mesh_prov_bearer_t bearer) +{ + esp_ble_mesh_prov_cb_param_t mesh_param = {0}; + btc_msg_t msg = {0}; + bt_status_t ret; + + LOG_DEBUG("%s", __func__); + + mesh_param.node_prov_link_open.bearer = (esp_ble_mesh_prov_bearer_t)bearer; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_PROV; + msg.act = ESP_BLE_MESH_NODE_PROV_LINK_OPEN_EVT; + ret = btc_transfer_context(&msg, &mesh_param, + sizeof(esp_ble_mesh_prov_cb_param_t), NULL); + + if (ret != BT_STATUS_SUCCESS) { + LOG_ERROR("%s btc_transfer_context failed", __func__); + } + return; +} + +static void btc_link_close_cb(bt_mesh_prov_bearer_t bearer) +{ + esp_ble_mesh_prov_cb_param_t mesh_param = {0}; + btc_msg_t msg = {0}; + bt_status_t ret; + + LOG_DEBUG("%s", __func__); + + mesh_param.node_prov_link_close.bearer = (esp_ble_mesh_prov_bearer_t)bearer; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_PROV; + msg.act = ESP_BLE_MESH_NODE_PROV_LINK_CLOSE_EVT; + ret = btc_transfer_context(&msg, &mesh_param, + sizeof(esp_ble_mesh_prov_cb_param_t), NULL); + + if (ret != BT_STATUS_SUCCESS) { + LOG_ERROR("%s btc_transfer_context failed", __func__); + } + return; +} + +static void btc_complete_cb(u16_t net_idx, u16_t addr, u8_t flags, u32_t iv_index) +{ + esp_ble_mesh_prov_cb_param_t mesh_param = {0}; + btc_msg_t msg = {0}; + bt_status_t ret; + + LOG_DEBUG("%s", __func__); + + mesh_param.node_prov_complete.net_idx = net_idx; + mesh_param.node_prov_complete.addr = addr; + mesh_param.node_prov_complete.flags = flags; + mesh_param.node_prov_complete.iv_index = iv_index; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_PROV; + msg.act = ESP_BLE_MESH_NODE_PROV_COMPLETE_EVT; + ret = btc_transfer_context(&msg, &mesh_param, + sizeof(esp_ble_mesh_prov_cb_param_t), NULL); + + if (ret != BT_STATUS_SUCCESS) { + LOG_ERROR("%s btc_transfer_context failed", __func__); + } + return; +} + +static void btc_reset_cb(void) +{ + btc_msg_t msg = {0}; + bt_status_t ret; + + LOG_DEBUG("%s", __func__); + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_PROV; + msg.act = ESP_BLE_MESH_NODE_PROV_RESET_EVT; + ret = btc_transfer_context(&msg, NULL, 0, NULL); + + if (ret != BT_STATUS_SUCCESS) { + LOG_ERROR("%s btc_transfer_context failed", __func__); + } + return; +} +#endif /* defined(CONFIG_BLE_MESH_NODE) */ + +static void btc_prov_register_complete_cb(int err_code) +{ + esp_ble_mesh_prov_cb_param_t mesh_param = {0}; + btc_msg_t msg = {0}; + bt_status_t ret; + + LOG_DEBUG("%s", __func__); + + mesh_param.prov_register_comp.err_code = err_code; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_PROV; + msg.act = ESP_BLE_MESH_PROV_REGISTER_COMP_EVT; + ret = btc_transfer_context(&msg, &mesh_param, + sizeof(esp_ble_mesh_prov_cb_param_t), NULL); + + if (ret != BT_STATUS_SUCCESS) { + LOG_ERROR("%s btc_transfer_context failed", __func__); + } + return; +} + +static void btc_client_model_timeout_cb(struct k_work *work) +{ + esp_ble_mesh_model_cb_param_t mesh_param = {0}; + bt_mesh_client_common_t *client_param = NULL; + bt_mesh_internal_data_t *data = NULL; + bt_mesh_client_node_t *node = NULL; + btc_msg_t msg = {0}; + bt_status_t ret; + + node = CONTAINER_OF(work, bt_mesh_client_node_t, timer.work); + if (!node || !node->ctx.model || !node->ctx.model->user_data) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + client_param = (bt_mesh_client_common_t *)node->ctx.model->user_data; + data = (bt_mesh_internal_data_t *)client_param->internal_data; + if (!data) { + LOG_ERROR("%s, Client internal_data is NULL", __func__); + return; + } + + mesh_param.client_send_timeout.opcode = node->opcode; + mesh_param.client_send_timeout.model = (esp_ble_mesh_model_t *)node->ctx.model; + mesh_param.client_send_timeout.ctx = (esp_ble_mesh_msg_ctx_t *)&node->ctx; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_MODEL; + msg.act = ESP_BLE_MESH_CLIENT_MODEL_SEND_TIMEOUT_EVT; + ret = btc_transfer_context(&msg, &mesh_param, + sizeof(esp_ble_mesh_model_cb_param_t), btc_ble_mesh_copy_req_data); + + if (ret != BT_STATUS_SUCCESS) { + LOG_ERROR("%s btc_transfer_context failed", __func__); + } + // Don't forget to release the node at the end. + bt_mesh_client_free_node(&data->queue, node); + return; +} + +static int btc_model_publish_update(struct bt_mesh_model *mod) +{ + esp_ble_mesh_model_cb_param_t mesh_param = {0}; + btc_msg_t msg = {0}; + bt_status_t ret; + + LOG_DEBUG("%s", __func__); + + mesh_param.model_publish_update.model = (esp_ble_mesh_model_t *)mod; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_MODEL; + msg.act = ESP_BLE_MESH_MODEL_PUBLISH_UPDATE_EVT; + ret = btc_transfer_context(&msg, &mesh_param, + sizeof(esp_ble_mesh_model_cb_param_t), NULL); + + if (ret != BT_STATUS_SUCCESS) { + LOG_ERROR("%s btc_transfer_context failed", __func__); + return -1; + } + return 0; +} + +static void btc_prov_set_complete_cb(esp_ble_mesh_prov_cb_param_t *param, uint8_t act) +{ + btc_msg_t msg = {0}; + bt_status_t ret; + + LOG_DEBUG("%s", __func__); + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_PROV; + msg.act = act; + ret = btc_transfer_context(&msg, param, + sizeof(esp_ble_mesh_prov_cb_param_t), NULL); + + if (ret != BT_STATUS_SUCCESS) { + LOG_ERROR("%s btc_transfer_context failed", __func__); + } + return; +} + +#if (CONFIG_BLE_MESH_PROVISIONER) +static void btc_provisioner_recv_unprov_adv_pkt_cb(const u8_t addr[6], const u8_t addr_type, + const u8_t adv_type, const u8_t dev_uuid[16], + u16_t oob_info, bt_mesh_prov_bearer_t bearer) +{ + esp_ble_mesh_prov_cb_param_t mesh_param = {0}; + btc_msg_t msg = {0}; + bt_status_t ret; + + LOG_DEBUG("%s", __func__); + + if (addr == NULL || dev_uuid == NULL || + (bearer != BLE_MESH_PROV_ADV && bearer != BLE_MESH_PROV_GATT)) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + memcpy(mesh_param.provisioner_recv_unprov_adv_pkt.dev_uuid, dev_uuid, 16); + memcpy(mesh_param.provisioner_recv_unprov_adv_pkt.addr, addr, ESP_BD_ADDR_LEN); + mesh_param.provisioner_recv_unprov_adv_pkt.addr_type = addr_type; + mesh_param.provisioner_recv_unprov_adv_pkt.oob_info = oob_info; + mesh_param.provisioner_recv_unprov_adv_pkt.adv_type = adv_type; + mesh_param.provisioner_recv_unprov_adv_pkt.bearer = bearer; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_PROV; + msg.act = ESP_BLE_MESH_PROVISIONER_RECV_UNPROV_ADV_PKT_EVT; + ret = btc_transfer_context(&msg, &mesh_param, + sizeof(esp_ble_mesh_prov_cb_param_t), NULL); + + if (ret != BT_STATUS_SUCCESS) { + LOG_ERROR("%s btc_transfer_context failed", __func__); + } + return; +} + +static int btc_provisioner_prov_read_oob_pub_key_cb(u8_t link_idx) +{ + esp_ble_mesh_prov_cb_param_t mesh_param = {0}; + btc_msg_t msg = {0}; + bt_status_t ret; + + LOG_DEBUG("%s", __func__); + + mesh_param.provisioner_prov_read_oob_pub_key.link_idx = link_idx; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_PROV; + msg.act = ESP_BLE_MESH_PROVISIONER_PROV_READ_OOB_PUB_KEY_EVT; + ret = btc_transfer_context(&msg, &mesh_param, + sizeof(esp_ble_mesh_prov_cb_param_t), NULL); + + if (ret != BT_STATUS_SUCCESS) { + LOG_ERROR("%s btc_transfer_context failed", __func__); + return -1; + } + return 0; +} + +static int btc_provisioner_prov_input_cb(u8_t method, bt_mesh_output_action_t act, u8_t size, u8_t link_idx) +{ + esp_ble_mesh_prov_cb_param_t mesh_param = {0}; + btc_msg_t msg = {0}; + bt_status_t ret; + + LOG_DEBUG("%s", __func__); + + mesh_param.provisioner_prov_input.method = (esp_ble_mesh_oob_method_t)method; + mesh_param.provisioner_prov_input.action = (esp_ble_mesh_output_action_t)act; + mesh_param.provisioner_prov_input.size = size; + mesh_param.provisioner_prov_input.link_idx = link_idx; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_PROV; + msg.act = ESP_BLE_MESH_PROVISIONER_PROV_INPUT_EVT; + ret = btc_transfer_context(&msg, &mesh_param, + sizeof(esp_ble_mesh_prov_cb_param_t), NULL); + + if (ret != BT_STATUS_SUCCESS) { + LOG_ERROR("%s btc_transfer_context failed", __func__); + return -1; + } + return 0; +} + +static int btc_provisioner_prov_output_cb(u8_t method, bt_mesh_input_action_t act, void *data, u8_t size, u8_t link_idx) +{ + esp_ble_mesh_prov_cb_param_t mesh_param = {0}; + btc_msg_t msg = {0}; + bt_status_t ret; + + LOG_DEBUG("%s", __func__); + + mesh_param.provisioner_prov_output.method = (esp_ble_mesh_oob_method_t)method; + mesh_param.provisioner_prov_output.action = (esp_ble_mesh_input_action_t)act; + mesh_param.provisioner_prov_output.size = size; + mesh_param.provisioner_prov_output.link_idx = link_idx; + if (act == BLE_MESH_ENTER_STRING) { + strncpy(mesh_param.provisioner_prov_output.string, (char *)data, size); + } else { + mesh_param.provisioner_prov_output.number = sys_get_le32((u8_t *)data); + } + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_PROV; + msg.act = ESP_BLE_MESH_PROVISIONER_PROV_OUTPUT_EVT; + ret = btc_transfer_context(&msg, &mesh_param, + sizeof(esp_ble_mesh_prov_cb_param_t), NULL); + + if (ret != BT_STATUS_SUCCESS) { + LOG_ERROR("%s btc_transfer_context failed", __func__); + return -1; + } + return 0; +} + +static void btc_provisioner_link_open_cb(bt_mesh_prov_bearer_t bearer) +{ + esp_ble_mesh_prov_cb_param_t mesh_param = {0}; + btc_msg_t msg = {0}; + bt_status_t ret; + + LOG_DEBUG("%s", __func__); + + mesh_param.provisioner_prov_link_open.bearer = (esp_ble_mesh_prov_bearer_t)bearer; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_PROV; + msg.act = ESP_BLE_MESH_PROVISIONER_PROV_LINK_OPEN_EVT; + ret = btc_transfer_context(&msg, &mesh_param, + sizeof(esp_ble_mesh_prov_cb_param_t), NULL); + + if (ret != BT_STATUS_SUCCESS) { + LOG_ERROR("%s btc_transfer_context failed", __func__); + } + return; +} + +static void btc_provisioner_link_close_cb(bt_mesh_prov_bearer_t bearer, u8_t reason) +{ + esp_ble_mesh_prov_cb_param_t mesh_param = {0}; + btc_msg_t msg = {0}; + bt_status_t ret; + + LOG_DEBUG("%s", __func__); + + mesh_param.provisioner_prov_link_close.bearer = (esp_ble_mesh_prov_bearer_t)bearer; + mesh_param.provisioner_prov_link_close.reason = reason; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_PROV; + msg.act = ESP_BLE_MESH_PROVISIONER_PROV_LINK_CLOSE_EVT; + ret = btc_transfer_context(&msg, &mesh_param, + sizeof(esp_ble_mesh_prov_cb_param_t), NULL); + + if (ret != BT_STATUS_SUCCESS) { + LOG_ERROR("%s btc_transfer_context failed", __func__); + } + return; +} + +static void btc_provisioner_prov_complete_cb(int node_idx, const u8_t device_uuid[16], + u16_t unicast_addr, u8_t element_num, + u16_t netkey_idx) +{ + esp_ble_mesh_prov_cb_param_t mesh_param = {0}; + btc_msg_t msg = {0}; + bt_status_t ret; + + LOG_DEBUG("%s", __func__); + + mesh_param.provisioner_prov_complete.node_idx = node_idx; + mesh_param.provisioner_prov_complete.unicast_addr = unicast_addr; + mesh_param.provisioner_prov_complete.element_num = element_num; + mesh_param.provisioner_prov_complete.netkey_idx = netkey_idx; + memcpy(mesh_param.provisioner_prov_complete.device_uuid, device_uuid, sizeof(esp_ble_mesh_octet16_t)); + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_PROV; + msg.act = ESP_BLE_MESH_PROVISIONER_PROV_COMPLETE_EVT; + ret = btc_transfer_context(&msg, &mesh_param, + sizeof(esp_ble_mesh_prov_cb_param_t), NULL); + + if (ret != BT_STATUS_SUCCESS) { + LOG_ERROR("%s btc_transfer_context failed", __func__); + } + return; +} +#endif /* CONFIG_BLE_MESH_PROVISIONER */ + +int btc_ble_mesh_client_init(esp_ble_mesh_model_t *model) +{ + __ASSERT(model && model->op, "%s, Invalid parameter", __func__); + esp_ble_mesh_model_op_t *op = model->op; + while (op != NULL && op->opcode != 0) { + op->param_cb = (esp_ble_mesh_cb_t)btc_ble_mesh_client_model_op_cb; + op++; + } + return bt_mesh_client_init((struct bt_mesh_model *)model); +} + +int32_t btc_ble_mesh_model_pub_period_get(esp_ble_mesh_model_t *mod) +{ + return bt_mesh_model_pub_period_get((struct bt_mesh_model *)mod); +} + +uint16_t btc_ble_mesh_get_primary_addr(void) +{ + return bt_mesh_primary_addr(); +} + +uint16_t *btc_ble_mesh_model_find_group(esp_ble_mesh_model_t *mod, uint16_t addr) +{ + return bt_mesh_model_find_group((struct bt_mesh_model *)mod, addr); +} + +esp_ble_mesh_elem_t *btc_ble_mesh_elem_find(u16_t addr) +{ + return (esp_ble_mesh_elem_t *)bt_mesh_elem_find(addr); +} + +uint8_t btc_ble_mesh_elem_count(void) +{ + return bt_mesh_elem_count(); +} + +esp_ble_mesh_model_t *btc_ble_mesh_model_find_vnd(const esp_ble_mesh_elem_t *elem, + uint16_t company, uint16_t id) +{ + return (esp_ble_mesh_model_t *)bt_mesh_model_find_vnd((struct bt_mesh_elem *)elem, company, id); +} + +esp_ble_mesh_model_t *btc_ble_mesh_model_find(const esp_ble_mesh_elem_t *elem, uint16_t id) +{ + return (esp_ble_mesh_model_t *)bt_mesh_model_find((struct bt_mesh_elem *)elem, id); +} + +const esp_ble_mesh_comp_t *btc_ble_mesh_comp_get(void) +{ + return (const esp_ble_mesh_comp_t *)bt_mesh_comp_get(); +} + +/* Configuration Models */ +extern const struct bt_mesh_model_op bt_mesh_cfg_srv_op[]; +extern const struct bt_mesh_model_op bt_mesh_cfg_cli_op[]; +/* Health Models */ +extern const struct bt_mesh_model_op bt_mesh_health_srv_op[]; +extern const struct bt_mesh_model_op bt_mesh_health_cli_op[]; +/* Generic Client Models */ +extern const struct bt_mesh_model_op gen_onoff_cli_op[]; +extern const struct bt_mesh_model_op gen_level_cli_op[]; +extern const struct bt_mesh_model_op gen_def_trans_time_cli_op[]; +extern const struct bt_mesh_model_op gen_power_onoff_cli_op[]; +extern const struct bt_mesh_model_op gen_power_level_cli_op[]; +extern const struct bt_mesh_model_op gen_battery_cli_op[]; +extern const struct bt_mesh_model_op gen_location_cli_op[]; +extern const struct bt_mesh_model_op gen_property_cli_op[]; +/* Lighting Client Models */ +extern const struct bt_mesh_model_op light_lightness_cli_op[]; +extern const struct bt_mesh_model_op light_ctl_cli_op[]; +extern const struct bt_mesh_model_op light_hsl_cli_op[]; +extern const struct bt_mesh_model_op light_xyl_cli_op[]; +extern const struct bt_mesh_model_op light_lc_cli_op[]; +/* Sensor Client Models */ +extern const struct bt_mesh_model_op sensor_cli_op[]; +/* Time and Scenes Client Models */ +extern const struct bt_mesh_model_op time_cli_op[]; +extern const struct bt_mesh_model_op scene_cli_op[]; +extern const struct bt_mesh_model_op scheduler_cli_op[]; + +static void btc_mesh_model_op_add(esp_ble_mesh_model_t *model) +{ + esp_ble_mesh_model_op_t *op = NULL; + + if (!model) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + /* 1. For SIG client models, model->op will be NULL and initialized here. + * 2. The vendor model opcode is 3 bytes. + */ + if ((model->op != NULL) && (model->op->opcode >= 0x10000)) { + goto add_model_op; + } + + switch (model->model_id) { + case BLE_MESH_MODEL_ID_CFG_SRV: { + model->op = (esp_ble_mesh_model_op_t *)bt_mesh_cfg_srv_op; + break; + } + case BLE_MESH_MODEL_ID_CFG_CLI: { + model->op = (esp_ble_mesh_model_op_t *)bt_mesh_cfg_cli_op; + bt_mesh_config_client_t *cli = (bt_mesh_config_client_t *)model->user_data; + if (cli != NULL) { + cli->publish_status = btc_mesh_cfg_client_publish_callback; + } + break; + } + case BLE_MESH_MODEL_ID_HEALTH_SRV: { + model->op = (esp_ble_mesh_model_op_t *)bt_mesh_health_srv_op; + break; + } + case BLE_MESH_MODEL_ID_HEALTH_CLI: { + model->op = (esp_ble_mesh_model_op_t *)bt_mesh_health_cli_op; + bt_mesh_health_client_t *cli = (bt_mesh_health_client_t *)model->user_data; + if (cli != NULL) { + cli->publish_status = btc_mesh_health_publish_callback; + } + break; + } + case BLE_MESH_MODEL_ID_GEN_ONOFF_CLI: { + model->op = ((esp_ble_mesh_model_op_t *)gen_onoff_cli_op); + bt_mesh_gen_onoff_cli_t *cli = (bt_mesh_gen_onoff_cli_t *)model->user_data; + if (cli != NULL) { + cli->publish_status = btc_mesh_generic_client_publish_callback; + } + break; + } + case BLE_MESH_MODEL_ID_GEN_LEVEL_CLI: { + model->op = ((esp_ble_mesh_model_op_t *)gen_level_cli_op); + bt_mesh_gen_level_cli_t *cli = (bt_mesh_gen_level_cli_t *)model->user_data; + if (cli != NULL) { + cli->publish_status = btc_mesh_generic_client_publish_callback; + } + break; + } + case BLE_MESH_MODEL_ID_GEN_DEF_TRANS_TIME_CLI: { + model->op = ((esp_ble_mesh_model_op_t *)gen_def_trans_time_cli_op); + bt_mesh_gen_def_trans_time_cli_t *cli = (bt_mesh_gen_def_trans_time_cli_t *)model->user_data; + if (cli != NULL) { + cli->publish_status = btc_mesh_generic_client_publish_callback; + } + break; + } + case BLE_MESH_MODEL_ID_GEN_POWER_ONOFF_CLI: { + model->op = ((esp_ble_mesh_model_op_t *)gen_power_onoff_cli_op); + bt_mesh_gen_power_onoff_cli_t *cli = (bt_mesh_gen_power_onoff_cli_t *)model->user_data; + if (cli != NULL) { + cli->publish_status = btc_mesh_generic_client_publish_callback; + } + break; + } + case BLE_MESH_MODEL_ID_GEN_POWER_LEVEL_CLI: { + model->op = ((esp_ble_mesh_model_op_t *)gen_power_level_cli_op); + bt_mesh_gen_power_level_cli_t *cli = (bt_mesh_gen_power_level_cli_t *)model->user_data; + if (cli != NULL) { + cli->publish_status = btc_mesh_generic_client_publish_callback; + } + break; + } + case BLE_MESH_MODEL_ID_GEN_BATTERY_CLI: { + model->op = ((esp_ble_mesh_model_op_t *)gen_battery_cli_op); + bt_mesh_gen_battery_cli_t *cli = (bt_mesh_gen_battery_cli_t *)model->user_data; + if (cli != NULL) { + cli->publish_status = btc_mesh_generic_client_publish_callback; + } + break; + } + case BLE_MESH_MODEL_ID_GEN_LOCATION_CLI: { + model->op = ((esp_ble_mesh_model_op_t *)gen_location_cli_op); + bt_mesh_gen_location_cli_t *cli = (bt_mesh_gen_location_cli_t *)model->user_data; + if (cli != NULL) { + cli->publish_status = btc_mesh_generic_client_publish_callback; + } + break; + } + case BLE_MESH_MODEL_ID_GEN_PROP_CLI: { + model->op = ((esp_ble_mesh_model_op_t *)gen_property_cli_op); + bt_mesh_gen_property_cli_t *cli = (bt_mesh_gen_property_cli_t *)model->user_data; + if (cli != NULL) { + cli->publish_status = btc_mesh_generic_client_publish_callback; + } + break; + } + case BLE_MESH_MODEL_ID_LIGHT_LIGHTNESS_CLI: { + model->op = ((esp_ble_mesh_model_op_t *)light_lightness_cli_op); + bt_mesh_light_lightness_cli_t *cli = (bt_mesh_light_lightness_cli_t *)model->user_data; + if (cli != NULL) { + cli->publish_status = btc_mesh_light_client_publish_callback; + } + break; + } + case BLE_MESH_MODEL_ID_LIGHT_CTL_CLI: { + model->op = ((esp_ble_mesh_model_op_t *)light_ctl_cli_op); + bt_mesh_light_ctl_cli_t *cli = (bt_mesh_light_ctl_cli_t *)model->user_data; + if (cli != NULL) { + cli->publish_status = btc_mesh_light_client_publish_callback; + } + break; + } + case BLE_MESH_MODEL_ID_LIGHT_HSL_CLI: { + model->op = ((esp_ble_mesh_model_op_t *)light_hsl_cli_op); + bt_mesh_light_hsl_cli_t *cli = (bt_mesh_light_hsl_cli_t *)model->user_data; + if (cli != NULL) { + cli->publish_status = btc_mesh_light_client_publish_callback; + } + break; + } + case BLE_MESH_MODEL_ID_LIGHT_XYL_CLI: { + model->op = ((esp_ble_mesh_model_op_t *)light_xyl_cli_op); + bt_mesh_light_xyl_cli_t *cli = (bt_mesh_light_xyl_cli_t *)model->user_data; + if (cli != NULL) { + cli->publish_status = btc_mesh_light_client_publish_callback; + } + break; + } + case BLE_MESH_MODEL_ID_LIGHT_LC_CLI: { + model->op = ((esp_ble_mesh_model_op_t *)light_lc_cli_op); + bt_mesh_light_lc_cli_t *cli = (bt_mesh_light_lc_cli_t *)model->user_data; + if (cli != NULL) { + cli->publish_status = btc_mesh_light_client_publish_callback; + } + break; + } + case BLE_MESH_MODEL_ID_SENSOR_CLI: { + model->op = ((esp_ble_mesh_model_op_t *)sensor_cli_op); + bt_mesh_sensor_client_t *cli = (bt_mesh_sensor_client_t *)model->user_data; + if (cli != NULL) { + cli->publish_status = btc_mesh_sensor_client_publish_callback; + } + break; + } + case BLE_MESH_MODEL_ID_TIME_CLI: { + model->op = ((esp_ble_mesh_model_op_t *)time_cli_op); + bt_mesh_time_scene_client_t *cli = (bt_mesh_time_scene_client_t *)model->user_data; + if (cli != NULL) { + cli->publish_status = btc_mesh_time_scene_client_publish_callback; + } + break; + } + case BLE_MESH_MODEL_ID_SCENE_CLI: { + model->op = ((esp_ble_mesh_model_op_t *)scene_cli_op); + bt_mesh_time_scene_client_t *cli = (bt_mesh_time_scene_client_t *)model->user_data; + if (cli != NULL) { + cli->publish_status = btc_mesh_time_scene_client_publish_callback; + } + break; + } + case BLE_MESH_MODEL_ID_SCHEDULER_CLI: { + model->op = ((esp_ble_mesh_model_op_t *)scheduler_cli_op); + bt_mesh_time_scene_client_t *cli = (bt_mesh_time_scene_client_t *)model->user_data; + if (cli != NULL) { + cli->publish_status = btc_mesh_time_scene_client_publish_callback; + } + break; + } + default: { + goto add_model_op; + } + } + return; + +add_model_op: + if (model->pub) { + model->pub->update = (esp_ble_mesh_cb_t)btc_model_publish_update; + } + op = model->op; + while (op != NULL && op->opcode != 0) { + op->param_cb = (esp_ble_mesh_cb_t)btc_ble_mesh_server_model_op_cb; + op++; + } + return; +} + +void btc_mesh_prov_call_handler(btc_msg_t *msg) +{ + esp_ble_mesh_prov_cb_param_t param = {0}; + btc_ble_mesh_prov_args_t *arg = NULL; + uint8_t act; + + if (!msg) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + arg = (btc_ble_mesh_prov_args_t *)(msg->arg); + + switch (msg->act) { + case BTC_BLE_MESH_ACT_MESH_INIT: { + int err_code; + for (int i = 0; i < arg->mesh_init.comp->element_count; i++) { + esp_ble_mesh_elem_t *elem = &arg->mesh_init.comp->elements[i]; + /* For SIG models */ + for (int j = 0; j < elem->sig_model_count; j++) { + esp_ble_mesh_model_t *sig_model = &elem->sig_models[j]; + /* The opcode of sig model should be 1 or 2 bytes. */ + if (sig_model && sig_model->op && (sig_model->op->opcode >= 0x10000)) { + err_code = -EINVAL; + btc_prov_register_complete_cb(err_code); + return; + } + btc_mesh_model_op_add(sig_model); + } + /* For vendor models */ + for (int k = 0; k < elem->vnd_model_count; k++) { + esp_ble_mesh_model_t *vnd_model = &elem->vnd_models[k]; + /* The opcode of vendor model should be 3 bytes. */ + if (vnd_model && vnd_model->op && vnd_model->op->opcode < 0x10000) { + err_code = -EINVAL; + btc_prov_register_complete_cb(err_code); + return; + } + btc_mesh_model_op_add(vnd_model); + } + } +#if CONFIG_BLE_MESH_NODE + arg->mesh_init.prov->oob_pub_key_cb = (esp_ble_mesh_cb_t)btc_oob_pub_key_cb; + arg->mesh_init.prov->output_num_cb = (esp_ble_mesh_cb_t)btc_output_number_cb; + arg->mesh_init.prov->output_str_cb = (esp_ble_mesh_cb_t)btc_output_string_cb; + arg->mesh_init.prov->input_cb = (esp_ble_mesh_cb_t)btc_input_cb; + arg->mesh_init.prov->link_open_cb = (esp_ble_mesh_cb_t)btc_link_open_cb; + arg->mesh_init.prov->link_close_cb = (esp_ble_mesh_cb_t)btc_link_close_cb; + arg->mesh_init.prov->complete_cb = (esp_ble_mesh_cb_t)btc_complete_cb; + arg->mesh_init.prov->reset_cb = (esp_ble_mesh_cb_t)btc_reset_cb; +#endif /* CONFIG_BLE_MESH_NODE */ +#if (CONFIG_BLE_MESH_PROVISIONER) + arg->mesh_init.prov->provisioner_prov_read_oob_pub_key = (esp_ble_mesh_cb_t)btc_provisioner_prov_read_oob_pub_key_cb; + arg->mesh_init.prov->provisioner_prov_input = (esp_ble_mesh_cb_t)btc_provisioner_prov_input_cb; + arg->mesh_init.prov->provisioner_prov_output = (esp_ble_mesh_cb_t)btc_provisioner_prov_output_cb; + arg->mesh_init.prov->provisioner_link_open = (esp_ble_mesh_cb_t)btc_provisioner_link_open_cb; + arg->mesh_init.prov->provisioner_link_close = (esp_ble_mesh_cb_t)btc_provisioner_link_close_cb; + arg->mesh_init.prov->provisioner_prov_comp = (esp_ble_mesh_cb_t)btc_provisioner_prov_complete_cb; + bt_mesh_prov_adv_pkt_cb_register(btc_provisioner_recv_unprov_adv_pkt_cb); +#endif /* CONFIG_BLE_MESH_PROVISIONER */ + err_code = bt_mesh_init((struct bt_mesh_prov *)arg->mesh_init.prov, + (struct bt_mesh_comp *)arg->mesh_init.comp); + /* Give the semaphore when BLE Mesh initialization is finished. */ + xSemaphoreGive(arg->mesh_init.semaphore); + btc_prov_register_complete_cb(err_code); + return; + } +#if CONFIG_BLE_MESH_NODE + case BTC_BLE_MESH_ACT_PROV_ENABLE: + LOG_DEBUG("%s, BTC_BLE_MESH_ACT_PROV_ENABLE, bearers = %d", __func__, arg->node_prov_enable.bearers); + act = ESP_BLE_MESH_NODE_PROV_ENABLE_COMP_EVT; + param.node_prov_enable_comp.err_code = bt_mesh_prov_enable(arg->node_prov_enable.bearers); + break; + case BTC_BLE_MESH_ACT_PROV_DISABLE: + LOG_DEBUG("%s, BTC_BLE_MESH_ACT_PROV_DISABLE, bearers = %d", __func__, arg->node_prov_disable.bearers); + act = ESP_BLE_MESH_NODE_PROV_DISABLE_COMP_EVT; + param.node_prov_disable_comp.err_code = bt_mesh_prov_disable(arg->node_prov_disable.bearers); + break; + case BTC_BLE_MESH_ACT_NODE_RESET: + LOG_DEBUG("%s, BTC_BLE_MESH_ACT_NODE_RESET", __func__); + bt_mesh_reset(); + return; + case BTC_BLE_MESH_ACT_SET_OOB_PUB_KEY: + act = ESP_BLE_MESH_NODE_PROV_SET_OOB_PUB_KEY_COMP_EVT; + param.node_prov_set_oob_pub_key_comp.err_code = + bt_mesh_set_oob_pub_key(arg->set_oob_pub_key.pub_key_x, + arg->set_oob_pub_key.pub_key_y, + arg->set_oob_pub_key.private_key); + break; + case BTC_BLE_MESH_ACT_INPUT_NUMBER: + act = ESP_BLE_MESH_NODE_PROV_INPUT_NUMBER_COMP_EVT; + param.node_prov_input_num_comp.err_code = bt_mesh_input_number(arg->input_number.number); + break; + case BTC_BLE_MESH_ACT_INPUT_STRING: + act = ESP_BLE_MESH_NODE_PROV_INPUT_STRING_COMP_EVT; + param.node_prov_input_str_comp.err_code = bt_mesh_input_string(arg->input_string.string); + break; + case BTC_BLE_MESH_ACT_SET_DEVICE_NAME: + act = ESP_BLE_MESH_NODE_SET_UNPROV_DEV_NAME_COMP_EVT; + param.node_set_unprov_dev_name_comp.err_code = bt_mesh_set_device_name(arg->set_device_name.name); + break; +#if defined(CONFIG_BLE_MESH_GATT_PROXY) + case BTC_BLE_MESH_ACT_PROXY_IDENTITY_ENABLE: + act = ESP_BLE_MESH_NODE_PROXY_IDENTITY_ENABLE_COMP_EVT; + param.node_proxy_identity_enable_comp.err_code = bt_mesh_proxy_identity_enable(); + break; + case BTC_BLE_MESH_ACT_PROXY_GATT_ENABLE: + act = ESP_BLE_MESH_NODE_PROXY_GATT_ENABLE_COMP_EVT; + param.node_proxy_gatt_enable_comp.err_code = bt_mesh_proxy_gatt_enable(); + break; + case BTC_BLE_MESH_ACT_PROXY_GATT_DISABLE: + act = ESP_BLE_MESH_NODE_PROXY_GATT_DISABLE_COMP_EVT; + param.node_proxy_gatt_disable_comp.err_code = bt_mesh_proxy_gatt_disable(); + break; +#endif /* CONFIG_BLE_MESH_GATT_PROXY */ +#endif /* CONFIG_BLE_MESH_NODE */ +#if (CONFIG_BLE_MESH_PROVISIONER) + case BTC_BLE_MESH_ACT_PROVISIONER_READ_OOB_PUB_KEY: + act = ESP_BLE_MESH_PROVISIONER_PROV_READ_OOB_PUB_KEY_COMP_EVT; + param.provisioner_prov_read_oob_pub_key_comp.err_code = + bt_mesh_prov_read_oob_pub_key(arg->provisioner_read_oob_pub_key.link_idx, + arg->provisioner_read_oob_pub_key.pub_key_x, + arg->provisioner_read_oob_pub_key.pub_key_y); + break; + case BTC_BLE_MESH_ACT_PROVISIONER_INPUT_STR: + act = ESP_BLE_MESH_PROVISIONER_PROV_INPUT_STRING_COMP_EVT; + param.provisioner_prov_input_str_comp.err_code = + bt_mesh_prov_set_oob_input_data(arg->provisioner_input_str.link_idx, + (const u8_t *)&arg->provisioner_input_str.string, false); + break; + case BTC_BLE_MESH_ACT_PROVISIONER_INPUT_NUM: + act = ESP_BLE_MESH_PROVISIONER_PROV_INPUT_NUMBER_COMP_EVT; + param.provisioner_prov_input_num_comp.err_code = + bt_mesh_prov_set_oob_input_data(arg->provisioner_input_num.link_idx, + (const u8_t *)&arg->provisioner_input_num.number, true); + break; + case BTC_BLE_MESH_ACT_PROVISIONER_ENABLE: + act = ESP_BLE_MESH_PROVISIONER_PROV_ENABLE_COMP_EVT; + param.provisioner_prov_enable_comp.err_code = + bt_mesh_provisioner_enable(arg->provisioner_enable.bearers); + break; + case BTC_BLE_MESH_ACT_PROVISIONER_DISABLE: + act = ESP_BLE_MESH_PROVISIONER_PROV_DISABLE_COMP_EVT; + param.provisioner_prov_disable_comp.err_code = + bt_mesh_provisioner_disable(arg->provisioner_disable.bearers); + break; + case BTC_BLE_MESH_ACT_PROVISIONER_DEV_ADD: { + struct bt_mesh_unprov_dev_add add_dev = {0}; + add_dev.addr_type = arg->provisioner_dev_add.add_dev.addr_type; + add_dev.oob_info = arg->provisioner_dev_add.add_dev.oob_info; + add_dev.bearer = arg->provisioner_dev_add.add_dev.bearer; + memcpy(add_dev.addr, arg->provisioner_dev_add.add_dev.addr, 6); + memcpy(add_dev.uuid, arg->provisioner_dev_add.add_dev.uuid, 16); + act = ESP_BLE_MESH_PROVISIONER_ADD_UNPROV_DEV_COMP_EVT; + param.provisioner_add_unprov_dev_comp.err_code = + bt_mesh_provisioner_add_unprov_dev(&add_dev, arg->provisioner_dev_add.flags); + break; + } + case BTC_BLE_MESH_ACT_PROVISIONER_DEV_DEL: { + struct bt_mesh_device_delete del_dev = {0}; + if (arg->provisioner_dev_del.del_dev.flag & DEL_DEV_ADDR_FLAG) { + del_dev.addr_type = arg->provisioner_dev_del.del_dev.addr_type; + memcpy(del_dev.addr, arg->provisioner_dev_del.del_dev.addr, 6); + } else if (arg->provisioner_dev_del.del_dev.flag & DEL_DEV_UUID_FLAG) { + memcpy(del_dev.uuid, arg->provisioner_dev_del.del_dev.uuid, 16); + } + act = ESP_BLE_MESH_PROVISIONER_DELETE_DEV_COMP_EVT; + param.provisioner_delete_dev_comp.err_code = bt_mesh_provisioner_delete_device(&del_dev); + break; + } + case BTC_BLE_MESH_ACT_PROVISIONER_SET_DEV_UUID_MATCH: + act = ESP_BLE_MESH_PROVISIONER_SET_DEV_UUID_MATCH_COMP_EVT; + param.provisioner_set_dev_uuid_match_comp.err_code = + bt_mesh_provisioner_set_dev_uuid_match(arg->set_dev_uuid_match.offset, + arg->set_dev_uuid_match.match_len, + arg->set_dev_uuid_match.match_val, + arg->set_dev_uuid_match.prov_after_match); + break; + case BTC_BLE_MESH_ACT_PROVISIONER_SET_PROV_DATA_INFO: { + struct bt_mesh_prov_data_info info = {0}; + info.flag = arg->set_prov_data_info.prov_data.flag; + if (arg->set_prov_data_info.prov_data.flag & PROV_DATA_NET_IDX_FLAG) { + info.net_idx = arg->set_prov_data_info.prov_data.net_idx; + } else if (arg->set_prov_data_info.prov_data.flag & PROV_DATA_FLAGS_FLAG) { + info.flags = arg->set_prov_data_info.prov_data.flags; + } else if (arg->set_prov_data_info.prov_data.flag & PROV_DATA_IV_INDEX_FLAG) { + info.iv_index = arg->set_prov_data_info.prov_data.iv_index; + } + act = ESP_BLE_MESH_PROVISIONER_SET_PROV_DATA_INFO_COMP_EVT; + param.provisioner_set_prov_data_info_comp.err_code = + bt_mesh_provisioner_set_prov_data_info(&info); + break; + } + case BTC_BLE_MESH_ACT_PROVISIONER_SET_NODE_NAME: + act = ESP_BLE_MESH_PROVISIONER_SET_NODE_NAME_COMP_EVT; + param.provisioner_set_node_name_comp.err_code = + bt_mesh_provisioner_set_node_name(arg->set_node_name.index, arg->set_node_name.name); + if (param.provisioner_set_node_name_comp.err_code) { + param.provisioner_set_node_name_comp.node_index = ESP_BLE_MESH_INVALID_NODE_INDEX; + } else { + param.provisioner_set_node_name_comp.node_index = arg->set_node_name.index; + } + break; + case BTC_BLE_MESH_ACT_PROVISIONER_SET_LOCAL_APP_KEY: { + const u8_t *app_key = NULL; + const u8_t zero[16] = {0}; + if (memcmp(arg->add_local_app_key.app_key, zero, 16)) { + app_key = arg->add_local_app_key.app_key; + } + act = ESP_BLE_MESH_PROVISIONER_ADD_LOCAL_APP_KEY_COMP_EVT; + param.provisioner_add_app_key_comp.err_code = + bt_mesh_provisioner_local_app_key_add(app_key, arg->add_local_app_key.net_idx, + &arg->add_local_app_key.app_idx); + if (param.provisioner_add_app_key_comp.err_code) { + param.provisioner_add_app_key_comp.app_idx = ESP_BLE_MESH_KEY_UNUSED; + } else { + param.provisioner_add_app_key_comp.app_idx = arg->add_local_app_key.app_idx; + } + break; + } + case BTC_BLE_MESH_ACT_PROVISIONER_BIND_LOCAL_MOD_APP: + act = ESP_BLE_MESH_PROVISIONER_BIND_APP_KEY_TO_MODEL_COMP_EVT; + param.provisioner_bind_app_key_to_model_comp.err_code = + bt_mesh_provisioner_bind_local_model_app_idx(arg->local_mod_app_bind.elem_addr, + arg->local_mod_app_bind.model_id, + arg->local_mod_app_bind.cid, + arg->local_mod_app_bind.app_idx); + break; + case BTC_BLE_MESH_ACT_PROVISIONER_ADD_LOCAL_NET_KEY: { + const u8_t *net_key = NULL; + const u8_t zero[16] = {0}; + if (memcmp(arg->add_local_net_key.net_key, zero, 16)) { + net_key = arg->add_local_net_key.net_key; + } + act = ESP_BLE_MESH_PROVISIONER_ADD_LOCAL_NET_KEY_COMP_EVT; + param.provisioner_add_net_key_comp.err_code = + bt_mesh_provisioner_local_net_key_add(net_key, &arg->add_local_net_key.net_idx); + if (param.provisioner_add_net_key_comp.err_code) { + param.provisioner_add_net_key_comp.net_idx = ESP_BLE_MESH_KEY_UNUSED; + } else { + param.provisioner_add_net_key_comp.net_idx = arg->add_local_net_key.net_idx; + } + break; + } +#endif /* CONFIG_BLE_MESH_PROVISIONER */ +#if (CONFIG_BLE_MESH_FAST_PROV) + case BTC_BLE_MESH_ACT_SET_FAST_PROV_INFO: + act = ESP_BLE_MESH_SET_FAST_PROV_INFO_COMP_EVT; + param.set_fast_prov_info_comp.status_unicast = + bt_mesh_set_fast_prov_unicast_addr_range(arg->set_fast_prov_info.unicast_min, + arg->set_fast_prov_info.unicast_max); + param.set_fast_prov_info_comp.status_net_idx = + bt_mesh_set_fast_prov_net_idx(arg->set_fast_prov_info.net_idx); + bt_mesh_set_fast_prov_flags_iv_index(arg->set_fast_prov_info.flags, + arg->set_fast_prov_info.iv_index); + param.set_fast_prov_info_comp.status_match = + bt_mesh_provisioner_set_dev_uuid_match(arg->set_fast_prov_info.offset, + arg->set_fast_prov_info.match_len, + arg->set_fast_prov_info.match_val, false); + break; + case BTC_BLE_MESH_ACT_SET_FAST_PROV_ACTION: + act = ESP_BLE_MESH_SET_FAST_PROV_ACTION_COMP_EVT; + param.set_fast_prov_action_comp.status_action = + bt_mesh_set_fast_prov_action(arg->set_fast_prov_action.action); + break; +#endif /* (CONFIG_BLE_MESH_FAST_PROV) */ + default: + LOG_WARN("%s, Invalid msg->act %d", __func__, msg->act); + return; + } + + btc_prov_set_complete_cb(¶m, act); + return; +} + +void btc_mesh_model_call_handler(btc_msg_t *msg) +{ + btc_ble_mesh_model_args_t *arg = NULL; + + if (!msg || !msg->arg) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + arg = (btc_ble_mesh_model_args_t *)(msg->arg); + + switch (msg->act) { + case BTC_BLE_MESH_ACT_MODEL_PUBLISH: { + if (arg->model_publish.device_role == PROVISIONER) { + bt_mesh_role_param_t common = {0}; + common.model = (struct bt_mesh_model *)(arg->model_publish.model); + common.role = arg->model_publish.device_role; + if (bt_mesh_set_model_role(&common)) { + LOG_ERROR("%s, Failed to set model role", __func__); + return; + } + } + int err = bt_mesh_model_publish((struct bt_mesh_model *)arg->model_publish.model); + btc_ble_mesh_model_publish_comp_cb(arg->model_publish.model, err); + break; + } + case BTC_BLE_MESH_ACT_SERVER_MODEL_SEND: { + /* arg->model_send.length contains opcode & message, 8 is used for TransMIC */ + struct net_buf_simple *buf = bt_mesh_alloc_buf(arg->model_send.length + 8); + if (!buf) { + LOG_ERROR("%s, Failed to allocate memory", __func__); + return; + } + net_buf_simple_add_mem(buf, arg->model_send.data, arg->model_send.length); + arg->model_send.ctx->srv_send = true; + int err = bt_mesh_model_send((struct bt_mesh_model *)arg->model_send.model, + (struct bt_mesh_msg_ctx *)arg->model_send.ctx, + buf, NULL, NULL); + bt_mesh_free_buf(buf); + btc_ble_mesh_model_send_comp_cb(arg->model_send.model, arg->model_send.ctx, + arg->model_send.opcode, err); + break; + } + case BTC_BLE_MESH_ACT_CLIENT_MODEL_SEND: { + bt_mesh_role_param_t common = {0}; + /* arg->model_send.length contains opcode & message, 8 is used for TransMIC */ + struct net_buf_simple *buf = bt_mesh_alloc_buf(arg->model_send.length + 8); + if (!buf) { + LOG_ERROR("%s, Failed to allocate memory", __func__); + return; + } + net_buf_simple_add_mem(buf, arg->model_send.data, arg->model_send.length); + arg->model_send.ctx->srv_send = false; + common.model = (struct bt_mesh_model *)(arg->model_send.model); + common.role = arg->model_send.device_role; + if (bt_mesh_set_model_role(&common)) { + LOG_ERROR("%s, Failed to set model role", __func__); + return; + } + int err = bt_mesh_client_send_msg((struct bt_mesh_model *)arg->model_send.model, + arg->model_send.opcode, + (struct bt_mesh_msg_ctx *)arg->model_send.ctx, buf, + btc_client_model_timeout_cb, arg->model_send.msg_timeout, + arg->model_send.need_rsp, NULL, NULL); + bt_mesh_free_buf(buf); + btc_ble_mesh_model_send_comp_cb(arg->model_send.model, arg->model_send.ctx, + arg->model_send.opcode, err); + break; + } + default: + LOG_WARN("%s, Unknown msg->act %d", __func__, msg->act); + break; + } + + btc_ble_mesh_prov_arg_deep_free(msg); +} + +void btc_mesh_model_cb_handler(btc_msg_t *msg) +{ + esp_ble_mesh_model_cb_param_t *param = NULL; + + if (!msg || !msg->arg) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + param = (esp_ble_mesh_model_cb_param_t *)(msg->arg); + + if (msg->act < ESP_BLE_MESH_MODEL_EVT_MAX) { + btc_ble_mesh_model_cb_to_app(msg->act, param); + } else { + LOG_ERROR("%s, Unknown msg->act = %d", __func__, msg->act); + } + + btc_ble_mesh_free_req_data(msg); +} + +void btc_mesh_prov_cb_handler(btc_msg_t *msg) +{ + esp_ble_mesh_prov_cb_param_t *param = NULL; + + if (!msg) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + param = (esp_ble_mesh_prov_cb_param_t *)(msg->arg); + + if (msg->act < ESP_BLE_MESH_PROV_EVT_MAX) { + btc_ble_mesh_cb_to_app(msg->act, param); + } else { + LOG_ERROR("%s, Unknown msg->act = %d", __func__, msg->act); + } +} diff --git a/components/bt/ble_mesh/btc/btc_ble_mesh_sensor_model.c b/components/bt/ble_mesh/btc/btc_ble_mesh_sensor_model.c new file mode 100644 index 0000000000..53d773842f --- /dev/null +++ b/components/bt/ble_mesh/btc/btc_ble_mesh_sensor_model.c @@ -0,0 +1,632 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 + +#include "btc/btc_manage.h" +#include "osi/allocator.h" + +#include "sensor_client.h" +#include "btc_ble_mesh_sensor_model.h" +#include "esp_ble_mesh_sensor_model_api.h" + +static inline void btc_ble_mesh_cb_to_app(esp_ble_mesh_sensor_client_cb_event_t event, + esp_ble_mesh_sensor_client_cb_param_t *param) +{ + esp_ble_mesh_sensor_client_cb_t btc_mesh_cb = (esp_ble_mesh_sensor_client_cb_t)btc_profile_cb_get(BTC_PID_SENSOR_CLIENT); + if (btc_mesh_cb) { + btc_mesh_cb(event, param); + } +} + +void btc_ble_mesh_sensor_client_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) +{ + btc_ble_mesh_sensor_client_args_t *dst = (btc_ble_mesh_sensor_client_args_t *)p_dest; + btc_ble_mesh_sensor_client_args_t *src = (btc_ble_mesh_sensor_client_args_t *)p_src; + u32_t opcode; + u16_t length; + + if (!msg || !dst || !src) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + switch (msg->act) { + case BTC_BLE_MESH_ACT_SENSOR_CLIENT_GET_STATE: { + dst->sensor_client_get_state.params = (esp_ble_mesh_client_common_param_t *)osi_malloc(sizeof(esp_ble_mesh_client_common_param_t)); + dst->sensor_client_get_state.get_state = (esp_ble_mesh_sensor_client_get_state_t *)osi_malloc(sizeof(esp_ble_mesh_sensor_client_get_state_t)); + if (dst->sensor_client_get_state.params && dst->sensor_client_get_state.get_state) { + memcpy(dst->sensor_client_get_state.params, src->sensor_client_get_state.params, + sizeof(esp_ble_mesh_client_common_param_t)); + memcpy(dst->sensor_client_get_state.get_state, src->sensor_client_get_state.get_state, + sizeof(esp_ble_mesh_sensor_client_get_state_t)); + + opcode = src->sensor_client_get_state.params->opcode; + switch (opcode) { + case ESP_BLE_MESH_MODEL_OP_SENSOR_COLUMN_GET: + if (src->sensor_client_get_state.get_state->column_get.raw_value_x) { + length = src->sensor_client_get_state.get_state->column_get.raw_value_x->len; + dst->sensor_client_get_state.get_state->column_get.raw_value_x = bt_mesh_alloc_buf(length); + if (!dst->sensor_client_get_state.get_state->column_get.raw_value_x) { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + return; + } + net_buf_simple_add_mem(dst->sensor_client_get_state.get_state->column_get.raw_value_x, + src->sensor_client_get_state.get_state->column_get.raw_value_x->data, + src->sensor_client_get_state.get_state->column_get.raw_value_x->len); + } + break; + case ESP_BLE_MESH_MODEL_OP_SENSOR_SERIES_GET: + if (src->sensor_client_get_state.get_state->series_get.raw_value_x1) { + length = src->sensor_client_get_state.get_state->series_get.raw_value_x1->len; + dst->sensor_client_get_state.get_state->series_get.raw_value_x1 = bt_mesh_alloc_buf(length); + if (!dst->sensor_client_get_state.get_state->series_get.raw_value_x1) { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + return; + } + net_buf_simple_add_mem(dst->sensor_client_get_state.get_state->series_get.raw_value_x1, + src->sensor_client_get_state.get_state->series_get.raw_value_x1->data, + src->sensor_client_get_state.get_state->series_get.raw_value_x1->len); + } + if (src->sensor_client_get_state.get_state->series_get.raw_value_x2) { + length = src->sensor_client_get_state.get_state->series_get.raw_value_x2->len; + dst->sensor_client_get_state.get_state->series_get.raw_value_x2 = bt_mesh_alloc_buf(length); + if (!dst->sensor_client_get_state.get_state->series_get.raw_value_x2) { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + return; + } + net_buf_simple_add_mem(dst->sensor_client_get_state.get_state->series_get.raw_value_x2, + src->sensor_client_get_state.get_state->series_get.raw_value_x2->data, + src->sensor_client_get_state.get_state->series_get.raw_value_x2->len); + } + break; + default: + break; + } + } else { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + } + break; + } + case BTC_BLE_MESH_ACT_SENSOR_CLIENT_SET_STATE: { + dst->sensor_client_set_state.params = (esp_ble_mesh_client_common_param_t *)osi_malloc(sizeof(esp_ble_mesh_client_common_param_t)); + dst->sensor_client_set_state.set_state = (esp_ble_mesh_sensor_client_set_state_t *)osi_malloc(sizeof(esp_ble_mesh_sensor_client_set_state_t)); + if (dst->sensor_client_set_state.params && dst->sensor_client_set_state.set_state) { + memcpy(dst->sensor_client_set_state.params, src->sensor_client_set_state.params, + sizeof(esp_ble_mesh_client_common_param_t)); + memcpy(dst->sensor_client_set_state.set_state, src->sensor_client_set_state.set_state, + sizeof(esp_ble_mesh_sensor_client_set_state_t)); + + opcode = src->sensor_client_set_state.params->opcode; + switch (opcode) { + case ESP_BLE_MESH_MODEL_OP_SENSOR_CADENCE_SET: + if (src->sensor_client_set_state.set_state->cadence_set.status_trigger_delta_down) { + length = src->sensor_client_set_state.set_state->cadence_set.status_trigger_delta_down->len; + dst->sensor_client_set_state.set_state->cadence_set.status_trigger_delta_down = bt_mesh_alloc_buf(length); + if (!dst->sensor_client_set_state.set_state->cadence_set.status_trigger_delta_down) { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + return; + } + net_buf_simple_add_mem(dst->sensor_client_set_state.set_state->cadence_set.status_trigger_delta_down, + src->sensor_client_set_state.set_state->cadence_set.status_trigger_delta_down->data, + src->sensor_client_set_state.set_state->cadence_set.status_trigger_delta_down->len); + } + if (src->sensor_client_set_state.set_state->cadence_set.status_trigger_delta_up) { + length = src->sensor_client_set_state.set_state->cadence_set.status_trigger_delta_up->len; + dst->sensor_client_set_state.set_state->cadence_set.status_trigger_delta_up = bt_mesh_alloc_buf(length); + if (!dst->sensor_client_set_state.set_state->cadence_set.status_trigger_delta_up) { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + return; + } + net_buf_simple_add_mem(dst->sensor_client_set_state.set_state->cadence_set.status_trigger_delta_up, + src->sensor_client_set_state.set_state->cadence_set.status_trigger_delta_up->data, + src->sensor_client_set_state.set_state->cadence_set.status_trigger_delta_up->len); + } + if (src->sensor_client_set_state.set_state->cadence_set.fast_cadence_low) { + length = src->sensor_client_set_state.set_state->cadence_set.fast_cadence_low->len; + dst->sensor_client_set_state.set_state->cadence_set.fast_cadence_low = bt_mesh_alloc_buf(length); + if (!dst->sensor_client_set_state.set_state->cadence_set.fast_cadence_low) { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + return; + } + net_buf_simple_add_mem(dst->sensor_client_set_state.set_state->cadence_set.fast_cadence_low, + src->sensor_client_set_state.set_state->cadence_set.fast_cadence_low->data, + src->sensor_client_set_state.set_state->cadence_set.fast_cadence_low->len); + } + if (src->sensor_client_set_state.set_state->cadence_set.fast_cadence_high) { + length = src->sensor_client_set_state.set_state->cadence_set.fast_cadence_high->len; + dst->sensor_client_set_state.set_state->cadence_set.fast_cadence_high = bt_mesh_alloc_buf(length); + if (!dst->sensor_client_set_state.set_state->cadence_set.fast_cadence_high) { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + return; + } + net_buf_simple_add_mem(dst->sensor_client_set_state.set_state->cadence_set.fast_cadence_high, + src->sensor_client_set_state.set_state->cadence_set.fast_cadence_high->data, + src->sensor_client_set_state.set_state->cadence_set.fast_cadence_high->len); + } + break; + case ESP_BLE_MESH_MODEL_OP_SENSOR_SETTING_SET: + if (src->sensor_client_set_state.set_state->setting_set.sensor_setting_raw) { + length = src->sensor_client_set_state.set_state->setting_set.sensor_setting_raw->len; + dst->sensor_client_set_state.set_state->setting_set.sensor_setting_raw = bt_mesh_alloc_buf(length); + if (!dst->sensor_client_set_state.set_state->setting_set.sensor_setting_raw) { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + return; + } + net_buf_simple_add_mem(dst->sensor_client_set_state.set_state->setting_set.sensor_setting_raw, + src->sensor_client_set_state.set_state->setting_set.sensor_setting_raw->data, + src->sensor_client_set_state.set_state->setting_set.sensor_setting_raw->len); + } + break; + default: + break; + } + } else { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + } + break; + } + default: + LOG_DEBUG("%s, Unknown deep copy act %d", __func__, msg->act); + break; + } +} + +static void btc_ble_mesh_copy_req_data(btc_msg_t *msg, void *p_dest, void *p_src) +{ + esp_ble_mesh_sensor_client_cb_param_t *p_dest_data = (esp_ble_mesh_sensor_client_cb_param_t *)p_dest; + esp_ble_mesh_sensor_client_cb_param_t *p_src_data = (esp_ble_mesh_sensor_client_cb_param_t *)p_src; + u32_t opcode; + u16_t length; + + if (!msg || !p_src_data || !p_dest_data) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + switch (msg->act) { + case ESP_BLE_MESH_SENSOR_CLIENT_GET_STATE_EVT: + case ESP_BLE_MESH_SENSOR_CLIENT_SET_STATE_EVT: + case ESP_BLE_MESH_SENSOR_CLIENT_PUBLISH_EVT: + if (p_src_data->params) { + opcode = p_src_data->params->opcode; + switch (opcode) { + case ESP_BLE_MESH_MODEL_OP_SENSOR_DESCRIPTOR_GET: + case ESP_BLE_MESH_MODEL_OP_SENSOR_DESCRIPTOR_STATUS: + if (p_src_data->status_cb.descriptor_status.descriptor) { + length = p_src_data->status_cb.descriptor_status.descriptor->len; + p_dest_data->status_cb.descriptor_status.descriptor = bt_mesh_alloc_buf(length); + if (!p_dest_data->status_cb.descriptor_status.descriptor) { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + return; + } + net_buf_simple_add_mem(p_dest_data->status_cb.descriptor_status.descriptor, + p_src_data->status_cb.descriptor_status.descriptor->data, + p_src_data->status_cb.descriptor_status.descriptor->len); + } + break; + case ESP_BLE_MESH_MODEL_OP_SENSOR_CADENCE_GET: + case ESP_BLE_MESH_MODEL_OP_SENSOR_CADENCE_SET: + case ESP_BLE_MESH_MODEL_OP_SENSOR_CADENCE_STATUS: + if (p_src_data->status_cb.cadence_status.sensor_cadence_value) { + length = p_src_data->status_cb.cadence_status.sensor_cadence_value->len; + p_dest_data->status_cb.cadence_status.sensor_cadence_value = bt_mesh_alloc_buf(length); + if (!p_dest_data->status_cb.cadence_status.sensor_cadence_value) { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + return; + } + net_buf_simple_add_mem(p_dest_data->status_cb.cadence_status.sensor_cadence_value, + p_src_data->status_cb.cadence_status.sensor_cadence_value->data, + p_src_data->status_cb.cadence_status.sensor_cadence_value->len); + } + break; + case ESP_BLE_MESH_MODEL_OP_SENSOR_SETTINGS_GET: + case ESP_BLE_MESH_MODEL_OP_SENSOR_SETTINGS_STATUS: + if (p_src_data->status_cb.settings_status.sensor_setting_property_ids) { + length = p_src_data->status_cb.settings_status.sensor_setting_property_ids->len; + p_dest_data->status_cb.settings_status.sensor_setting_property_ids = bt_mesh_alloc_buf(length); + if (!p_dest_data->status_cb.settings_status.sensor_setting_property_ids) { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + return; + } + net_buf_simple_add_mem(p_dest_data->status_cb.settings_status.sensor_setting_property_ids, + p_src_data->status_cb.settings_status.sensor_setting_property_ids->data, + p_src_data->status_cb.settings_status.sensor_setting_property_ids->len); + } + break; + case ESP_BLE_MESH_MODEL_OP_SENSOR_SETTING_GET: + case ESP_BLE_MESH_MODEL_OP_SENSOR_SETTING_SET: + case ESP_BLE_MESH_MODEL_OP_SENSOR_SETTING_STATUS: + if (p_src_data->status_cb.setting_status.sensor_setting_raw) { + length = p_src_data->status_cb.setting_status.sensor_setting_raw->len; + p_dest_data->status_cb.setting_status.sensor_setting_raw = bt_mesh_alloc_buf(length); + if (!p_dest_data->status_cb.setting_status.sensor_setting_raw) { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + return; + } + net_buf_simple_add_mem(p_dest_data->status_cb.setting_status.sensor_setting_raw, + p_src_data->status_cb.setting_status.sensor_setting_raw->data, + p_src_data->status_cb.setting_status.sensor_setting_raw->len); + } + break; + case ESP_BLE_MESH_MODEL_OP_SENSOR_GET: + case ESP_BLE_MESH_MODEL_OP_SENSOR_STATUS: + if (p_src_data->status_cb.sensor_status.marshalled_sensor_data) { + length = p_src_data->status_cb.sensor_status.marshalled_sensor_data->len; + p_dest_data->status_cb.sensor_status.marshalled_sensor_data = bt_mesh_alloc_buf(length); + if (!p_dest_data->status_cb.sensor_status.marshalled_sensor_data) { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + return; + } + net_buf_simple_add_mem(p_dest_data->status_cb.sensor_status.marshalled_sensor_data, + p_src_data->status_cb.sensor_status.marshalled_sensor_data->data, + p_src_data->status_cb.sensor_status.marshalled_sensor_data->len); + } + break; + case ESP_BLE_MESH_MODEL_OP_SENSOR_COLUMN_GET: + case ESP_BLE_MESH_MODEL_OP_SENSOR_COLUMN_STATUS: + if (p_src_data->status_cb.column_status.sensor_column_value) { + length = p_src_data->status_cb.column_status.sensor_column_value->len; + p_dest_data->status_cb.column_status.sensor_column_value = bt_mesh_alloc_buf(length); + if (!p_dest_data->status_cb.column_status.sensor_column_value) { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + return; + } + net_buf_simple_add_mem(p_dest_data->status_cb.column_status.sensor_column_value, + p_src_data->status_cb.column_status.sensor_column_value->data, + p_src_data->status_cb.column_status.sensor_column_value->len); + } + break; + case ESP_BLE_MESH_MODEL_OP_SENSOR_SERIES_GET: + case ESP_BLE_MESH_MODEL_OP_SENSOR_SERIES_STATUS: + if (p_src_data->status_cb.series_status.sensor_series_value) { + length = p_src_data->status_cb.series_status.sensor_series_value->len; + p_dest_data->status_cb.series_status.sensor_series_value = bt_mesh_alloc_buf(length); + if (!p_dest_data->status_cb.series_status.sensor_series_value) { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + return; + } + net_buf_simple_add_mem(p_dest_data->status_cb.series_status.sensor_series_value, + p_src_data->status_cb.series_status.sensor_series_value->data, + p_src_data->status_cb.series_status.sensor_series_value->len); + } + break; + default: + break; + } + } + case ESP_BLE_MESH_SENSOR_CLIENT_TIMEOUT_EVT: + if (p_src_data->params) { + p_dest_data->params = osi_malloc(sizeof(esp_ble_mesh_client_common_param_t)); + if (p_dest_data->params) { + memcpy(p_dest_data->params, p_src_data->params, sizeof(esp_ble_mesh_client_common_param_t)); + } else { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + } + } + break; + default: + break; + } +} + +static void btc_ble_mesh_free_req_data(btc_msg_t *msg) +{ + esp_ble_mesh_sensor_client_cb_param_t *arg = NULL; + u32_t opcode; + + if (!msg || !msg->arg) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + arg = (esp_ble_mesh_sensor_client_cb_param_t *)(msg->arg); + + switch (msg->act) { + case ESP_BLE_MESH_SENSOR_CLIENT_GET_STATE_EVT: + case ESP_BLE_MESH_SENSOR_CLIENT_SET_STATE_EVT: + case ESP_BLE_MESH_SENSOR_CLIENT_PUBLISH_EVT: + if (arg->params) { + opcode = arg->params->opcode; + switch (opcode) { + case ESP_BLE_MESH_MODEL_OP_SENSOR_DESCRIPTOR_GET: + case ESP_BLE_MESH_MODEL_OP_SENSOR_DESCRIPTOR_STATUS: + bt_mesh_free_buf(arg->status_cb.descriptor_status.descriptor); + break; + case ESP_BLE_MESH_MODEL_OP_SENSOR_CADENCE_GET: + case ESP_BLE_MESH_MODEL_OP_SENSOR_CADENCE_SET: + case ESP_BLE_MESH_MODEL_OP_SENSOR_CADENCE_STATUS: + bt_mesh_free_buf(arg->status_cb.cadence_status.sensor_cadence_value); + break; + case ESP_BLE_MESH_MODEL_OP_SENSOR_SETTINGS_GET: + case ESP_BLE_MESH_MODEL_OP_SENSOR_SETTINGS_STATUS: + bt_mesh_free_buf(arg->status_cb.settings_status.sensor_setting_property_ids); + break; + case ESP_BLE_MESH_MODEL_OP_SENSOR_SETTING_GET: + case ESP_BLE_MESH_MODEL_OP_SENSOR_SETTING_SET: + case ESP_BLE_MESH_MODEL_OP_SENSOR_SETTING_STATUS: + bt_mesh_free_buf(arg->status_cb.setting_status.sensor_setting_raw); + break; + case ESP_BLE_MESH_MODEL_OP_SENSOR_GET: + case ESP_BLE_MESH_MODEL_OP_SENSOR_STATUS: + bt_mesh_free_buf(arg->status_cb.sensor_status.marshalled_sensor_data); + break; + case ESP_BLE_MESH_MODEL_OP_SENSOR_COLUMN_GET: + case ESP_BLE_MESH_MODEL_OP_SENSOR_COLUMN_STATUS: + bt_mesh_free_buf(arg->status_cb.column_status.sensor_column_value); + break; + case ESP_BLE_MESH_MODEL_OP_SENSOR_SERIES_GET: + case ESP_BLE_MESH_MODEL_OP_SENSOR_SERIES_STATUS: + bt_mesh_free_buf(arg->status_cb.series_status.sensor_series_value); + break; + default: + break; + } + } + case ESP_BLE_MESH_SENSOR_CLIENT_TIMEOUT_EVT: + if (arg->params) { + osi_free(arg->params); + } + break; + default: + break; + } +} + +void btc_ble_mesh_sensor_client_arg_deep_free(btc_msg_t *msg) +{ + btc_ble_mesh_sensor_client_args_t *arg = NULL; + u32_t opcode = 0; + + if (!msg || !msg->arg) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + arg = (btc_ble_mesh_sensor_client_args_t *)(msg->arg); + + switch (msg->act) { + case BTC_BLE_MESH_ACT_SENSOR_CLIENT_GET_STATE: + if (arg->sensor_client_get_state.params) { + opcode = arg->sensor_client_get_state.params->opcode; + osi_free(arg->sensor_client_get_state.params); + } + if (arg->sensor_client_get_state.get_state) { + if (opcode) { + switch (opcode) { + case ESP_BLE_MESH_MODEL_OP_SENSOR_COLUMN_GET: + bt_mesh_free_buf(arg->sensor_client_get_state.get_state->column_get.raw_value_x); + break; + case ESP_BLE_MESH_MODEL_OP_SENSOR_SERIES_GET: + bt_mesh_free_buf(arg->sensor_client_get_state.get_state->series_get.raw_value_x1); + bt_mesh_free_buf(arg->sensor_client_get_state.get_state->series_get.raw_value_x2); + break; + default: + break; + } + } + osi_free(arg->sensor_client_get_state.get_state); + } + break; + case BTC_BLE_MESH_ACT_SENSOR_CLIENT_SET_STATE: + if (arg->sensor_client_set_state.params) { + opcode = arg->sensor_client_set_state.params->opcode; + osi_free(arg->sensor_client_set_state.params); + } + if (arg->sensor_client_set_state.set_state) { + if (opcode) { + switch (opcode) { + case ESP_BLE_MESH_MODEL_OP_SENSOR_CADENCE_SET: + bt_mesh_free_buf(arg->sensor_client_set_state.set_state->cadence_set.status_trigger_delta_down); + bt_mesh_free_buf(arg->sensor_client_set_state.set_state->cadence_set.status_trigger_delta_up); + bt_mesh_free_buf(arg->sensor_client_set_state.set_state->cadence_set.fast_cadence_low); + bt_mesh_free_buf(arg->sensor_client_set_state.set_state->cadence_set.fast_cadence_high); + break; + case ESP_BLE_MESH_MODEL_OP_SENSOR_SETTING_SET: + bt_mesh_free_buf(arg->sensor_client_set_state.set_state->setting_set.sensor_setting_raw); + break; + default: + break; + } + } + osi_free(arg->sensor_client_set_state.set_state); + } + break; + default: + break; + } + + return; +} + +static void btc_mesh_sensor_client_callback(esp_ble_mesh_sensor_client_cb_param_t *cb_params, uint8_t act) +{ + btc_msg_t msg = {0}; + + LOG_DEBUG("%s", __func__); + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_SENSOR_CLIENT; + msg.act = act; + + btc_transfer_context(&msg, cb_params, + sizeof(esp_ble_mesh_sensor_client_cb_param_t), btc_ble_mesh_copy_req_data); +} + +void bt_mesh_callback_sensor_status_to_btc(u32_t opcode, u8_t evt_type, + struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + const u8_t *val, size_t len) +{ + esp_ble_mesh_sensor_client_cb_param_t cb_params = {0}; + esp_ble_mesh_client_common_param_t params = {0}; + size_t length; + uint8_t act; + + if (!model || !ctx) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + switch (evt_type) { + case 0x00: + act = ESP_BLE_MESH_SENSOR_CLIENT_GET_STATE_EVT; + break; + case 0x01: + act = ESP_BLE_MESH_SENSOR_CLIENT_SET_STATE_EVT; + break; + case 0x02: + act = ESP_BLE_MESH_SENSOR_CLIENT_PUBLISH_EVT; + break; + case 0x03: + act = ESP_BLE_MESH_SENSOR_CLIENT_TIMEOUT_EVT; + break; + default: + LOG_ERROR("%s, Unknown sensor client event type %d", __func__, evt_type); + return; + } + + params.opcode = opcode; + params.model = (esp_ble_mesh_model_t *)model; + params.ctx.net_idx = ctx->net_idx; + params.ctx.app_idx = ctx->app_idx; + params.ctx.addr = ctx->addr; + params.ctx.recv_ttl = ctx->recv_ttl; + params.ctx.recv_op = ctx->recv_op; + params.ctx.recv_dst = ctx->recv_dst; + + cb_params.error_code = 0; + cb_params.params = ¶ms; + + if (val && len) { + length = (len <= sizeof(cb_params.status_cb)) ? len : sizeof(cb_params.status_cb); + memcpy(&cb_params.status_cb, val, length); + } + + btc_mesh_sensor_client_callback(&cb_params, act); +} + +void btc_mesh_sensor_client_publish_callback(u32_t opcode, struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) +{ + if (!model || !ctx || !buf) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + bt_mesh_callback_sensor_status_to_btc(opcode, 0x02, model, ctx, buf->data, buf->len); +} + +void btc_mesh_sensor_client_call_handler(btc_msg_t *msg) +{ + esp_ble_mesh_sensor_client_cb_param_t sensor_client_cb = {0}; + esp_ble_mesh_client_common_param_t *params = NULL; + btc_ble_mesh_sensor_client_args_t *arg = NULL; + struct bt_mesh_common_param common = {0}; + bt_mesh_role_param_t role_param = {0}; + + if (!msg || !msg->arg) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + arg = (btc_ble_mesh_sensor_client_args_t *)(msg->arg); + + switch (msg->act) { + case BTC_BLE_MESH_ACT_SENSOR_CLIENT_GET_STATE: { + params = arg->sensor_client_get_state.params; + role_param.model = (struct bt_mesh_model *)params->model; + role_param.role = params->msg_role; + if (bt_mesh_set_model_role(&role_param)) { + LOG_ERROR("%s, Failed to set model role", __func__); + return; + } + common.opcode = params->opcode; + common.model = (struct bt_mesh_model *)params->model; + common.ctx.net_idx = params->ctx.net_idx; + common.ctx.app_idx = params->ctx.app_idx; + common.ctx.addr = params->ctx.addr; + common.ctx.send_rel = params->ctx.send_rel; + common.ctx.send_ttl = params->ctx.send_ttl; + common.msg_timeout = params->msg_timeout; + + sensor_client_cb.params = arg->sensor_client_get_state.params; + sensor_client_cb.error_code = + bt_mesh_sensor_client_get_state(&common, + (void *)arg->sensor_client_get_state.get_state, + (void *)&sensor_client_cb.status_cb); + if (sensor_client_cb.error_code) { + /* If send failed, callback error_code to app layer immediately */ + btc_mesh_sensor_client_callback(&sensor_client_cb, + ESP_BLE_MESH_SENSOR_CLIENT_GET_STATE_EVT); + } + break; + } + case BTC_BLE_MESH_ACT_SENSOR_CLIENT_SET_STATE: { + params = arg->sensor_client_set_state.params; + role_param.model = (struct bt_mesh_model *)params->model; + role_param.role = params->msg_role; + if (bt_mesh_set_model_role(&role_param)) { + LOG_ERROR("%s, Failed to set model role", __func__); + return; + } + common.opcode = params->opcode; + common.model = (struct bt_mesh_model *)params->model; + common.ctx.net_idx = params->ctx.net_idx; + common.ctx.app_idx = params->ctx.app_idx; + common.ctx.addr = params->ctx.addr; + common.ctx.send_rel = params->ctx.send_rel; + common.ctx.send_ttl = params->ctx.send_ttl; + common.msg_timeout = params->msg_timeout; + + sensor_client_cb.params = arg->sensor_client_set_state.params; + sensor_client_cb.error_code = + bt_mesh_sensor_client_set_state(&common, + (void *)arg->sensor_client_set_state.set_state, + (void *)&sensor_client_cb.status_cb); + if (sensor_client_cb.error_code) { + /* If send failed, callback error_code to app layer immediately */ + btc_mesh_sensor_client_callback(&sensor_client_cb, + ESP_BLE_MESH_SENSOR_CLIENT_SET_STATE_EVT); + } + break; + } + default: + break; + } + + btc_ble_mesh_sensor_client_arg_deep_free(msg); +} + +void btc_mesh_sensor_client_cb_handler(btc_msg_t *msg) +{ + esp_ble_mesh_sensor_client_cb_param_t *param = NULL; + + if (!msg || !msg->arg) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + param = (esp_ble_mesh_sensor_client_cb_param_t *)(msg->arg); + + if (msg->act < ESP_BLE_MESH_SENSOR_CLIENT_EVT_MAX) { + btc_ble_mesh_cb_to_app(msg->act, param); + } else { + LOG_ERROR("%s, Unknown msg->act = %d", __func__, msg->act); + } + + btc_ble_mesh_free_req_data(msg); +} + diff --git a/components/bt/ble_mesh/btc/btc_ble_mesh_time_scene_model.c b/components/bt/ble_mesh/btc/btc_ble_mesh_time_scene_model.c new file mode 100644 index 0000000000..4c1a36905e --- /dev/null +++ b/components/bt/ble_mesh/btc/btc_ble_mesh_time_scene_model.c @@ -0,0 +1,383 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 + +#include "btc/btc_manage.h" +#include "osi/allocator.h" + +#include "time_scene_client.h" +#include "btc_ble_mesh_time_scene_model.h" +#include "esp_ble_mesh_time_scene_model_api.h" + +static inline void btc_ble_mesh_cb_to_app(esp_ble_mesh_time_scene_client_cb_event_t event, + esp_ble_mesh_time_scene_client_cb_param_t *param) +{ + esp_ble_mesh_time_scene_client_cb_t btc_mesh_cb = (esp_ble_mesh_time_scene_client_cb_t)btc_profile_cb_get(BTC_PID_TIME_SCENE_CLIENT); + if (btc_mesh_cb) { + btc_mesh_cb(event, param); + } +} + +void btc_ble_mesh_time_scene_client_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) +{ + btc_ble_mesh_time_scene_client_args_t *dst = (btc_ble_mesh_time_scene_client_args_t *)p_dest; + btc_ble_mesh_time_scene_client_args_t *src = (btc_ble_mesh_time_scene_client_args_t *)p_src; + + if (!msg || !dst || !src) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + switch (msg->act) { + case BTC_BLE_MESH_ACT_TIME_SCENE_CLIENT_GET_STATE: { + dst->time_scene_client_get_state.params = (esp_ble_mesh_client_common_param_t *)osi_malloc(sizeof(esp_ble_mesh_client_common_param_t)); + dst->time_scene_client_get_state.get_state = (esp_ble_mesh_time_scene_client_get_state_t *)osi_malloc(sizeof(esp_ble_mesh_time_scene_client_get_state_t)); + if (dst->time_scene_client_get_state.params && dst->time_scene_client_get_state.get_state) { + memcpy(dst->time_scene_client_get_state.params, src->time_scene_client_get_state.params, + sizeof(esp_ble_mesh_client_common_param_t)); + memcpy(dst->time_scene_client_get_state.get_state, src->time_scene_client_get_state.get_state, + sizeof(esp_ble_mesh_time_scene_client_get_state_t)); + } else { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + } + break; + } + case BTC_BLE_MESH_ACT_TIME_SCENE_CLIENT_SET_STATE: { + dst->time_scene_client_set_state.params = (esp_ble_mesh_client_common_param_t *)osi_malloc(sizeof(esp_ble_mesh_client_common_param_t)); + dst->time_scene_client_set_state.set_state = (esp_ble_mesh_time_scene_client_set_state_t *)osi_malloc(sizeof(esp_ble_mesh_time_scene_client_set_state_t)); + if (dst->time_scene_client_set_state.params && dst->time_scene_client_set_state.set_state) { + memcpy(dst->time_scene_client_set_state.params, src->time_scene_client_set_state.params, + sizeof(esp_ble_mesh_client_common_param_t)); + memcpy(dst->time_scene_client_set_state.set_state, src->time_scene_client_set_state.set_state, + sizeof(esp_ble_mesh_time_scene_client_set_state_t)); + } else { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + } + break; + } + default: + LOG_DEBUG("%s, Unknown deep copy act %d", __func__, msg->act); + break; + } +} + +static void btc_ble_mesh_copy_req_data(btc_msg_t *msg, void *p_dest, void *p_src) +{ + esp_ble_mesh_time_scene_client_cb_param_t *p_dest_data = (esp_ble_mesh_time_scene_client_cb_param_t *)p_dest; + esp_ble_mesh_time_scene_client_cb_param_t *p_src_data = (esp_ble_mesh_time_scene_client_cb_param_t *)p_src; + u32_t opcode; + u16_t length; + + if (!msg || !p_src_data || !p_dest_data) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + switch (msg->act) { + case ESP_BLE_MESH_TIME_SCENE_CLIENT_GET_STATE_EVT: + case ESP_BLE_MESH_TIME_SCENE_CLIENT_SET_STATE_EVT: + case ESP_BLE_MESH_TIME_SCENE_CLIENT_PUBLISH_EVT: + if (p_src_data->params) { + opcode = p_src_data->params->opcode; + switch (opcode) { + case ESP_BLE_MESH_MODEL_OP_SCENE_STORE: + case ESP_BLE_MESH_MODEL_OP_SCENE_REGISTER_GET: + case ESP_BLE_MESH_MODEL_OP_SCENE_DELETE: + case ESP_BLE_MESH_MODEL_OP_SCENE_REGISTER_STATUS: + if (p_src_data->status_cb.scene_register_status.scenes) { + length = p_src_data->status_cb.scene_register_status.scenes->len; + p_dest_data->status_cb.scene_register_status.scenes = bt_mesh_alloc_buf(length); + if (!p_dest_data->status_cb.scene_register_status.scenes) { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + return; + } + net_buf_simple_add_mem(p_dest_data->status_cb.scene_register_status.scenes, + p_src_data->status_cb.scene_register_status.scenes->data, + p_src_data->status_cb.scene_register_status.scenes->len); + } + break; + default: + break; + } + } + case ESP_BLE_MESH_TIME_SCENE_CLIENT_TIMEOUT_EVT: + if (p_src_data->params) { + p_dest_data->params = osi_malloc(sizeof(esp_ble_mesh_client_common_param_t)); + if (p_dest_data->params) { + memcpy(p_dest_data->params, p_src_data->params, sizeof(esp_ble_mesh_client_common_param_t)); + } else { + LOG_ERROR("%s, Failed to allocate memory, act %d", __func__, msg->act); + } + } + break; + default: + break; + } +} + +static void btc_ble_mesh_free_req_data(btc_msg_t *msg) +{ + esp_ble_mesh_time_scene_client_cb_param_t *arg = NULL; + u32_t opcode; + + if (!msg || !msg->arg) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + arg = (esp_ble_mesh_time_scene_client_cb_param_t *)(msg->arg); + + switch (msg->act) { + case ESP_BLE_MESH_TIME_SCENE_CLIENT_GET_STATE_EVT: + case ESP_BLE_MESH_TIME_SCENE_CLIENT_SET_STATE_EVT: + case ESP_BLE_MESH_TIME_SCENE_CLIENT_PUBLISH_EVT: + if (arg->params) { + opcode = arg->params->opcode; + switch (opcode) { + case ESP_BLE_MESH_MODEL_OP_SCENE_STORE: + case ESP_BLE_MESH_MODEL_OP_SCENE_REGISTER_GET: + case ESP_BLE_MESH_MODEL_OP_SCENE_DELETE: + case ESP_BLE_MESH_MODEL_OP_SCENE_REGISTER_STATUS: + bt_mesh_free_buf(arg->status_cb.scene_register_status.scenes); + break; + default: + break; + } + } + case ESP_BLE_MESH_TIME_SCENE_CLIENT_TIMEOUT_EVT: + if (arg->params) { + osi_free(arg->params); + } + break; + default: + break; + } +} + +void btc_ble_mesh_time_scene_client_arg_deep_free(btc_msg_t *msg) +{ + btc_ble_mesh_time_scene_client_args_t *arg = NULL; + + if (!msg || !msg->arg) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + arg = (btc_ble_mesh_time_scene_client_args_t *)(msg->arg); + + switch (msg->act) { + case BTC_BLE_MESH_ACT_TIME_SCENE_CLIENT_GET_STATE: + if (arg->time_scene_client_get_state.params) { + osi_free(arg->time_scene_client_get_state.params); + } + if (arg->time_scene_client_get_state.get_state) { + osi_free(arg->time_scene_client_get_state.get_state); + } + break; + case BTC_BLE_MESH_ACT_TIME_SCENE_CLIENT_SET_STATE: + if (arg->time_scene_client_set_state.params) { + osi_free(arg->time_scene_client_set_state.params); + } + if (arg->time_scene_client_set_state.set_state) { + osi_free(arg->time_scene_client_set_state.set_state); + } + break; + default: + break; + } + + return; +} + +static void btc_mesh_time_scene_client_callback(esp_ble_mesh_time_scene_client_cb_param_t *cb_params, uint8_t act) +{ + btc_msg_t msg = {0}; + + LOG_DEBUG("%s", __func__); + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_TIME_SCENE_CLIENT; + msg.act = act; + + btc_transfer_context(&msg, cb_params, + sizeof(esp_ble_mesh_time_scene_client_cb_param_t), btc_ble_mesh_copy_req_data); +} + +void bt_mesh_callback_time_scene_status_to_btc(u32_t opcode, u8_t evt_type, + struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + const u8_t *val, size_t len) +{ + esp_ble_mesh_time_scene_client_cb_param_t cb_params = {0}; + esp_ble_mesh_client_common_param_t params = {0}; + size_t length; + uint8_t act; + + if (!model || !ctx) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + switch (evt_type) { + case 0x00: + act = ESP_BLE_MESH_TIME_SCENE_CLIENT_GET_STATE_EVT; + break; + case 0x01: + act = ESP_BLE_MESH_TIME_SCENE_CLIENT_SET_STATE_EVT; + break; + case 0x02: + act = ESP_BLE_MESH_TIME_SCENE_CLIENT_PUBLISH_EVT; + break; + case 0x03: + act = ESP_BLE_MESH_TIME_SCENE_CLIENT_TIMEOUT_EVT; + break; + default: + LOG_ERROR("%s, Unknown time scene client event type %d", __func__, evt_type); + return; + } + + params.opcode = opcode; + params.model = (esp_ble_mesh_model_t *)model; + params.ctx.net_idx = ctx->net_idx; + params.ctx.app_idx = ctx->app_idx; + params.ctx.addr = ctx->addr; + params.ctx.recv_ttl = ctx->recv_ttl; + params.ctx.recv_op = ctx->recv_op; + params.ctx.recv_dst = ctx->recv_dst; + + cb_params.error_code = 0; + cb_params.params = ¶ms; + + if (val && len) { + length = (len <= sizeof(cb_params.status_cb)) ? len : sizeof(cb_params.status_cb); + memcpy(&cb_params.status_cb, val, length); + } + + btc_mesh_time_scene_client_callback(&cb_params, act); +} + +void btc_mesh_time_scene_client_publish_callback(u32_t opcode, struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) +{ + if (!model || !ctx || !buf) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + bt_mesh_callback_time_scene_status_to_btc(opcode, 0x02, model, ctx, buf->data, buf->len); +} + +void btc_mesh_time_scene_client_call_handler(btc_msg_t *msg) +{ + esp_ble_mesh_time_scene_client_cb_param_t time_scene_client_cb = {0}; + btc_ble_mesh_time_scene_client_args_t *arg = NULL; + esp_ble_mesh_client_common_param_t *params = NULL; + struct bt_mesh_common_param common = {0}; + bt_mesh_role_param_t role_param = {0}; + + if (!msg || !msg->arg) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + arg = (btc_ble_mesh_time_scene_client_args_t *)(msg->arg); + + switch (msg->act) { + case BTC_BLE_MESH_ACT_TIME_SCENE_CLIENT_GET_STATE: { + params = arg->time_scene_client_get_state.params; + role_param.model = (struct bt_mesh_model *)params->model; + role_param.role = params->msg_role; + if (bt_mesh_set_model_role(&role_param)) { + LOG_ERROR("%s, Failed to set model role", __func__); + return; + } + common.opcode = params->opcode; + common.model = (struct bt_mesh_model *)params->model; + common.ctx.net_idx = params->ctx.net_idx; + common.ctx.app_idx = params->ctx.app_idx; + common.ctx.addr = params->ctx.addr; + common.ctx.send_rel = params->ctx.send_rel; + common.ctx.send_ttl = params->ctx.send_ttl; + common.msg_timeout = params->msg_timeout; + + time_scene_client_cb.params = arg->time_scene_client_get_state.params; + time_scene_client_cb.error_code = + bt_mesh_time_scene_client_get_state(&common, + (void *)arg->time_scene_client_get_state.get_state, + (void *)&time_scene_client_cb.status_cb); + if (time_scene_client_cb.error_code) { + /* If send failed, callback error_code to app layer immediately */ + btc_mesh_time_scene_client_callback(&time_scene_client_cb, + ESP_BLE_MESH_TIME_SCENE_CLIENT_GET_STATE_EVT); + } + break; + } + case BTC_BLE_MESH_ACT_TIME_SCENE_CLIENT_SET_STATE: { + params = arg->time_scene_client_set_state.params; + role_param.model = (struct bt_mesh_model *)params->model; + role_param.role = params->msg_role; + if (bt_mesh_set_model_role(&role_param)) { + LOG_ERROR("%s, Failed to set model role", __func__); + return; + } + common.opcode = params->opcode; + common.model = (struct bt_mesh_model *)params->model; + common.ctx.net_idx = params->ctx.net_idx; + common.ctx.app_idx = params->ctx.app_idx; + common.ctx.addr = params->ctx.addr; + common.ctx.send_rel = params->ctx.send_rel; + common.ctx.send_ttl = params->ctx.send_ttl; + common.msg_timeout = params->msg_timeout; + + time_scene_client_cb.params = arg->time_scene_client_set_state.params; + time_scene_client_cb.error_code = + bt_mesh_time_scene_client_set_state(&common, + (void *)arg->time_scene_client_set_state.set_state, + (void *)&time_scene_client_cb.status_cb); + if (time_scene_client_cb.error_code) { + /* If send failed, callback error_code to app layer immediately */ + btc_mesh_time_scene_client_callback(&time_scene_client_cb, + ESP_BLE_MESH_TIME_SCENE_CLIENT_SET_STATE_EVT); + } + break; + } + default: + break; + } + + btc_ble_mesh_time_scene_client_arg_deep_free(msg); +} + +void btc_mesh_time_scene_client_cb_handler(btc_msg_t *msg) +{ + esp_ble_mesh_time_scene_client_cb_param_t *param = NULL; + + if (!msg || !msg->arg) { + LOG_ERROR("%s, Invalid parameter", __func__); + return; + } + + param = (esp_ble_mesh_time_scene_client_cb_param_t *)(msg->arg); + + if (msg->act < ESP_BLE_MESH_TIME_SCENE_CLIENT_EVT_MAX) { + btc_ble_mesh_cb_to_app(msg->act, param); + } else { + LOG_ERROR("%s, Unknown msg->act = %d", __func__, msg->act); + } + + btc_ble_mesh_free_req_data(msg); +} + diff --git a/components/bt/ble_mesh/btc/include/btc_ble_mesh_config_model.h b/components/bt/ble_mesh/btc/include/btc_ble_mesh_config_model.h new file mode 100644 index 0000000000..6744b96120 --- /dev/null +++ b/components/bt/ble_mesh/btc/include/btc_ble_mesh_config_model.h @@ -0,0 +1,64 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#ifndef _BTC_BLE_MESH_CONFIG_MODEL_H_ +#define _BTC_BLE_MESH_CONFIG_MODEL_H_ + +#include +#include "btc/btc_task.h" +#include "esp_ble_mesh_config_model_api.h" + +typedef enum { + BTC_BLE_MESH_ACT_CONFIG_CLIENT_GET_STATE, + BTC_BLE_MESH_ACT_CONFIG_CLIENT_SET_STATE, +} btc_ble_mesh_cfg_client_act_t; + +typedef union { + struct ble_mesh_clg_client_get_state_reg_args { + esp_ble_mesh_client_common_param_t *params; + esp_ble_mesh_cfg_client_get_state_t *get_state; + } cfg_client_get_state; + struct ble_mesh_clg_client_set_state_reg_args { + esp_ble_mesh_client_common_param_t *params; + esp_ble_mesh_cfg_client_set_state_t *set_state; + } cfg_client_set_state; +} btc_ble_mesh_cfg_client_args_t; + +void btc_mesh_cfg_client_call_handler(btc_msg_t *msg); + +void btc_mesh_cfg_client_cb_handler(btc_msg_t *msg); + +void btc_ble_mesh_cfg_client_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src); + +int btc_ble_mesh_config_client_get_state(esp_ble_mesh_client_common_param_t *params, esp_ble_mesh_cfg_client_get_state_t *get_state, + esp_ble_mesh_cfg_client_cb_param_t *cfg_client_cb); + +int btc_ble_mesh_config_client_set_state(esp_ble_mesh_client_common_param_t *params, esp_ble_mesh_cfg_client_set_state_t *set_state, + esp_ble_mesh_cfg_client_cb_param_t *cfg_client_cb); + +void btc_mesh_cfg_client_publish_callback(u32_t opcode, struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf); + +void bt_mesh_callback_config_status_to_btc(u32_t opcode, u8_t evt_type, + struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + const u8_t *val, size_t len); + +void btc_mesh_cfg_server_cb_handler(btc_msg_t *msg); + +void bt_mesh_callback_cfg_server_event_to_btc(u8_t evt_type, struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + const u8_t *val, size_t len); + +#endif /* _BTC_BLE_MESH_CONFIG_MODEL_H_ */ diff --git a/components/bt/ble_mesh/btc/include/btc_ble_mesh_generic_model.h b/components/bt/ble_mesh/btc/include/btc_ble_mesh_generic_model.h new file mode 100644 index 0000000000..480003141d --- /dev/null +++ b/components/bt/ble_mesh/btc/include/btc_ble_mesh_generic_model.h @@ -0,0 +1,52 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#ifndef _BTC_BLE_MESH_GENERIC_MODEL_H_ +#define _BTC_BLE_MESH_GENERIC_MODEL_H_ + +#include +#include "btc/btc_task.h" +#include "esp_ble_mesh_generic_model_api.h" + +typedef enum { + BTC_BLE_MESH_ACT_GENERIC_CLIENT_GET_STATE, + BTC_BLE_MESH_ACT_GENERIC_CLIENT_SET_STATE, +} btc_ble_mesh_generic_client_act_t; + +typedef union { + struct ble_mesh_generic_client_get_state_reg_args { + esp_ble_mesh_client_common_param_t *params; + esp_ble_mesh_generic_client_get_state_t *get_state; + } generic_client_get_state; + struct ble_mesh_generic_client_set_state_reg_args { + esp_ble_mesh_client_common_param_t *params; + esp_ble_mesh_generic_client_set_state_t *set_state; + } generic_client_set_state; +} btc_ble_mesh_generic_client_args_t; + +void btc_mesh_generic_client_call_handler(btc_msg_t *msg); + +void btc_mesh_generic_client_cb_handler(btc_msg_t *msg); + +void btc_ble_mesh_generic_client_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src); + +void btc_mesh_generic_client_publish_callback(u32_t opcode, struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf); + +void bt_mesh_callback_generic_status_to_btc(u32_t opcode, u8_t evt_type, + struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + const u8_t *val, size_t len); + +#endif /* _BTC_BLE_MESH_GENERIC_MODEL_H_ */ diff --git a/components/bt/ble_mesh/btc/include/btc_ble_mesh_health_model.h b/components/bt/ble_mesh/btc/include/btc_ble_mesh_health_model.h new file mode 100644 index 0000000000..859624dba2 --- /dev/null +++ b/components/bt/ble_mesh/btc/include/btc_ble_mesh_health_model.h @@ -0,0 +1,78 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#ifndef _BTC_BLE_MESH_HEALTH_MODEL_H_ +#define _BTC_BLE_MESH_HEALTH_MODEL_H_ + +#include +#include "btc/btc_task.h" +#include "esp_ble_mesh_health_model_api.h" + +typedef enum { + BTC_BLE_MESH_ACT_HEALTH_CLIENT_GET_STATE, + BTC_BLE_MESH_ACT_HEALTH_CLIENT_SET_STATE, + BTC_BLE_MESH_ACT_HEALTH_CLIENT_MAX, +} btc_ble_mesh_health_client_act_t; + +typedef enum { + BTC_BLE_MESH_ACT_HEALTH_SERVER_FAULT_UPDATE, + BTC_BLE_MESH_ACT_HEALTH_SERVER_MAX, +} btc_ble_mesh_health_server_act_t; + +typedef union { + struct ble_mesh_health_client_get_state_reg_args { + esp_ble_mesh_client_common_param_t *params; + esp_ble_mesh_health_client_get_state_t *get_state; + } health_client_get_state; + struct ble_mesh_health_client_set_state_reg_args { + esp_ble_mesh_client_common_param_t *params; + esp_ble_mesh_health_client_set_state_t *set_state; + } health_client_set_state; +} btc_ble_mesh_health_client_args_t; + +typedef union { + struct ble_mesh_health_server_fault_update_args { + esp_ble_mesh_elem_t *element; + } fault_update; +} btc_ble_mesh_health_server_args_t; + +void btc_mesh_health_client_call_handler(btc_msg_t *msg); + +void btc_mesh_health_client_cb_handler(btc_msg_t *msg); + +void btc_mesh_health_server_call_handler(btc_msg_t *msg); + +void btc_mesh_health_server_cb_handler(btc_msg_t *msg); + +void btc_ble_mesh_health_client_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src); + +void btc_ble_mesh_health_server_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src); + +int btc_ble_mesh_health_client_get_state(esp_ble_mesh_client_common_param_t *params, + esp_ble_mesh_health_client_get_state_t *get_state, + esp_ble_mesh_health_client_cb_param_t *client_cb); + +int btc_ble_mesh_health_client_set_state(esp_ble_mesh_client_common_param_t *params, + esp_ble_mesh_health_client_set_state_t *set_state, + esp_ble_mesh_health_client_cb_param_t *client_cb); + +void btc_mesh_health_publish_callback(u32_t opcode, struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf); + +void bt_mesh_callback_health_status_to_btc(u32_t opcode, u8_t evt_type, + struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + const u8_t *val, u16_t len); + +#endif /* _BTC_BLE_MESH_HEALTH_MODEL_H_ */ diff --git a/components/bt/ble_mesh/btc/include/btc_ble_mesh_lighting_model.h b/components/bt/ble_mesh/btc/include/btc_ble_mesh_lighting_model.h new file mode 100644 index 0000000000..8a3088f32d --- /dev/null +++ b/components/bt/ble_mesh/btc/include/btc_ble_mesh_lighting_model.h @@ -0,0 +1,53 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#ifndef _BTC_BLE_MESH_LIGHTING_MODEL_H_ +#define _BTC_BLE_MESH_LIGHTING_MODEL_H_ + +#include +#include "btc/btc_task.h" +#include "esp_ble_mesh_lighting_model_api.h" + +typedef enum { + BTC_BLE_MESH_ACT_LIGHT_CLIENT_GET_STATE, + BTC_BLE_MESH_ACT_LIGHT_CLIENT_SET_STATE, +} btc_ble_mesh_light_client_act_t; + +typedef union { + struct ble_mesh_light_client_get_state_reg_args { + esp_ble_mesh_client_common_param_t *params; + esp_ble_mesh_light_client_get_state_t *get_state; + } light_client_get_state; + struct ble_mesh_light_client_set_state_reg_args { + esp_ble_mesh_client_common_param_t *params; + esp_ble_mesh_light_client_set_state_t *set_state; + } light_client_set_state; +} btc_ble_mesh_light_client_args_t; + +void btc_mesh_light_client_call_handler(btc_msg_t *msg); + +void btc_mesh_light_client_cb_handler(btc_msg_t *msg); + +void btc_ble_mesh_light_client_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src); + +void btc_mesh_light_client_publish_callback(u32_t opcode, struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf); + +void bt_mesh_callback_light_status_to_btc(u32_t opcode, u8_t evt_type, + struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + const u8_t *val, size_t len); + +#endif /* _BTC_BLE_MESH_LIGHTING_MODEL_H_ */ + diff --git a/components/bt/ble_mesh/btc/include/btc_ble_mesh_prov.h b/components/bt/ble_mesh/btc/include/btc_ble_mesh_prov.h new file mode 100644 index 0000000000..9ce21e4865 --- /dev/null +++ b/components/bt/ble_mesh/btc/include/btc_ble_mesh_prov.h @@ -0,0 +1,210 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#ifndef _BTC_BLE_MESH_PROV_H_ +#define _BTC_BLE_MESH_PROV_H_ + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "freertos/semphr.h" + +#include "btc/btc_task.h" +#include "esp_bt_defs.h" + +#include "mesh_access.h" +#include "mesh_buf.h" +#include "mesh_main.h" +#include "provisioner_prov.h" +#include "esp_ble_mesh_defs.h" + +typedef enum { + BTC_BLE_MESH_ACT_MESH_INIT = 0, + BTC_BLE_MESH_ACT_PROV_ENABLE, + BTC_BLE_MESH_ACT_PROV_DISABLE, + BTC_BLE_MESH_ACT_NODE_RESET, + BTC_BLE_MESH_ACT_SET_OOB_PUB_KEY, + BTC_BLE_MESH_ACT_INPUT_NUMBER, + BTC_BLE_MESH_ACT_INPUT_STRING, + BTC_BLE_MESH_ACT_SET_DEVICE_NAME, + BTC_BLE_MESH_ACT_PROXY_IDENTITY_ENABLE, + BTC_BLE_MESH_ACT_PROXY_GATT_ENABLE, + BTC_BLE_MESH_ACT_PROXY_GATT_DISABLE, + BTC_BLE_MESH_ACT_PROVISIONER_READ_OOB_PUB_KEY, + BTC_BLE_MESH_ACT_PROVISIONER_INPUT_STR, + BTC_BLE_MESH_ACT_PROVISIONER_INPUT_NUM, + BTC_BLE_MESH_ACT_PROVISIONER_ENABLE, + BTC_BLE_MESH_ACT_PROVISIONER_DISABLE, + BTC_BLE_MESH_ACT_PROVISIONER_DEV_ADD, + BTC_BLE_MESH_ACT_PROVISIONER_DEV_DEL, + BTC_BLE_MESH_ACT_PROVISIONER_SET_DEV_UUID_MATCH, + BTC_BLE_MESH_ACT_PROVISIONER_SET_PROV_DATA_INFO, + BTC_BLE_MESH_ACT_PROVISIONER_SET_NODE_NAME, + BTC_BLE_MESH_ACT_PROVISIONER_SET_LOCAL_APP_KEY, + BTC_BLE_MESH_ACT_PROVISIONER_BIND_LOCAL_MOD_APP, + BTC_BLE_MESH_ACT_PROVISIONER_ADD_LOCAL_NET_KEY, + BTC_BLE_MESH_ACT_SET_FAST_PROV_INFO, + BTC_BLE_MESH_ACT_SET_FAST_PROV_ACTION, +} btc_ble_mesh_prov_act_t; + +typedef enum { + BTC_BLE_MESH_ACT_MODEL_PUBLISH, + BTC_BLE_MESH_ACT_SERVER_MODEL_SEND, + BTC_BLE_MESH_ACT_CLIENT_MODEL_SEND +} btc_ble_mesh_model_act_t; + +typedef union { + struct ble_mesh_init_args { + esp_ble_mesh_prov_t *prov; + esp_ble_mesh_comp_t *comp; + SemaphoreHandle_t semaphore; + } mesh_init; + struct ble_mesh_node_prov_enable_args { + esp_ble_mesh_prov_bearer_t bearers; + } node_prov_enable; + struct ble_mesh_node_prov_disable_args { + esp_ble_mesh_prov_bearer_t bearers; + } node_prov_disable; + struct ble_mesh_set_oob_pub_key_args { + uint8_t pub_key_x[32]; + uint8_t pub_key_y[32]; + uint8_t private_key[32]; + } set_oob_pub_key; + struct ble_mesh_node_input_num_args { + uint32_t number; + } input_number; + struct ble_mesh_node_input_str_args { + char string[8]; + } input_string; + struct ble_mesh_set_device_name_args { + char name[ESP_BLE_MESH_DEVICE_NAME_MAX_LEN]; + } set_device_name; + struct ble_mesh_provisioner_read_oob_pub_key_args { + uint8_t link_idx; + uint8_t pub_key_x[32]; + uint8_t pub_key_y[32]; + } provisioner_read_oob_pub_key; + struct ble_mesh_provisioner_input_str_args { + char string[8]; + uint8_t link_idx; + } provisioner_input_str; + struct ble_mesh_provisioner_input_num_args { + uint32_t number; + uint8_t link_idx; + } provisioner_input_num; + struct ble_mesh_provisioner_enable_args { + esp_ble_mesh_prov_bearer_t bearers; + } provisioner_enable; + struct ble_mesh_provisioner_disable_args { + esp_ble_mesh_prov_bearer_t bearers; + } provisioner_disable; + struct ble_mesh_provisioner_dev_add_args { + esp_ble_mesh_unprov_dev_add_t add_dev; + esp_ble_mesh_dev_add_flag_t flags; + } provisioner_dev_add; + struct ble_mesh_provisioner_dev_del_args { + esp_ble_mesh_device_delete_t del_dev; + } provisioner_dev_del; + struct ble_mesh_provisioner_set_dev_uuid_match_args { + uint8_t offset; + uint8_t match_len; + uint8_t match_val[16]; + bool prov_after_match; + } set_dev_uuid_match; + struct ble_mesh_provisioner_set_prov_net_idx_args { + esp_ble_mesh_prov_data_info_t prov_data; + } set_prov_data_info; + struct ble_mesh_provisioner_set_node_name_args { + int index; + char name[ESP_BLE_MESH_NODE_NAME_MAX_LEN]; + } set_node_name; + struct ble_mesh_provisioner_add_local_app_key_args { + uint8_t app_key[16]; + uint16_t net_idx; + uint16_t app_idx; + } add_local_app_key; + struct ble_mesh_provisioner_bind_local_mod_app_args { + uint16_t elem_addr; + uint16_t model_id; + uint16_t cid; + uint16_t app_idx; + } local_mod_app_bind; + struct ble_mesh_provisioner_add_local_net_key_args { + uint8_t net_key[16]; + uint16_t net_idx; + } add_local_net_key; + struct ble_mesh_set_fast_prov_info_args { + uint16_t unicast_min; + uint16_t unicast_max; + uint16_t net_idx; + uint8_t flags; + uint32_t iv_index; + uint8_t offset; + uint8_t match_len; + uint8_t match_val[16]; + } set_fast_prov_info; + struct ble_mesh_set_fast_prov_action_args { + uint8_t action; + } set_fast_prov_action; +} btc_ble_mesh_prov_args_t; + +typedef union { + struct ble_mesh_model_publish_args { + esp_ble_mesh_model_t *model; + uint8_t device_role; + } model_publish; + struct ble_mesh_model_send_args { + esp_ble_mesh_model_t *model; + esp_ble_mesh_msg_ctx_t *ctx; + uint32_t opcode; + bool need_rsp; + uint16_t length; + uint8_t *data; + uint8_t device_role; + int32_t msg_timeout; + } model_send; +} btc_ble_mesh_model_args_t; + +void btc_ble_mesh_prov_arg_deep_free(btc_msg_t *msg); + +void btc_ble_mesh_prov_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src); + +int btc_ble_mesh_client_init(esp_ble_mesh_model_t *model); + +int32_t btc_ble_mesh_model_pub_period_get(esp_ble_mesh_model_t *mod); + +uint16_t btc_ble_mesh_get_primary_addr(void); + +uint16_t *btc_ble_mesh_model_find_group(esp_ble_mesh_model_t *mod, uint16_t addr); + +esp_ble_mesh_elem_t *btc_ble_mesh_elem_find(u16_t addr); + +uint8_t btc_ble_mesh_elem_count(void); + +esp_ble_mesh_model_t *btc_ble_mesh_model_find_vnd(const esp_ble_mesh_elem_t *elem, + uint16_t company, uint16_t id); + +esp_ble_mesh_model_t *btc_ble_mesh_model_find(const esp_ble_mesh_elem_t *elem, + uint16_t id); + +const esp_ble_mesh_comp_t *btc_ble_mesh_comp_get(void); + +void btc_mesh_model_call_handler(btc_msg_t *msg); +void btc_mesh_model_cb_handler(btc_msg_t *msg); + +void btc_mesh_prov_call_handler(btc_msg_t *msg); + +void btc_mesh_prov_cb_handler(btc_msg_t *msg); + +#endif /* _BTC_BLE_MESH_PROV_H_ */ diff --git a/components/bt/ble_mesh/btc/include/btc_ble_mesh_sensor_model.h b/components/bt/ble_mesh/btc/include/btc_ble_mesh_sensor_model.h new file mode 100644 index 0000000000..6bd97355e8 --- /dev/null +++ b/components/bt/ble_mesh/btc/include/btc_ble_mesh_sensor_model.h @@ -0,0 +1,53 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#ifndef _BTC_BLE_MESH_SENSOR_MODEL_H_ +#define _BTC_BLE_MESH_SENSOR_MODEL_H_ + +#include +#include "btc/btc_task.h" +#include "esp_ble_mesh_sensor_model_api.h" + +typedef enum { + BTC_BLE_MESH_ACT_SENSOR_CLIENT_GET_STATE, + BTC_BLE_MESH_ACT_SENSOR_CLIENT_SET_STATE, +} btc_ble_mesh_sensor_client_act_t; + +typedef union { + struct ble_mesh_sensor_client_get_state_reg_args { + esp_ble_mesh_client_common_param_t *params; + esp_ble_mesh_sensor_client_get_state_t *get_state; + } sensor_client_get_state; + struct ble_mesh_sensor_client_set_state_reg_args { + esp_ble_mesh_client_common_param_t *params; + esp_ble_mesh_sensor_client_set_state_t *set_state; + } sensor_client_set_state; +} btc_ble_mesh_sensor_client_args_t; + +void btc_mesh_sensor_client_call_handler(btc_msg_t *msg); + +void btc_mesh_sensor_client_cb_handler(btc_msg_t *msg); + +void btc_ble_mesh_sensor_client_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src); + +void btc_mesh_sensor_client_publish_callback(u32_t opcode, struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf); + +void bt_mesh_callback_sensor_status_to_btc(u32_t opcode, u8_t evt_type, + struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + const u8_t *val, size_t len); + +#endif /* _BTC_BLE_MESH_SENSOR_MODEL_H_ */ + diff --git a/components/bt/ble_mesh/btc/include/btc_ble_mesh_time_scene_model.h b/components/bt/ble_mesh/btc/include/btc_ble_mesh_time_scene_model.h new file mode 100644 index 0000000000..1778fa2347 --- /dev/null +++ b/components/bt/ble_mesh/btc/include/btc_ble_mesh_time_scene_model.h @@ -0,0 +1,53 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#ifndef _BTC_BLE_MESH_TIME_SCENE_MODEL_H_ +#define _BTC_BLE_MESH_TIME_SCENE_MODEL_H_ + +#include +#include "btc/btc_task.h" +#include "esp_ble_mesh_time_scene_model_api.h" + +typedef enum { + BTC_BLE_MESH_ACT_TIME_SCENE_CLIENT_GET_STATE, + BTC_BLE_MESH_ACT_TIME_SCENE_CLIENT_SET_STATE, +} btc_ble_mesh_time_scene_client_act_t; + +typedef union { + struct ble_mesh_time_scene_client_get_state_reg_args { + esp_ble_mesh_client_common_param_t *params; + esp_ble_mesh_time_scene_client_get_state_t *get_state; + } time_scene_client_get_state; + struct ble_mesh_time_scene_client_set_state_reg_args { + esp_ble_mesh_client_common_param_t *params; + esp_ble_mesh_time_scene_client_set_state_t *set_state; + } time_scene_client_set_state; +} btc_ble_mesh_time_scene_client_args_t; + +void btc_mesh_time_scene_client_call_handler(btc_msg_t *msg); + +void btc_mesh_time_scene_client_cb_handler(btc_msg_t *msg); + +void btc_ble_mesh_time_scene_client_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src); + +void btc_mesh_time_scene_client_publish_callback(u32_t opcode, struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf); + +void bt_mesh_callback_time_scene_status_to_btc(u32_t opcode, u8_t evt_type, + struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + const u8_t *val, size_t len); + +#endif /* _BTC_BLE_MESH_TIME_SCENE_MODEL_H_ */ + diff --git a/components/bt/ble_mesh/mesh_core/access.c b/components/bt/ble_mesh/mesh_core/access.c new file mode 100644 index 0000000000..6184d30091 --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/access.c @@ -0,0 +1,1043 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include + +#include "sdkconfig.h" +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BLE_MESH_DEBUG_ACCESS) + +#include "mesh_util.h" +#include "mesh_buf.h" +#include "mesh_trace.h" +#include "mesh_kernel.h" +#include "mesh_access.h" +#include "mesh_main.h" + +#include "mesh.h" +#include "adv.h" +#include "net.h" +#include "lpn.h" +#include "transport.h" +#include "access.h" +#include "foundation.h" + +#include "mesh_common.h" +#include "generic_client.h" +#include "sensor_client.h" +#include "time_scene_client.h" +#include "lighting_client.h" +#include "provisioner_main.h" + +#define BLE_MESH_SDU_MAX_LEN 384 + +static const struct bt_mesh_comp *dev_comp; +static u16_t dev_primary_addr; + +static const struct { + const u16_t id; + int (*const init)(struct bt_mesh_model *model, bool primary); +} model_init[] = { + { BLE_MESH_MODEL_ID_CFG_SRV, bt_mesh_cfg_srv_init }, + { BLE_MESH_MODEL_ID_HEALTH_SRV, bt_mesh_health_srv_init }, +#if defined(CONFIG_BLE_MESH_CFG_CLI) + { BLE_MESH_MODEL_ID_CFG_CLI, bt_mesh_cfg_cli_init }, +#endif +#if defined(CONFIG_BLE_MESH_HEALTH_CLI) + { BLE_MESH_MODEL_ID_HEALTH_CLI, bt_mesh_health_cli_init }, +#endif +#if defined(CONFIG_BLE_MESH_GENERIC_ONOFF_CLI) + { BLE_MESH_MODEL_ID_GEN_ONOFF_CLI, bt_mesh_gen_onoff_cli_init }, +#endif +#if defined(CONFIG_BLE_MESH_GENERIC_LEVEL_CLI) + { BLE_MESH_MODEL_ID_GEN_LEVEL_CLI, bt_mesh_gen_level_cli_init }, +#endif +#if defined(CONFIG_BLE_MESH_GENERIC_DEF_TRANS_TIME_CLI) + { BLE_MESH_MODEL_ID_GEN_DEF_TRANS_TIME_CLI, bt_mesh_gen_def_trans_time_cli_init }, +#endif +#if defined(CONFIG_BLE_MESH_GENERIC_POWER_ONOFF_CLI) + { BLE_MESH_MODEL_ID_GEN_POWER_ONOFF_CLI, bt_mesh_gen_pwr_onoff_cli_init }, +#endif +#if defined(CONFIG_BLE_MESH_GENERIC_POWER_LEVEL_CLI) + { BLE_MESH_MODEL_ID_GEN_POWER_LEVEL_CLI, bt_mesh_gen_pwr_level_cli_init }, +#endif +#if defined(CONFIG_BLE_MESH_GENERIC_BATTERY_CLI) + { BLE_MESH_MODEL_ID_GEN_BATTERY_CLI, bt_mesh_gen_battery_cli_init }, +#endif +#if defined(CONFIG_BLE_MESH_GENERIC_LOCATION_CLI) + { BLE_MESH_MODEL_ID_GEN_LOCATION_CLI, bt_mesh_gen_location_cli_init }, +#endif +#if defined(CONFIG_BLE_MESH_GENERIC_PROPERTY_CLI) + { BLE_MESH_MODEL_ID_GEN_PROP_CLI, bt_mesh_gen_property_cli_init }, +#endif +#if defined(CONFIG_BLE_MESH_SENSOR_CLI) + { BLE_MESH_MODEL_ID_SENSOR_CLI, bt_mesh_sensor_cli_init }, +#endif +#if defined(CONFIG_BLE_MESH_TIME_CLI) + { BLE_MESH_MODEL_ID_TIME_CLI, bt_mesh_time_cli_init }, +#endif +#if defined(CONFIG_BLE_MESH_SCENE_CLI) + { BLE_MESH_MODEL_ID_SCENE_CLI, bt_mesh_scene_cli_init }, +#endif +#if defined(CONFIG_BLE_MESH_SCHEDULER_CLI) + { BLE_MESH_MODEL_ID_SCHEDULER_CLI, bt_mesh_scheduler_cli_init }, +#endif +#if defined(CONFIG_BLE_MESH_LIGHT_LIGHTNESS_CLI) + { BLE_MESH_MODEL_ID_LIGHT_LIGHTNESS_CLI, bt_mesh_light_lightness_cli_init }, +#endif +#if defined(CONFIG_BLE_MESH_LIGHT_CTL_CLI) + { BLE_MESH_MODEL_ID_LIGHT_CTL_CLI, bt_mesh_light_ctl_cli_init }, +#endif +#if defined(CONFIG_BLE_MESH_LIGHT_HSL_CLI) + { BLE_MESH_MODEL_ID_LIGHT_HSL_CLI, bt_mesh_light_hsl_cli_init }, +#endif +#if defined(CONFIG_BLE_MESH_LIGHT_XYL_CLI) + { BLE_MESH_MODEL_ID_LIGHT_XYL_CLI, bt_mesh_light_xyl_cli_init }, +#endif +#if defined(CONFIG_BLE_MESH_LIGHT_LC_CLI) + { BLE_MESH_MODEL_ID_LIGHT_LC_CLI, bt_mesh_light_lc_cli_init }, +#endif +}; + +void bt_mesh_model_foreach(void (*func)(struct bt_mesh_model *mod, + struct bt_mesh_elem *elem, + bool vnd, bool primary, + void *user_data), + void *user_data) +{ + int i, j; + + for (i = 0; i < dev_comp->elem_count; i++) { + struct bt_mesh_elem *elem = &dev_comp->elem[i]; + + for (j = 0; j < elem->model_count; j++) { + struct bt_mesh_model *model = &elem->models[j]; + + func(model, elem, false, i == 0, user_data); + } + + for (j = 0; j < elem->vnd_model_count; j++) { + struct bt_mesh_model *model = &elem->vnd_models[j]; + + func(model, elem, true, i == 0, user_data); + } + } +} + +s32_t bt_mesh_model_pub_period_get(struct bt_mesh_model *mod) +{ + int period = 0; + + if (!mod->pub) { + BT_ERR("%s, Model has no publication support", __func__); + return 0; + } + + switch (mod->pub->period >> 6) { + case 0x00: + /* 1 step is 100 ms */ + period = K_MSEC((mod->pub->period & BIT_MASK(6)) * 100U); + break; + case 0x01: + /* 1 step is 1 second */ + period = K_SECONDS(mod->pub->period & BIT_MASK(6)); + break; + case 0x02: + /* 1 step is 10 seconds */ + period = K_SECONDS((mod->pub->period & BIT_MASK(6)) * 10U); + break; + case 0x03: + /* 1 step is 10 minutes */ + period = K_MINUTES((mod->pub->period & BIT_MASK(6)) * 10U); + break; + default: + BT_ERR("%s, Unknown model publication period", __func__); + return 0; + } + + if (mod->pub->fast_period) { + return period >> mod->pub->period_div; + } else { + return period; + } +} + +static s32_t next_period(struct bt_mesh_model *mod) +{ + struct bt_mesh_model_pub *pub = mod->pub; + u32_t elapsed, period; + + if (!pub) { + BT_ERR("%s, Model has no publication support", __func__); + return -ENOTSUP; + } + + period = bt_mesh_model_pub_period_get(mod); + if (!period) { + return 0; + } + + elapsed = k_uptime_get_32() - pub->period_start; + + BT_DBG("Publishing took %ums", elapsed); + + if (elapsed > period) { + BT_WARN("Publication sending took longer than the period"); + /* Return smallest positive number since 0 means disabled */ + return K_MSEC(1); + } + + return period - elapsed; +} + +static void publish_sent(int err, void *user_data) +{ + struct bt_mesh_model *mod = user_data; + s32_t delay; + + BT_DBG("err %d", err); + + if (!mod->pub) { + BT_ERR("%s, Model has no publication support", __func__); + return; + } + + if (mod->pub->count) { + delay = BLE_MESH_PUB_TRANSMIT_INT(mod->pub->retransmit); + } else { + delay = next_period(mod); + } + + if (delay) { + BT_DBG("Publishing next time in %dms", delay); + k_delayed_work_submit(&mod->pub->timer, delay); + } +} + +static const struct bt_mesh_send_cb pub_sent_cb = { + .end = publish_sent, +}; + +static int publish_retransmit(struct bt_mesh_model *mod) +{ + struct bt_mesh_model_pub *pub = mod->pub; + if (!pub) { + BT_ERR("%s, Model has no publication support", __func__); + return -ENOTSUP; + } + + struct bt_mesh_app_key *key = NULL; + struct net_buf_simple *sdu = NULL; + struct bt_mesh_msg_ctx ctx = { + .addr = pub->addr, + .send_ttl = pub->ttl, + .model = mod, + .srv_send = (pub->dev_role == NODE ? true : false), + }; + struct bt_mesh_net_tx tx = { + .ctx = &ctx, + .src = bt_mesh_model_elem(mod)->addr, + .xmit = bt_mesh_net_transmit_get(), + .friend_cred = pub->cred, + }; + int err; + + key = bt_mesh_app_key_find(pub->key); + if (!key) { + BT_ERR("%s, Failed to find AppKey", __func__); + return -EADDRNOTAVAIL; + } + + tx.sub = bt_mesh_subnet_get(key->net_idx); + + ctx.net_idx = key->net_idx; + ctx.app_idx = key->app_idx; + + sdu = bt_mesh_alloc_buf(pub->msg->len + 4); + if (!sdu) { + BT_ERR("%s, Failed to allocate memory", __func__); + return -ENOMEM; + } + + net_buf_simple_add_mem(sdu, pub->msg->data, pub->msg->len); + + pub->count--; + + err = bt_mesh_trans_send(&tx, sdu, &pub_sent_cb, mod); + + bt_mesh_free_buf(sdu); + return err; +} + +static void mod_publish(struct k_work *work) +{ + struct bt_mesh_model_pub *pub = CONTAINER_OF(work, + struct bt_mesh_model_pub, + timer.work); + s32_t period_ms; + int err; + + BT_DBG("%s", __func__); + + period_ms = bt_mesh_model_pub_period_get(pub->mod); + BT_DBG("period %u ms", period_ms); + + if (pub->count) { + err = publish_retransmit(pub->mod); + if (err) { + BT_ERR("%s, Failed to retransmit (err %d)", __func__, err); + + pub->count = 0U; + + /* Continue with normal publication */ + if (period_ms) { + k_delayed_work_submit(&pub->timer, period_ms); + } + } + + return; + } + + if (!period_ms) { + return; + } + + __ASSERT_NO_MSG(pub->update != NULL); + + pub->period_start = k_uptime_get_32(); + + /* Callback the model publish update event to the application layer. + * In the event, users can update the context of the publish message + * which will be published in the next period. + */ + err = pub->update(pub->mod); + if (err) { + BT_ERR("%s, Failed to update publication message", __func__); + return; + } + + err = bt_mesh_model_publish(pub->mod); + if (err) { + BT_ERR("%s, Publishing failed (err %d)", __func__, err); + } + + if (pub->count) { + /* Retransmissions also control the timer */ + k_delayed_work_cancel(&pub->timer); + } +} + +struct bt_mesh_elem *bt_mesh_model_elem(struct bt_mesh_model *mod) +{ + return &dev_comp->elem[mod->elem_idx]; +} + +struct bt_mesh_model *bt_mesh_model_get(bool vnd, u8_t elem_idx, u8_t mod_idx) +{ + struct bt_mesh_elem *elem; + + if (!dev_comp) { + BT_ERR("%s, dev_comp is not initialized", __func__); + return NULL; + } + + if (elem_idx >= dev_comp->elem_count) { + BT_ERR("%s, Invalid element index %u", __func__, elem_idx); + return NULL; + } + + elem = &dev_comp->elem[elem_idx]; + + if (vnd) { + if (mod_idx >= elem->vnd_model_count) { + BT_ERR("%s, Invalid vendor model index %u", __func__, mod_idx); + return NULL; + } + + return &elem->vnd_models[mod_idx]; + } else { + if (mod_idx >= elem->model_count) { + BT_ERR("%s, Invalid SIG model index %u", __func__, mod_idx); + return NULL; + } + + return &elem->models[mod_idx]; + } +} + +static void mod_init(struct bt_mesh_model *mod, struct bt_mesh_elem *elem, + bool vnd, bool primary, void *user_data) +{ + int i; + + mod->elem = elem; + + if (mod->pub) { + mod->pub->mod = mod; + k_delayed_work_init(&mod->pub->timer, mod_publish); + } + + for (i = 0; i < ARRAY_SIZE(mod->keys); i++) { + mod->keys[i] = BLE_MESH_KEY_UNUSED; + } + + mod->flags = 0; + mod->elem_idx = elem - dev_comp->elem; + if (vnd) { + mod->model_idx = mod - elem->vnd_models; + } else { + mod->model_idx = mod - elem->models; + } + + if (vnd) { + return; + } + + for (i = 0; i < ARRAY_SIZE(model_init); i++) { + if (model_init[i].id == mod->id) { + model_init[i].init(mod, primary); + } + } +} + +int bt_mesh_comp_register(const struct bt_mesh_comp *comp) +{ + /* There must be at least one element */ + if (!comp->elem_count) { + return -EINVAL; + } + + dev_comp = comp; + + bt_mesh_model_foreach(mod_init, NULL); + + return 0; +} + +void bt_mesh_comp_provision(u16_t addr) +{ + int i; + + dev_primary_addr = addr; + + BT_DBG("addr 0x%04x elem_count %u", addr, dev_comp->elem_count); + + for (i = 0; i < dev_comp->elem_count; i++) { + struct bt_mesh_elem *elem = &dev_comp->elem[i]; + + elem->addr = addr++; + + BT_DBG("addr 0x%04x mod_count %u vnd_mod_count %u", + elem->addr, elem->model_count, elem->vnd_model_count); + } +} + +void bt_mesh_comp_unprovision(void) +{ + BT_DBG("%s", __func__); + + dev_primary_addr = BLE_MESH_ADDR_UNASSIGNED; + + bt_mesh_model_foreach(mod_init, NULL); +} + +u16_t bt_mesh_primary_addr(void) +{ + return dev_primary_addr; +} + +u16_t *bt_mesh_model_find_group(struct bt_mesh_model *mod, u16_t addr) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mod->groups); i++) { + if (mod->groups[i] == addr) { + return &mod->groups[i]; + } + } + + return NULL; +} + +static struct bt_mesh_model *bt_mesh_elem_find_group(struct bt_mesh_elem *elem, + u16_t group_addr) +{ + struct bt_mesh_model *model; + u16_t *match; + int i; + + for (i = 0; i < elem->model_count; i++) { + model = &elem->models[i]; + + match = bt_mesh_model_find_group(model, group_addr); + if (match) { + return model; + } + } + + for (i = 0; i < elem->vnd_model_count; i++) { + model = &elem->vnd_models[i]; + + match = bt_mesh_model_find_group(model, group_addr); + if (match) { + return model; + } + } + + return NULL; +} + +struct bt_mesh_elem *bt_mesh_elem_find(u16_t addr) +{ + int i; + + for (i = 0; i < dev_comp->elem_count; i++) { + struct bt_mesh_elem *elem = &dev_comp->elem[i]; + + if (BLE_MESH_ADDR_IS_GROUP(addr) || + BLE_MESH_ADDR_IS_VIRTUAL(addr)) { + if (bt_mesh_elem_find_group(elem, addr)) { + return elem; + } + } else if (elem->addr == addr) { + return elem; + } + } + + return NULL; +} + +u8_t bt_mesh_elem_count(void) +{ + return dev_comp->elem_count; +} + +static bool model_has_key(struct bt_mesh_model *mod, u16_t key) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mod->keys); i++) { + if (mod->keys[i] == key) { + return true; + } + } + + return false; +} + +static const struct bt_mesh_model_op *find_op(struct bt_mesh_model *models, + u8_t model_count, u16_t dst, + u16_t app_idx, u32_t opcode, + struct bt_mesh_model **model) +{ + u8_t i; + + for (i = 0U; i < model_count; i++) { + const struct bt_mesh_model_op *op; + + *model = &models[i]; + + if (BLE_MESH_ADDR_IS_GROUP(dst) || + BLE_MESH_ADDR_IS_VIRTUAL(dst)) { + if (!bt_mesh_model_find_group(*model, dst)) { + continue; + } + } + + if (!model_has_key(*model, app_idx)) { + continue; + } + + for (op = (*model)->op; op->func; op++) { + if (op->opcode == opcode) { + return op; + } + } + } + + *model = NULL; + return NULL; +} + +static int get_opcode(struct net_buf_simple *buf, u32_t *opcode) +{ + switch (buf->data[0] >> 6) { + case 0x00: + case 0x01: + if (buf->data[0] == 0x7f) { + BT_ERR("%s, Ignoring RFU OpCode", __func__); + return -EINVAL; + } + + *opcode = net_buf_simple_pull_u8(buf); + return 0; + case 0x02: + if (buf->len < 2) { + BT_ERR("%s, Too short payload for 2-octet OpCode", __func__); + return -EINVAL; + } + + *opcode = net_buf_simple_pull_be16(buf); + return 0; + case 0x03: + if (buf->len < 3) { + BT_ERR("%s, Too short payload for 3-octet OpCode", __func__); + return -EINVAL; + } + + *opcode = net_buf_simple_pull_u8(buf) << 16; + *opcode |= net_buf_simple_pull_le16(buf); + return 0; + } + + return -EINVAL; +} + +bool bt_mesh_fixed_group_match(u16_t addr) +{ + /* Check for fixed group addresses */ + switch (addr) { + case BLE_MESH_ADDR_ALL_NODES: + return true; + case BLE_MESH_ADDR_PROXIES: + return (bt_mesh_gatt_proxy_get() == BLE_MESH_GATT_PROXY_ENABLED); + case BLE_MESH_ADDR_FRIENDS: + return (bt_mesh_friend_get() == BLE_MESH_FRIEND_ENABLED); + case BLE_MESH_ADDR_RELAYS: + return (bt_mesh_relay_get() == BLE_MESH_RELAY_ENABLED); + default: + return false; + } +} + +u32_t mesh_opcode; + +void bt_mesh_model_recv(struct bt_mesh_net_rx *rx, struct net_buf_simple *buf) +{ + struct bt_mesh_model *models, *model; + const struct bt_mesh_model_op *op; + u32_t opcode; + u8_t count; + int i; + + BT_DBG("app_idx 0x%04x src 0x%04x dst 0x%04x", rx->ctx.app_idx, + rx->ctx.addr, rx->ctx.recv_dst); + BT_DBG("len %u: %s", buf->len, bt_hex(buf->data, buf->len)); + + if (get_opcode(buf, &opcode) < 0) { + BT_WARN("%s, Unable to decode OpCode", __func__); + return; + } + + BT_DBG("OpCode 0x%08x", opcode); + + mesh_opcode = opcode; + + for (i = 0; i < dev_comp->elem_count; i++) { + struct bt_mesh_elem *elem = &dev_comp->elem[i]; + + if (BLE_MESH_ADDR_IS_UNICAST(rx->ctx.recv_dst)) { + if (elem->addr != rx->ctx.recv_dst) { + continue; + } + } else if (BLE_MESH_ADDR_IS_GROUP(rx->ctx.recv_dst) || + BLE_MESH_ADDR_IS_VIRTUAL(rx->ctx.recv_dst)) { + /* find_op() will do proper model/group matching */ + } else if (i != 0 || + !bt_mesh_fixed_group_match(rx->ctx.recv_dst)) { + continue; + } + + /* SIG models cannot contain 3-byte (vendor) OpCodes, and + * vendor models cannot contain SIG (1- or 2-byte) OpCodes, so + * we only need to do the lookup in one of the model lists. + */ + if (opcode < 0x10000) { + models = elem->models; + count = elem->model_count; + } else { + models = elem->vnd_models; + count = elem->vnd_model_count; + } + + op = find_op(models, count, rx->ctx.recv_dst, rx->ctx.app_idx, + opcode, &model); + if (op) { + struct net_buf_simple_state state; + + if (buf->len < op->min_len) { + BT_ERR("%s, Too short message for OpCode 0x%08x", + __func__, opcode); + continue; + } + + /* The callback will likely parse the buffer, so + * store the parsing state in case multiple models + * receive the message. + */ + net_buf_simple_save(buf, &state); + + /** Changed by Espressif, here we update recv_op with the + * value opcode got from the buf. + */ + rx->ctx.recv_op = opcode; + /** Changed by Espressif, we update the model pointer to the + * found model when we received a message. + */ + rx->ctx.model = model; + /** Changed by Espressif, we update the srv_send flag to be + * true when we received a message. This flag will be used + * when a server model sends a status message and will + * have no impact on the client sent messages. + */ + rx->ctx.srv_send = true; + + op->func(model, &rx->ctx, buf); + net_buf_simple_restore(buf, &state); + + } else { + BT_DBG("No OpCode 0x%08x for elem %d", opcode, i); + } + } +} + +void bt_mesh_model_msg_init(struct net_buf_simple *msg, u32_t opcode) +{ + net_buf_simple_init(msg, 0); + + if (opcode < 0x100) { + /* 1-byte OpCode */ + net_buf_simple_add_u8(msg, opcode); + return; + } + + if (opcode < 0x10000) { + /* 2-byte OpCode */ + net_buf_simple_add_be16(msg, opcode); + return; + } + + /* 3-byte OpCode */ + net_buf_simple_add_u8(msg, ((opcode >> 16) & 0xff)); + net_buf_simple_add_le16(msg, opcode & 0xffff); +} + +static int model_send(struct bt_mesh_model *model, + struct bt_mesh_net_tx *tx, bool implicit_bind, + struct net_buf_simple *msg, + const struct bt_mesh_send_cb *cb, void *cb_data) +{ + bool check = false; + u8_t role; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x dst 0x%04x", tx->ctx->net_idx, + tx->ctx->app_idx, tx->ctx->addr); + BT_DBG("len %u: %s", msg->len, bt_hex(msg->data, msg->len)); + + role = bt_mesh_get_model_role(model, tx->ctx->srv_send); + if (role == ROLE_NVAL) { + BT_ERR("%s, Failed to get model role", __func__); + return -EINVAL; + } + +#if CONFIG_BLE_MESH_NODE && !CONFIG_BLE_MESH_PROVISIONER + if (role == NODE) { + if (!bt_mesh_is_provisioned()) { + BT_ERR("%s, Local node is not yet provisioned", __func__); + return -EAGAIN; + } + if (!bt_mesh_is_provisioner_en()) { + check = true; + } + } +#endif + +#if !CONFIG_BLE_MESH_NODE && CONFIG_BLE_MESH_PROVISIONER + if (role == PROVISIONER) { + if (!provisioner_check_msg_dst_addr(tx->ctx->addr)) { + BT_ERR("%s, Failed to check DST", __func__); + return -EINVAL; + } + if (bt_mesh_is_provisioner_en()) { + check = true; + } + } +#endif + +#if CONFIG_BLE_MESH_NODE && CONFIG_BLE_MESH_PROVISIONER + if (role == PROVISIONER) { + if (!provisioner_check_msg_dst_addr(tx->ctx->addr)) { + BT_ERR("%s, Failed to check DST", __func__); + return -EINVAL; + } + if (bt_mesh_is_provisioner_en()) { + check = true; + } + } else { + if (!bt_mesh_is_provisioned()) { + BT_ERR("%s, Local node is not yet provisioned", __func__); + return -EAGAIN; + } + check = true; + } +#endif + + if (!check) { + BT_ERR("%s, fail", __func__); + return -EINVAL; + } + + if (net_buf_simple_tailroom(msg) < 4) { + BT_ERR("%s, Not enough tailroom for TransMIC", __func__); + return -EINVAL; + } + + if (msg->len > MIN(BLE_MESH_TX_SDU_MAX, BLE_MESH_SDU_MAX_LEN) - 4) { + BT_ERR("%s, Too big message", __func__); + return -EMSGSIZE; + } + + if (!implicit_bind && !model_has_key(model, tx->ctx->app_idx)) { + BT_ERR("%s, Model not bound to AppKey 0x%04x", __func__, tx->ctx->app_idx); + return -EINVAL; + } + + return bt_mesh_trans_send(tx, msg, cb, cb_data); +} + +int bt_mesh_model_send(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *msg, + const struct bt_mesh_send_cb *cb, void *cb_data) +{ + struct bt_mesh_subnet *sub = NULL; + u8_t role; + + role = bt_mesh_get_model_role(model, ctx->srv_send); + if (role == ROLE_NVAL) { + BT_ERR("%s, Failed to get model role", __func__); + return -EINVAL; + } + +#if CONFIG_BLE_MESH_NODE && !CONFIG_BLE_MESH_PROVISIONER + if (role == NODE) { + if (!bt_mesh_is_provisioner_en()) { + sub = bt_mesh_subnet_get(ctx->net_idx); + } + } +#endif + +#if !CONFIG_BLE_MESH_NODE && CONFIG_BLE_MESH_PROVISIONER + if (role == PROVISIONER) { + if (bt_mesh_is_provisioner_en()) { + sub = provisioner_subnet_get(ctx->net_idx); + } + } +#endif + +#if CONFIG_BLE_MESH_NODE && CONFIG_BLE_MESH_PROVISIONER + if (role == NODE) { + sub = bt_mesh_subnet_get(ctx->net_idx); + } else if (role == PROVISIONER) { + if (bt_mesh_is_provisioner_en()) { + sub = provisioner_subnet_get(ctx->net_idx); + } + } else if (role == FAST_PROV) { +#if CONFIG_BLE_MESH_FAST_PROV + sub = get_fast_prov_subnet(ctx->net_idx); +#endif + } +#endif + + if (!sub) { + BT_ERR("%s, Failed to get subnet", __func__); + return -EINVAL; + } + + ctx->model = model; + + struct bt_mesh_net_tx tx = { + .sub = sub, + .ctx = ctx, + .src = bt_mesh_model_elem(model)->addr, + .xmit = bt_mesh_net_transmit_get(), + .friend_cred = 0, + }; + + return model_send(model, &tx, false, msg, cb, cb_data); +} + +int bt_mesh_model_publish(struct bt_mesh_model *model) +{ + struct bt_mesh_model_pub *pub = model->pub; + struct bt_mesh_app_key *key = NULL; + struct net_buf_simple *sdu = NULL; + struct bt_mesh_msg_ctx ctx = {0}; + struct bt_mesh_net_tx tx = { + .sub = NULL, + .ctx = &ctx, + .src = bt_mesh_model_elem(model)->addr, + .xmit = bt_mesh_net_transmit_get(), + }; + int err; + + BT_DBG("%s", __func__); + + if (!pub) { + BT_ERR("%s, Model has no publication support", __func__); + return -ENOTSUP; + } + + if (pub->addr == BLE_MESH_ADDR_UNASSIGNED) { + BT_WARN("%s, Unassigned model publish address", __func__); + return -EADDRNOTAVAIL; + } + +#if CONFIG_BLE_MESH_NODE && !CONFIG_BLE_MESH_PROVISIONER + if (pub->dev_role == NODE) { + if (bt_mesh_is_provisioned()) { + key = bt_mesh_app_key_find(pub->key); + } + } +#endif + +#if !CONFIG_BLE_MESH_NODE && CONFIG_BLE_MESH_PROVISIONER + if (pub->dev_role == PROVISIONER) { + if (bt_mesh_is_provisioner_en()) { + key = provisioner_app_key_find(pub->key); + } + } +#endif + +#if CONFIG_BLE_MESH_NODE && CONFIG_BLE_MESH_PROVISIONER + if (pub->dev_role == NODE) { + if (bt_mesh_is_provisioned()) { + key = bt_mesh_app_key_find(pub->key); + } + } else if (pub->dev_role == PROVISIONER) { + if (bt_mesh_is_provisioner_en()) { + key = provisioner_app_key_find(pub->key); + } + } +#endif + + if (!key) { + BT_ERR("%s, Failed to get AppKey", __func__); + return -EADDRNOTAVAIL; + } + + if (pub->msg->len + 4 > MIN(BLE_MESH_TX_SDU_MAX, BLE_MESH_SDU_MAX_LEN)) { + BT_ERR("%s, Message does not fit maximum SDU size", __func__); + return -EMSGSIZE; + } + + if (pub->count) { + BT_WARN("%s, Clearing publish retransmit timer", __func__); + k_delayed_work_cancel(&pub->timer); + } + + ctx.addr = pub->addr; + ctx.send_ttl = pub->ttl; + ctx.net_idx = key->net_idx; + ctx.app_idx = key->app_idx; + ctx.srv_send = pub->dev_role == NODE ? true : false; + + tx.friend_cred = pub->cred; + +#if CONFIG_BLE_MESH_NODE && !CONFIG_BLE_MESH_PROVISIONER + if (pub->dev_role == NODE) { + if (bt_mesh_is_provisioned()) { + tx.sub = bt_mesh_subnet_get(ctx.net_idx); + } + } +#endif + +#if !CONFIG_BLE_MESH_NODE && CONFIG_BLE_MESH_PROVISIONER + if (pub->dev_role == PROVISIONER) { + if (bt_mesh_is_provisioner_en()) { + tx.sub = provisioner_subnet_get(ctx.net_idx); + } + } +#endif + +#if CONFIG_BLE_MESH_NODE && CONFIG_BLE_MESH_PROVISIONER + if (pub->dev_role == NODE) { + if (bt_mesh_is_provisioned()) { + tx.sub = bt_mesh_subnet_get(ctx.net_idx); + } + } else if (pub->dev_role == PROVISIONER) { + if (bt_mesh_is_provisioner_en()) { + tx.sub = provisioner_subnet_get(ctx.net_idx); + } + } +#endif + + if (!tx.sub) { + BT_ERR("%s, Failed to get subnet", __func__); + return -EADDRNOTAVAIL; + } + + pub->count = BLE_MESH_PUB_TRANSMIT_COUNT(pub->retransmit); + + BT_DBG("Publish Retransmit Count %u Interval %ums", pub->count, + BLE_MESH_PUB_TRANSMIT_INT(pub->retransmit)); + + sdu = bt_mesh_alloc_buf(pub->msg->len + 4); + if (!sdu) { + BT_ERR("%s, Failed to allocate memory", __func__); + return -ENOMEM; + } + + net_buf_simple_add_mem(sdu, pub->msg->data, pub->msg->len); + + err = model_send(model, &tx, true, sdu, &pub_sent_cb, model); + if (err) { + /* Don't try retransmissions for this publish attempt */ + pub->count = 0U; + /* Make sure the publish timer gets reset */ + publish_sent(err, model); + } + + bt_mesh_free_buf(sdu); + return err; +} + +struct bt_mesh_model *bt_mesh_model_find_vnd(struct bt_mesh_elem *elem, + u16_t company, u16_t id) +{ + u8_t i; + + for (i = 0U; i < elem->vnd_model_count; i++) { + if (elem->vnd_models[i].vnd.company == company && + elem->vnd_models[i].vnd.id == id) { + return &elem->vnd_models[i]; + } + } + + return NULL; +} + +struct bt_mesh_model *bt_mesh_model_find(struct bt_mesh_elem *elem, + u16_t id) +{ + u8_t i; + + for (i = 0U; i < elem->model_count; i++) { + if (elem->models[i].id == id) { + return &elem->models[i]; + } + } + + return NULL; +} + +const struct bt_mesh_comp *bt_mesh_comp_get(void) +{ + return dev_comp; +} diff --git a/components/bt/ble_mesh/mesh_core/access.h b/components/bt/ble_mesh/mesh_core/access.h new file mode 100644 index 0000000000..114c1662be --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/access.h @@ -0,0 +1,60 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _ACCESS_H_ +#define _ACCESS_H_ + +#include "mesh_access.h" +#include "mesh_buf.h" +#include "net.h" + +/* bt_mesh_model.flags */ +enum { + BLE_MESH_MOD_BIND_PENDING = BIT(0), + BLE_MESH_MOD_SUB_PENDING = BIT(1), + BLE_MESH_MOD_PUB_PENDING = BIT(2), +}; + +void bt_mesh_elem_register(struct bt_mesh_elem *elem, u8_t count); + +u8_t bt_mesh_elem_count(void); + +/* Find local element based on unicast or group address */ +struct bt_mesh_elem *bt_mesh_elem_find(u16_t addr); + +struct bt_mesh_model *bt_mesh_model_find_vnd(struct bt_mesh_elem *elem, + u16_t company, u16_t id); +struct bt_mesh_model *bt_mesh_model_find(struct bt_mesh_elem *elem, + u16_t id); + +u16_t *bt_mesh_model_find_group(struct bt_mesh_model *mod, u16_t addr); + +bool bt_mesh_fixed_group_match(u16_t addr); + +void bt_mesh_model_foreach(void (*func)(struct bt_mesh_model *mod, + struct bt_mesh_elem *elem, + bool vnd, bool primary, + void *user_data), + void *user_data); + +s32_t bt_mesh_model_pub_period_get(struct bt_mesh_model *mod); + +void bt_mesh_comp_provision(u16_t addr); +void bt_mesh_comp_unprovision(void); + +u16_t bt_mesh_primary_addr(void); + +const struct bt_mesh_comp *bt_mesh_comp_get(void); + +struct bt_mesh_model *bt_mesh_model_get(bool vnd, u8_t elem_idx, u8_t mod_idx); + +void bt_mesh_model_recv(struct bt_mesh_net_rx *rx, struct net_buf_simple *buf); + +int bt_mesh_comp_register(const struct bt_mesh_comp *comp); + +#endif /* _ACCESS_H_ */ diff --git a/components/bt/ble_mesh/mesh_core/adv.c b/components/bt/ble_mesh/mesh_core/adv.c new file mode 100644 index 0000000000..87ef363061 --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/adv.c @@ -0,0 +1,411 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "freertos/task.h" + +#include "osi/thread.h" +#include "sdkconfig.h" +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BLE_MESH_DEBUG_ADV) + +#include "mesh_util.h" +#include "mesh_buf.h" +#include "mesh_bearer_adapt.h" +#include "mesh_trace.h" +#include "mesh_hci.h" + +#include "mesh.h" +#include "adv.h" +#include "net.h" +#include "foundation.h" +#include "beacon.h" +#include "prov.h" +#include "proxy.h" + +#include "provisioner_prov.h" +#include "provisioner_proxy.h" +#include "provisioner_beacon.h" + +/* Convert from ms to 0.625ms units */ +#define ADV_SCAN_UNIT(_ms) ((_ms) * 8 / 5) + +/* Window and Interval are equal for continuous scanning */ +#define MESH_SCAN_INTERVAL 0x20 +#define MESH_SCAN_WINDOW 0x20 + +/* Pre-5.0 controllers enforce a minimum interval of 100ms + * whereas 5.0+ controllers can go down to 20ms. + */ +#define ADV_INT_DEFAULT_MS 100 +#define ADV_INT_FAST_MS 20 + +#if defined(CONFIG_BT_HOST_CRYPTO) +#define ADV_STACK_SIZE 1024 +#else +#define ADV_STACK_SIZE 768 +#endif + +static xQueueHandle xBleMeshQueue; +static const bt_mesh_addr_t *dev_addr; + +static const u8_t adv_type[] = { + [BLE_MESH_ADV_PROV] = BLE_MESH_DATA_MESH_PROV, + [BLE_MESH_ADV_DATA] = BLE_MESH_DATA_MESH_MESSAGE, + [BLE_MESH_ADV_BEACON] = BLE_MESH_DATA_MESH_BEACON, + [BLE_MESH_ADV_URI] = BLE_MESH_DATA_URI, +}; + +NET_BUF_POOL_DEFINE(adv_buf_pool, CONFIG_BLE_MESH_ADV_BUF_COUNT + 3 * CONFIG_BLE_MESH_PBA_SAME_TIME, + BLE_MESH_ADV_DATA_SIZE, BLE_MESH_ADV_USER_DATA_SIZE, NULL); + +static struct bt_mesh_adv adv_pool[CONFIG_BLE_MESH_ADV_BUF_COUNT + 3 * CONFIG_BLE_MESH_PBA_SAME_TIME]; + +static struct bt_mesh_adv *adv_alloc(int id) +{ + return &adv_pool[id]; +} + +static inline void adv_send_start(u16_t duration, int err, + const struct bt_mesh_send_cb *cb, + void *cb_data) +{ + if (cb && cb->start) { + cb->start(duration, err, cb_data); + } +} + +static inline void adv_send_end(int err, const struct bt_mesh_send_cb *cb, + void *cb_data) +{ + if (cb && cb->end) { + cb->end(err, cb_data); + } +} + +static inline int adv_send(struct net_buf *buf) +{ + const s32_t adv_int_min = ((bt_mesh_dev.hci_version >= BLE_MESH_HCI_VERSION_5_0) ? + ADV_INT_FAST_MS : ADV_INT_DEFAULT_MS); + const struct bt_mesh_send_cb *cb = BLE_MESH_ADV(buf)->cb; + void *cb_data = BLE_MESH_ADV(buf)->cb_data; + struct bt_mesh_adv_param param = {0}; + u16_t duration, adv_int; + struct bt_mesh_adv_data ad = {0}; + int err; + + adv_int = MAX(adv_int_min, + BLE_MESH_TRANSMIT_INT(BLE_MESH_ADV(buf)->xmit)); + duration = (BLE_MESH_TRANSMIT_COUNT(BLE_MESH_ADV(buf)->xmit) + 1) * + (adv_int + 10); + + BT_DBG("type %u len %u: %s", BLE_MESH_ADV(buf)->type, + buf->len, bt_hex(buf->data, buf->len)); + BT_DBG("count %u interval %ums duration %ums", + BLE_MESH_TRANSMIT_COUNT(BLE_MESH_ADV(buf)->xmit) + 1, adv_int, + duration); + + ad.type = adv_type[BLE_MESH_ADV(buf)->type]; + ad.data_len = buf->len; + ad.data = buf->data; + + param.options = 0U; + param.interval_min = ADV_SCAN_UNIT(adv_int); + param.interval_max = param.interval_min; + + err = bt_le_adv_start(¶m, &ad, 1, NULL, 0); + net_buf_unref(buf); + adv_send_start(duration, err, cb, cb_data); + if (err) { + BT_ERR("%s, Advertising failed: err %d", __func__, err); + return err; + } + + BT_DBG("Advertising started. Sleeping %u ms", duration); + + k_sleep(K_MSEC(duration)); + + err = bt_le_adv_stop(); + adv_send_end(err, cb, cb_data); + if (err) { + BT_ERR("%s, Stop advertising failed: err %d", __func__, err); + return 0; + } + + BT_DBG("Advertising stopped"); + return 0; +} + +static void adv_thread(void *p) +{ + struct net_buf **buf = NULL; + bt_mesh_msg_t msg = {0}; + int status; + + BT_DBG("started"); + + buf = (struct net_buf **)(&msg.arg); + + while (1) { + *buf = NULL; +#if CONFIG_BLE_MESH_NODE + if (IS_ENABLED(CONFIG_BLE_MESH_PROXY)) { + xQueueReceive(xBleMeshQueue, &msg, K_NO_WAIT); + while (!(*buf)) { + s32_t timeout; + BT_DBG("Proxy advertising start"); + timeout = bt_mesh_proxy_adv_start(); + BT_DBG("Proxy Advertising up to %d ms", timeout); + xQueueReceive(xBleMeshQueue, &msg, timeout); + BT_DBG("Proxy advertising stop"); + bt_mesh_proxy_adv_stop(); + } + } else { + xQueueReceive(xBleMeshQueue, &msg, (portTickType)portMAX_DELAY); + } +#else + xQueueReceive(xBleMeshQueue, &msg, (portTickType)portMAX_DELAY); +#endif + + if (!(*buf)) { + continue; + } + + /* busy == 0 means this was canceled */ + if (BLE_MESH_ADV(*buf)->busy) { + BLE_MESH_ADV(*buf)->busy = 0U; + status = adv_send(*buf); + if (status) { + if (xQueueSendToFront(xBleMeshQueue, &msg, K_NO_WAIT) != pdTRUE) { + BT_ERR("%s, xQueueSendToFront failed", __func__); + } + } + } + + /* Give other threads a chance to run */ + taskYIELD(); + } +} + +void bt_mesh_adv_update(void) +{ + BT_DBG("%s", __func__); + bt_mesh_msg_t msg = {0}; + bt_mesh_task_post(&msg, 0); +} + +struct net_buf *bt_mesh_adv_create_from_pool(struct net_buf_pool *pool, + bt_mesh_adv_alloc_t get_id, + enum bt_mesh_adv_type type, + u8_t xmit, s32_t timeout) +{ + struct bt_mesh_adv *adv; + struct net_buf *buf; + + if (bt_mesh_atomic_test_bit(bt_mesh.flags, BLE_MESH_SUSPENDED)) { + BT_WARN("Refusing to allocate buffer while suspended"); + return NULL; + } + + buf = net_buf_alloc(pool, timeout); + if (!buf) { + return NULL; + } + + BT_DBG("%s, pool = %p, buf_count = %d, uinit_count = %d", __func__, + buf->pool, pool->buf_count, pool->uninit_count); + + adv = get_id(net_buf_id(buf)); + BLE_MESH_ADV(buf) = adv; + + (void)memset(adv, 0, sizeof(*adv)); + + adv->type = type; + adv->xmit = xmit; + + return buf; +} + +struct net_buf *bt_mesh_adv_create(enum bt_mesh_adv_type type, u8_t xmit, + s32_t timeout) +{ + return bt_mesh_adv_create_from_pool(&adv_buf_pool, adv_alloc, type, + xmit, timeout); +} + +void bt_mesh_task_post(bt_mesh_msg_t *msg, uint32_t timeout) +{ + BT_DBG("%s", __func__); + if (xQueueSend(xBleMeshQueue, msg, timeout) != pdTRUE) { + BT_ERR("%s, Failed to post msg to queue", __func__); + } +} + +void bt_mesh_adv_send(struct net_buf *buf, const struct bt_mesh_send_cb *cb, + void *cb_data) +{ + BT_DBG("type 0x%02x len %u: %s", BLE_MESH_ADV(buf)->type, buf->len, + bt_hex(buf->data, buf->len)); + + BLE_MESH_ADV(buf)->cb = cb; + BLE_MESH_ADV(buf)->cb_data = cb_data; + BLE_MESH_ADV(buf)->busy = 1U; + + bt_mesh_msg_t msg = {0}; + msg.arg = (void *)net_buf_ref(buf); + bt_mesh_task_post(&msg, portMAX_DELAY); +} + +const bt_mesh_addr_t *bt_mesh_pba_get_addr(void) +{ + return dev_addr; +} + +static void bt_mesh_scan_cb(const bt_mesh_addr_t *addr, s8_t rssi, + u8_t adv_type, struct net_buf_simple *buf) +{ +#if CONFIG_BLE_MESH_PROVISIONER && CONFIG_BLE_MESH_PB_GATT + u16_t uuid = 0; +#endif + + if (adv_type != BLE_MESH_ADV_NONCONN_IND && adv_type != BLE_MESH_ADV_IND) { + return; + } + + BT_DBG("%s, len %u: %s", __func__, buf->len, bt_hex(buf->data, buf->len)); + + dev_addr = addr; + + while (buf->len > 1) { + struct net_buf_simple_state state; + u8_t len, type; + + len = net_buf_simple_pull_u8(buf); + /* Check for early termination */ + if (len == 0U) { + return; + } + + if (len > buf->len) { + BT_WARN("AD malformed"); + return; + } + + net_buf_simple_save(buf, &state); + + type = net_buf_simple_pull_u8(buf); + + buf->len = len - 1; + +#if 0 + /* TODO: Check with BLE Mesh BQB test cases */ + if ((type == BLE_MESH_DATA_MESH_PROV || type == BLE_MESH_DATA_MESH_MESSAGE || + type == BLE_MESH_DATA_MESH_BEACON) && (adv_type != BLE_MESH_ADV_NONCONN_IND)) { + BT_DBG("%s, ignore BLE Mesh packet (type 0x%02x) with adv_type 0x%02x", + __func__, type, adv_type); + return; + } +#endif + + switch (type) { + case BLE_MESH_DATA_MESH_MESSAGE: + bt_mesh_net_recv(buf, rssi, BLE_MESH_NET_IF_ADV); + break; +#if CONFIG_BLE_MESH_PB_ADV + case BLE_MESH_DATA_MESH_PROV: +#if CONFIG_BLE_MESH_NODE + if (!bt_mesh_is_provisioner_en()) { + bt_mesh_pb_adv_recv(buf); + } +#endif +#if CONFIG_BLE_MESH_PROVISIONER + if (bt_mesh_is_provisioner_en()) { + provisioner_pb_adv_recv(buf); + } +#endif + break; +#endif /* CONFIG_BLE_MESH_PB_ADV */ + case BLE_MESH_DATA_MESH_BEACON: +#if CONFIG_BLE_MESH_NODE + if (!bt_mesh_is_provisioner_en()) { + bt_mesh_beacon_recv(buf); + } +#endif +#if CONFIG_BLE_MESH_PROVISIONER + if (bt_mesh_is_provisioner_en()) { + provisioner_beacon_recv(buf); + } +#endif + break; +#if CONFIG_BLE_MESH_PROVISIONER && CONFIG_BLE_MESH_PB_GATT + case BLE_MESH_DATA_FLAGS: + if (bt_mesh_is_provisioner_en()) { + if (!provisioner_flags_match(buf)) { + BT_DBG("Flags mismatch, ignore this adv pkt"); + return; + } + } + break; + case BLE_MESH_DATA_UUID16_ALL: + if (bt_mesh_is_provisioner_en()) { + uuid = provisioner_srv_uuid_recv(buf); + if (!uuid) { + BT_DBG("Service UUID mismatch, ignore this adv pkt"); + return; + } + } + break; + case BLE_MESH_DATA_SVC_DATA16: + if (bt_mesh_is_provisioner_en()) { + provisioner_srv_data_recv(buf, addr, uuid); + } + break; +#endif /* CONFIG_BLE_MESH_PROVISIONER && CONFIG_BLE_MESH_PB_GATT */ + default: + break; + } + + net_buf_simple_restore(buf, &state); + net_buf_simple_pull(buf, len); + } + + return; +} + +void bt_mesh_adv_init(void) +{ + xBleMeshQueue = xQueueCreate(150, sizeof(bt_mesh_msg_t)); + xTaskCreatePinnedToCore(adv_thread, "BLE_Mesh_ADV_Task", 3072, NULL, + configMAX_PRIORITIES - 7, NULL, TASK_PINNED_TO_CORE); +} + +int bt_mesh_scan_enable(void) +{ + struct bt_mesh_scan_param scan_param = { + .type = BLE_MESH_SCAN_PASSIVE, +#if defined(CONFIG_BLE_MESH_USE_DUPLICATE_SCAN) + .filter_dup = BLE_MESH_SCAN_FILTER_DUP_ENABLE, +#else + .filter_dup = BLE_MESH_SCAN_FILTER_DUP_DISABLE, +#endif + .interval = MESH_SCAN_INTERVAL, + .window = MESH_SCAN_WINDOW + }; + + BT_DBG("%s", __func__); + + return bt_le_scan_start(&scan_param, bt_mesh_scan_cb); +} + +int bt_mesh_scan_disable(void) +{ + BT_DBG("%s", __func__); + + return bt_le_scan_stop(); +} diff --git a/components/bt/ble_mesh/mesh_core/adv.h b/components/bt/ble_mesh/mesh_core/adv.h new file mode 100644 index 0000000000..b827af59ea --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/adv.h @@ -0,0 +1,86 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _ADV_H_ +#define _ADV_H_ + +#include "mesh_bearer_adapt.h" + +/* Maximum advertising data payload for a single data type */ +#define BLE_MESH_ADV_DATA_SIZE 29 + +/* The user data is a pointer (4 bytes) to struct bt_mesh_adv */ +#define BLE_MESH_ADV_USER_DATA_SIZE 4 + +#define BLE_MESH_ADV(buf) (*(struct bt_mesh_adv **)net_buf_user_data(buf)) + +typedef struct bt_mesh_msg { + uint8_t sig; //event signal + uint8_t aid; //application id + uint8_t pid; //profile id + uint8_t act; //profile action, defined in seprerate header files + void *arg; //param for btc function or function param +} bt_mesh_msg_t; + +enum bt_mesh_adv_type { + BLE_MESH_ADV_PROV, + BLE_MESH_ADV_DATA, + BLE_MESH_ADV_BEACON, + BLE_MESH_ADV_URI, +}; + +typedef void (*bt_mesh_adv_func_t)(struct net_buf *buf, u16_t duration, + int err, void *user_data); + +struct bt_mesh_adv { + const struct bt_mesh_send_cb *cb; + void *cb_data; + + u8_t type: 2, + busy: 1; + u8_t xmit; + + union { + /* Address, used e.g. for Friend Queue messages */ + u16_t addr; + + /* For transport layer segment sending */ + struct { + u8_t attempts; + } seg; + }; +}; + +typedef struct bt_mesh_adv *(*bt_mesh_adv_alloc_t)(int id); + +/* xmit_count: Number of retransmissions, i.e. 0 == 1 transmission */ +struct net_buf *bt_mesh_adv_create(enum bt_mesh_adv_type type, u8_t xmit, + s32_t timeout); + +struct net_buf *bt_mesh_adv_create_from_pool(struct net_buf_pool *pool, + bt_mesh_adv_alloc_t get_id, + enum bt_mesh_adv_type type, + u8_t xmit, s32_t timeout); + +void bt_mesh_adv_send(struct net_buf *buf, const struct bt_mesh_send_cb *cb, + void *cb_data); + +const bt_mesh_addr_t *bt_mesh_pba_get_addr(void); + +void bt_mesh_adv_update(void); + +void bt_mesh_adv_init(void); + +int bt_mesh_scan_enable(void); + +int bt_mesh_scan_disable(void); + +void bt_mesh_task_post(bt_mesh_msg_t *msg, uint32_t timeout); + +#endif /* _ADV_H_ */ diff --git a/components/bt/ble_mesh/mesh_core/beacon.c b/components/bt/ble_mesh/mesh_core/beacon.c new file mode 100644 index 0000000000..a62cf073ad --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/beacon.c @@ -0,0 +1,422 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include "sdkconfig.h" +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BLE_MESH_DEBUG_BEACON) + +#include "mesh_util.h" +#include "mesh_buf.h" +#include "mesh_main.h" +#include "mesh_trace.h" + +#include "adv.h" +#include "mesh.h" +#include "net.h" +#include "prov.h" +#include "crypto.h" +#include "beacon.h" +#include "foundation.h" + +#if CONFIG_BLE_MESH_NODE + +#if defined(CONFIG_BLE_MESH_FAST_PROV) +#define UNPROVISIONED_INTERVAL K_SECONDS(3) +#else +#define UNPROVISIONED_INTERVAL K_SECONDS(5) +#endif /* CONFIG_BLE_MESH_FAST_PROV */ +#define PROVISIONED_INTERVAL K_SECONDS(10) + +#define BEACON_TYPE_UNPROVISIONED 0x00 +#define BEACON_TYPE_SECURE 0x01 + +/* 3 transmissions, 20ms interval */ +#define UNPROV_XMIT BLE_MESH_TRANSMIT(2, 20) + +/* 1 transmission, 20ms interval */ +#define PROV_XMIT BLE_MESH_TRANSMIT(0, 20) + +static struct k_delayed_work beacon_timer; + +static struct bt_mesh_subnet *cache_check(u8_t data[21]) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + struct bt_mesh_subnet *sub = &bt_mesh.sub[i]; + + if (sub->net_idx == BLE_MESH_KEY_UNUSED) { + continue; + } + + if (!memcmp(sub->beacon_cache, data, 21)) { + return sub; + } + } + + return NULL; +} + +static void cache_add(u8_t data[21], struct bt_mesh_subnet *sub) +{ + memcpy(sub->beacon_cache, data, 21); +} + +static void beacon_complete(int err, void *user_data) +{ + struct bt_mesh_subnet *sub = user_data; + + BT_DBG("err %d", err); + + sub->beacon_sent = k_uptime_get_32(); +} + +void bt_mesh_beacon_create(struct bt_mesh_subnet *sub, + struct net_buf_simple *buf) +{ + u8_t flags = bt_mesh_net_flags(sub); + struct bt_mesh_subnet_keys *keys; + + net_buf_simple_add_u8(buf, BEACON_TYPE_SECURE); + + if (sub->kr_flag) { + keys = &sub->keys[1]; + } else { + keys = &sub->keys[0]; + } + + net_buf_simple_add_u8(buf, flags); + + /* Network ID */ + net_buf_simple_add_mem(buf, keys->net_id, 8); + + /* IV Index */ + net_buf_simple_add_be32(buf, bt_mesh.iv_index); + + net_buf_simple_add_mem(buf, sub->auth, 8); + + BT_DBG("net_idx 0x%04x flags 0x%02x NetID %s", sub->net_idx, + flags, bt_hex(keys->net_id, 8)); + BT_DBG("IV Index 0x%08x Auth %s", bt_mesh.iv_index, + bt_hex(sub->auth, 8)); +} + +/* If the interval has passed or is within 5 seconds from now send a beacon */ +#define BEACON_THRESHOLD(sub) (K_SECONDS(10 * ((sub)->beacons_last + 1)) - \ + K_SECONDS(5)) + +static int secure_beacon_send(void) +{ + static const struct bt_mesh_send_cb send_cb = { + .end = beacon_complete, + }; + u32_t now = k_uptime_get_32(); + int i; + + BT_DBG("%s", __func__); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + struct bt_mesh_subnet *sub = &bt_mesh.sub[i]; + struct net_buf *buf; + u32_t time_diff; + + if (sub->net_idx == BLE_MESH_KEY_UNUSED) { + continue; + } + + time_diff = now - sub->beacon_sent; + if (time_diff < K_SECONDS(600) && + time_diff < BEACON_THRESHOLD(sub)) { + continue; + } + + buf = bt_mesh_adv_create(BLE_MESH_ADV_BEACON, PROV_XMIT, + K_NO_WAIT); + if (!buf) { + BT_ERR("%s, Unable to allocate beacon buffer", __func__); + return -ENOBUFS; + } + + bt_mesh_beacon_create(sub, &buf->b); + + bt_mesh_adv_send(buf, &send_cb, sub); + net_buf_unref(buf); + } + + return 0; +} + +static int unprovisioned_beacon_send(void) +{ +#if defined(CONFIG_BLE_MESH_PB_ADV) + const struct bt_mesh_prov *prov; + u8_t uri_hash[16] = { 0 }; + struct net_buf *buf; + u16_t oob_info; + + BT_DBG("%s", __func__); + + buf = bt_mesh_adv_create(BLE_MESH_ADV_BEACON, UNPROV_XMIT, K_NO_WAIT); + if (!buf) { + BT_ERR("%s, Unable to allocate beacon buffer", __func__); + return -ENOBUFS; + } + + prov = bt_mesh_prov_get(); + + net_buf_add_u8(buf, BEACON_TYPE_UNPROVISIONED); + net_buf_add_mem(buf, prov->uuid, 16); + + if (prov->uri && bt_mesh_s1(prov->uri, uri_hash) == 0) { + oob_info = prov->oob_info | BLE_MESH_PROV_OOB_URI; + } else { + oob_info = prov->oob_info; + } + + net_buf_add_be16(buf, oob_info); + net_buf_add_mem(buf, uri_hash, 4); + + bt_mesh_adv_send(buf, NULL, NULL); + net_buf_unref(buf); + + if (prov->uri) { + size_t len; + + buf = bt_mesh_adv_create(BLE_MESH_ADV_URI, UNPROV_XMIT, + K_NO_WAIT); + if (!buf) { + BT_ERR("Unable to allocate URI buffer"); + return -ENOBUFS; + } + + len = strlen(prov->uri); + if (net_buf_tailroom(buf) < len) { + BT_WARN("Too long URI to fit advertising data"); + } else { + net_buf_add_mem(buf, prov->uri, len); + bt_mesh_adv_send(buf, NULL, NULL); + } + + net_buf_unref(buf); + } + +#endif /* CONFIG_BLE_MESH_PB_ADV */ + return 0; +} + +static void update_beacon_observation(void) +{ + static bool first_half; + int i; + + /* Observation period is 20 seconds, whereas the beacon timer + * runs every 10 seconds. We process what's happened during the + * window only after the second half. + */ + first_half = !first_half; + if (first_half) { + return; + } + + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + struct bt_mesh_subnet *sub = &bt_mesh.sub[i]; + + if (sub->net_idx == BLE_MESH_KEY_UNUSED) { + continue; + } + + sub->beacons_last = sub->beacons_cur; + sub->beacons_cur = 0U; + } +} + +static void beacon_send(struct k_work *work) +{ + /* Don't send anything if we have an active provisioning link */ + if (IS_ENABLED(CONFIG_BLE_MESH_PROV) && bt_prov_active()) { + k_delayed_work_submit(&beacon_timer, UNPROVISIONED_INTERVAL); + return; + } + + BT_DBG("%s", __func__); + + if (bt_mesh_is_provisioned()) { + update_beacon_observation(); + secure_beacon_send(); + + /* Only resubmit if beaconing is still enabled */ + if (bt_mesh_beacon_get() == BLE_MESH_BEACON_ENABLED || + bt_mesh_atomic_test_bit(bt_mesh.flags, BLE_MESH_IVU_INITIATOR)) { + k_delayed_work_submit(&beacon_timer, + PROVISIONED_INTERVAL); + } + } else { + unprovisioned_beacon_send(); + k_delayed_work_submit(&beacon_timer, UNPROVISIONED_INTERVAL); + } + +} + +static void secure_beacon_recv(struct net_buf_simple *buf) +{ + u8_t *data, *net_id, *auth; + struct bt_mesh_subnet *sub; + u32_t iv_index; + bool new_key, kr_change, iv_change; + u8_t flags; + + if (buf->len < 21) { + BT_ERR("%s, Too short secure beacon (len %u)", __func__, buf->len); + return; + } + + sub = cache_check(buf->data); + if (sub) { + /* We've seen this beacon before - just update the stats */ + goto update_stats; + } + + /* So we can add to the cache if auth matches */ + data = buf->data; + + flags = net_buf_simple_pull_u8(buf); + net_id = net_buf_simple_pull_mem(buf, 8); + iv_index = net_buf_simple_pull_be32(buf); + auth = buf->data; + + BT_DBG("flags 0x%02x id %s iv_index 0x%08x", + flags, bt_hex(net_id, 8), iv_index); + + sub = bt_mesh_subnet_find(net_id, flags, iv_index, auth, &new_key); + if (!sub) { + BT_DBG("No subnet that matched beacon"); + return; + } + + if (sub->kr_phase == BLE_MESH_KR_PHASE_2 && !new_key) { + BT_WARN("Ignoring Phase 2 KR Update secured using old key"); + return; + } + + cache_add(data, sub); + + /* If we have NetKey0 accept initiation only from it */ + if (bt_mesh_subnet_get(BLE_MESH_KEY_PRIMARY) && + sub->net_idx != BLE_MESH_KEY_PRIMARY) { + BT_WARN("Ignoring secure beacon on non-primary subnet"); + goto update_stats; + } + + BT_DBG("net_idx 0x%04x iv_index 0x%08x, current iv_index 0x%08x", + sub->net_idx, iv_index, bt_mesh.iv_index); + + if (bt_mesh_atomic_test_bit(bt_mesh.flags, BLE_MESH_IVU_INITIATOR) && + (bt_mesh_atomic_test_bit(bt_mesh.flags, BLE_MESH_IVU_IN_PROGRESS) == + BLE_MESH_IV_UPDATE(flags))) { + bt_mesh_beacon_ivu_initiator(false); + } + + iv_change = bt_mesh_net_iv_update(iv_index, BLE_MESH_IV_UPDATE(flags)); + + kr_change = bt_mesh_kr_update(sub, BLE_MESH_KEY_REFRESH(flags), new_key); + if (kr_change) { + bt_mesh_net_beacon_update(sub); + } + + if (iv_change) { + /* Update all subnets */ + bt_mesh_net_sec_update(NULL); + } else if (kr_change) { + /* Key Refresh without IV Update only impacts one subnet */ + bt_mesh_net_sec_update(sub); + } + +update_stats: + if (bt_mesh_beacon_get() == BLE_MESH_BEACON_ENABLED && + sub->beacons_cur < 0xff) { + sub->beacons_cur++; + } +} + +void bt_mesh_beacon_recv(struct net_buf_simple *buf) +{ + u8_t type; + + BT_DBG("%u bytes: %s", buf->len, bt_hex(buf->data, buf->len)); + + if (buf->len < 1) { + BT_ERR("%s, Too short beacon", __func__); + return; + } + + type = net_buf_simple_pull_u8(buf); + switch (type) { + case BEACON_TYPE_UNPROVISIONED: + BT_DBG("Ignoring unprovisioned device beacon"); + break; + case BEACON_TYPE_SECURE: + secure_beacon_recv(buf); + break; + default: + BT_DBG("Unknown beacon type 0x%02x", type); + break; + } +} + +void bt_mesh_beacon_init(void) +{ + k_delayed_work_init(&beacon_timer, beacon_send); +} + +void bt_mesh_beacon_ivu_initiator(bool enable) +{ + bt_mesh_atomic_set_bit_to(bt_mesh.flags, BLE_MESH_IVU_INITIATOR, enable); + + if (enable) { + k_work_submit(&beacon_timer.work); + } else if (bt_mesh_beacon_get() == BLE_MESH_BEACON_DISABLED) { + k_delayed_work_cancel(&beacon_timer); + } +} + +void bt_mesh_beacon_enable(void) +{ + int i; + + if (!bt_mesh_is_provisioned()) { + k_work_submit(&beacon_timer.work); + return; + } + + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + struct bt_mesh_subnet *sub = &bt_mesh.sub[i]; + + if (sub->net_idx == BLE_MESH_KEY_UNUSED) { + continue; + } + + sub->beacons_last = 0U; + sub->beacons_cur = 0U; + + bt_mesh_net_beacon_update(sub); + } + + k_work_submit(&beacon_timer.work); +} + +void bt_mesh_beacon_disable(void) +{ + if (!bt_mesh_atomic_test_bit(bt_mesh.flags, BLE_MESH_IVU_INITIATOR)) { + k_delayed_work_cancel(&beacon_timer); + } +} + +#endif /* CONFIG_BLE_MESH_NODE */ diff --git a/components/bt/ble_mesh/mesh_core/beacon.h b/components/bt/ble_mesh/mesh_core/beacon.h new file mode 100644 index 0000000000..f410fa5d59 --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/beacon.h @@ -0,0 +1,24 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _BEACON_H_ +#define _BEACON_H_ + +void bt_mesh_beacon_enable(void); +void bt_mesh_beacon_disable(void); + +void bt_mesh_beacon_ivu_initiator(bool enable); + +void bt_mesh_beacon_recv(struct net_buf_simple *buf); + +void bt_mesh_beacon_create(struct bt_mesh_subnet *sub, + struct net_buf_simple *buf); + +void bt_mesh_beacon_init(void); + +#endif /* _BEACON_H_ */ diff --git a/components/bt/ble_mesh/mesh_core/cfg_cli.c b/components/bt/ble_mesh/mesh_core/cfg_cli.c new file mode 100644 index 0000000000..aa2daf0da8 --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/cfg_cli.c @@ -0,0 +1,1657 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include "osi/allocator.h" +#include "sdkconfig.h" +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BLE_MESH_DEBUG_MODEL) + +#include "mesh_types.h" +#include "mesh_util.h" +#include "mesh_kernel.h" +#include "mesh_trace.h" +#include "cfg_cli.h" + +#include "mesh.h" +#include "foundation.h" +#include "mesh_common.h" +#include "btc_ble_mesh_config_model.h" + +#define CID_NVAL 0xffff + +s32_t config_msg_timeout; + +static bt_mesh_config_client_t *cli; + +static const bt_mesh_client_op_pair_t cfg_op_pair[] = { + { OP_BEACON_GET, OP_BEACON_STATUS }, + { OP_BEACON_SET, OP_BEACON_STATUS }, + { OP_DEV_COMP_DATA_GET, OP_DEV_COMP_DATA_STATUS }, + { OP_DEFAULT_TTL_GET, OP_DEFAULT_TTL_STATUS }, + { OP_DEFAULT_TTL_SET, OP_DEFAULT_TTL_STATUS }, + { OP_GATT_PROXY_GET, OP_GATT_PROXY_STATUS }, + { OP_GATT_PROXY_SET, OP_GATT_PROXY_STATUS }, + { OP_RELAY_GET, OP_RELAY_STATUS }, + { OP_RELAY_SET, OP_RELAY_STATUS }, + { OP_MOD_PUB_GET, OP_MOD_PUB_STATUS }, + { OP_MOD_PUB_SET, OP_MOD_PUB_STATUS }, + { OP_MOD_PUB_VA_SET, OP_MOD_PUB_STATUS }, + { OP_MOD_SUB_ADD, OP_MOD_SUB_STATUS }, + { OP_MOD_SUB_VA_ADD, OP_MOD_SUB_STATUS }, + { OP_MOD_SUB_DEL, OP_MOD_SUB_STATUS }, + { OP_MOD_SUB_VA_DEL, OP_MOD_SUB_STATUS }, + { OP_MOD_SUB_OVERWRITE, OP_MOD_SUB_STATUS }, + { OP_MOD_SUB_VA_OVERWRITE, OP_MOD_SUB_STATUS }, + { OP_MOD_SUB_DEL_ALL, OP_MOD_SUB_STATUS }, + { OP_MOD_SUB_GET, OP_MOD_SUB_LIST }, + { OP_MOD_SUB_GET_VND, OP_MOD_SUB_LIST_VND }, + { OP_NET_KEY_ADD, OP_NET_KEY_STATUS }, + { OP_NET_KEY_UPDATE, OP_NET_KEY_STATUS }, + { OP_NET_KEY_DEL, OP_NET_KEY_STATUS }, + { OP_NET_KEY_GET, OP_NET_KEY_LIST }, + { OP_APP_KEY_ADD, OP_APP_KEY_STATUS }, + { OP_APP_KEY_UPDATE, OP_APP_KEY_STATUS }, + { OP_APP_KEY_DEL, OP_APP_KEY_STATUS }, + { OP_APP_KEY_GET, OP_APP_KEY_LIST }, + { OP_NODE_IDENTITY_GET, OP_NODE_IDENTITY_STATUS }, + { OP_NODE_IDENTITY_SET, OP_NODE_IDENTITY_STATUS }, + { OP_MOD_APP_BIND, OP_MOD_APP_STATUS }, + { OP_MOD_APP_UNBIND, OP_MOD_APP_STATUS }, + { OP_SIG_MOD_APP_GET, OP_SIG_MOD_APP_LIST }, + { OP_VND_MOD_APP_GET, OP_VND_MOD_APP_LIST }, + { OP_NODE_RESET, OP_NODE_RESET_STATUS }, + { OP_FRIEND_GET, OP_FRIEND_STATUS }, + { OP_FRIEND_SET, OP_FRIEND_STATUS }, + { OP_KRP_GET, OP_KRP_STATUS }, + { OP_KRP_SET, OP_KRP_STATUS }, + { OP_HEARTBEAT_PUB_GET, OP_HEARTBEAT_PUB_STATUS }, + { OP_HEARTBEAT_PUB_SET, OP_HEARTBEAT_PUB_STATUS }, + { OP_HEARTBEAT_SUB_GET, OP_HEARTBEAT_SUB_STATUS }, + { OP_HEARTBEAT_SUB_SET, OP_HEARTBEAT_SUB_STATUS }, + { OP_LPN_TIMEOUT_GET, OP_LPN_TIMEOUT_STATUS }, + { OP_NET_TRANSMIT_GET, OP_NET_TRANSMIT_STATUS }, + { OP_NET_TRANSMIT_SET, OP_NET_TRANSMIT_STATUS }, +}; + +static void timeout_handler(struct k_work *work) +{ + config_internal_data_t *internal = NULL; + bt_mesh_config_client_t *client = NULL; + bt_mesh_client_node_t *node = NULL; + + BT_WARN("Receive configuration status message timeout"); + + node = CONTAINER_OF(work, bt_mesh_client_node_t, timer.work); + if (!node || !node->ctx.model) { + BT_ERR("%s, Invalid parameter", __func__); + return; + } + + client = (bt_mesh_config_client_t *)node->ctx.model->user_data; + if (!client) { + BT_ERR("%s, Config Client user_data is NULL", __func__); + return; + } + + internal = (config_internal_data_t *)client->internal_data; + if (!internal) { + BT_ERR("%s, Config Client internal_data is NULL", __func__); + return; + } + + bt_mesh_callback_config_status_to_btc(node->opcode, 0x03, node->ctx.model, + &node->ctx, NULL, 0); + + bt_mesh_client_free_node(&internal->queue, node); + + return; +} + +static void cfg_client_cancel(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + void *status, size_t len) +{ + config_internal_data_t *data = NULL; + bt_mesh_client_node_t *node = NULL; + struct net_buf_simple buf = {0}; + u8_t evt_type = 0xFF; + + if (!model || !ctx) { + BT_ERR("%s, Invalid parameter", __func__); + return; + } + + data = (config_internal_data_t *)cli->internal_data; + if (!data) { + BT_ERR("%s, Config Client internal_data is NULL", __func__); + return; + } + + /* If it is a publish message, sent to the user directly. */ + buf.data = (u8_t *)status; + buf.len = (u16_t)len; + node = bt_mesh_is_model_message_publish(model, ctx, &buf, true); + if (!node) { + BT_DBG("Unexpected config status message 0x%x", ctx->recv_op); + } else { + switch (node->opcode) { + case OP_BEACON_GET: + case OP_DEV_COMP_DATA_GET: + case OP_DEFAULT_TTL_GET: + case OP_GATT_PROXY_GET: + case OP_RELAY_GET: + case OP_MOD_PUB_GET: + case OP_MOD_SUB_GET: + case OP_MOD_SUB_GET_VND: + case OP_NET_KEY_GET: + case OP_APP_KEY_GET: + case OP_NODE_IDENTITY_GET: + case OP_SIG_MOD_APP_GET: + case OP_VND_MOD_APP_GET: + case OP_FRIEND_GET: + case OP_KRP_GET: + case OP_HEARTBEAT_PUB_GET: + case OP_HEARTBEAT_SUB_GET: + case OP_LPN_TIMEOUT_GET: + case OP_NET_TRANSMIT_GET: + evt_type = 0x00; + break; + case OP_BEACON_SET: + case OP_DEFAULT_TTL_SET: + case OP_GATT_PROXY_SET: + case OP_RELAY_SET: + case OP_MOD_PUB_SET: + case OP_MOD_PUB_VA_SET: + case OP_MOD_SUB_ADD: + case OP_MOD_SUB_VA_ADD: + case OP_MOD_SUB_DEL: + case OP_MOD_SUB_VA_DEL: + case OP_MOD_SUB_OVERWRITE: + case OP_MOD_SUB_VA_OVERWRITE: + case OP_MOD_SUB_DEL_ALL: + case OP_NET_KEY_ADD: + case OP_NET_KEY_UPDATE: + case OP_NET_KEY_DEL: + case OP_APP_KEY_ADD: + case OP_APP_KEY_UPDATE: + case OP_APP_KEY_DEL: + case OP_NODE_IDENTITY_SET: + case OP_MOD_APP_BIND: + case OP_MOD_APP_UNBIND: + case OP_NODE_RESET: + case OP_FRIEND_SET: + case OP_KRP_SET: + case OP_HEARTBEAT_PUB_SET: + case OP_HEARTBEAT_SUB_SET: + case OP_NET_TRANSMIT_SET: + evt_type = 0x01; + break; + default: + break; + } + + bt_mesh_callback_config_status_to_btc(node->opcode, evt_type, model, + ctx, (const u8_t *)status, len); + // Don't forget to release the node at the end. + bt_mesh_client_free_node(&data->queue, node); + } + + switch (ctx->recv_op) { + case OP_DEV_COMP_DATA_STATUS: { + struct bt_mesh_cfg_comp_data_status *val; + val = (struct bt_mesh_cfg_comp_data_status *)status; + bt_mesh_free_buf(val->comp_data); + break; + } + case OP_MOD_SUB_LIST: + case OP_MOD_SUB_LIST_VND: { + struct bt_mesh_cfg_mod_sub_list *val = status; + bt_mesh_free_buf(val->addr); + break; + } + case OP_NET_KEY_LIST: { + struct bt_mesh_cfg_net_key_list *val = status; + bt_mesh_free_buf(val->net_idx); + break; + } + case OP_APP_KEY_LIST: { + struct bt_mesh_cfg_app_key_list *val = status; + bt_mesh_free_buf(val->app_idx); + break; + } + case OP_SIG_MOD_APP_LIST: + case OP_VND_MOD_APP_LIST: { + struct bt_mesh_cfg_mod_app_list *val = status; + bt_mesh_free_buf(val->app_idx); + break; + } + default: + break; + } +} + +static void comp_data_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_cfg_comp_data_status status = {0}; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->len, + bt_hex(buf->data, buf->len)); + + status.page = net_buf_simple_pull_u8(buf); + status.comp_data = bt_mesh_alloc_buf(buf->len); + if (!status.comp_data) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + + net_buf_simple_add_mem(status.comp_data, buf->data, buf->len); + + cfg_client_cancel(model, ctx, &status, sizeof(struct bt_mesh_cfg_comp_data_status)); +} + +static void state_status_u8(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + u8_t status = 0; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->len, + bt_hex(buf->data, buf->len)); + + status = net_buf_simple_pull_u8(buf); + + cfg_client_cancel(model, ctx, &status, sizeof(u8_t)); +} + +static void beacon_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + state_status_u8(model, ctx, buf); +} + +static void ttl_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + state_status_u8(model, ctx, buf); +} + +static void friend_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + state_status_u8(model, ctx, buf); +} + +static void gatt_proxy_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + state_status_u8(model, ctx, buf); +} + +static void relay_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_cfg_relay_status status = {0}; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->len, + bt_hex(buf->data, buf->len)); + + status.relay = net_buf_simple_pull_u8(buf); + status.retransmit = net_buf_simple_pull_u8(buf); + + cfg_client_cancel(model, ctx, &status, sizeof(struct bt_mesh_cfg_relay_status)); +} + +static void net_key_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_cfg_netkey_status status = {0}; + u16_t app_idx; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->len, + bt_hex(buf->data, buf->len)); + + status.status = net_buf_simple_pull_u8(buf); + key_idx_unpack(buf, &status.net_idx, &app_idx); + + cfg_client_cancel(model, ctx, &status, sizeof(struct bt_mesh_cfg_netkey_status)); +} + +static void app_key_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_cfg_appkey_status status = {0}; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->len, + bt_hex(buf->data, buf->len)); + + status.status = net_buf_simple_pull_u8(buf); + key_idx_unpack(buf, &status.net_idx, &status.app_idx); + + cfg_client_cancel(model, ctx, &status, sizeof(struct bt_mesh_cfg_appkey_status)); +} + +static void mod_app_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_cfg_mod_app_status status = {0}; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->len, + bt_hex(buf->data, buf->len)); + + status.status = net_buf_simple_pull_u8(buf); + status.elem_addr = net_buf_simple_pull_le16(buf); + status.app_idx = net_buf_simple_pull_le16(buf); + if (buf->len >= 4) { + status.cid = net_buf_simple_pull_le16(buf); + } else { + status.cid = CID_NVAL; + } + status.mod_id = net_buf_simple_pull_le16(buf); + + cfg_client_cancel(model, ctx, &status, sizeof(struct bt_mesh_cfg_mod_app_status)); +} + +static void mod_pub_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_cfg_mod_pub_status status = {0}; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->len, + bt_hex(buf->data, buf->len)); + + status.status = net_buf_simple_pull_u8(buf); + status.elem_addr = net_buf_simple_pull_le16(buf); + status.addr = net_buf_simple_pull_le16(buf); + status.app_idx = net_buf_simple_pull_le16(buf); + status.cred_flag = (status.app_idx & BIT(12)); + status.app_idx &= BIT_MASK(12); + status.ttl = net_buf_simple_pull_u8(buf); + status.period = net_buf_simple_pull_u8(buf); + status.transmit = net_buf_simple_pull_u8(buf); + if (buf->len >= 4) { + status.cid = net_buf_simple_pull_le16(buf); + } else { + status.cid = CID_NVAL; + } + status.mod_id = net_buf_simple_pull_le16(buf); + + cfg_client_cancel(model, ctx, &status, sizeof(struct bt_mesh_cfg_mod_pub_status)); +} + +static void mod_sub_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_cfg_mod_sub_status status = {0}; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->len, + bt_hex(buf->data, buf->len)); + + status.status = net_buf_simple_pull_u8(buf); + status.elem_addr = net_buf_simple_pull_le16(buf); + status.sub_addr = net_buf_simple_pull_le16(buf); + if (buf->len >= 4) { + status.cid = net_buf_simple_pull_le16(buf); + } else { + status.cid = CID_NVAL; + } + status.mod_id = net_buf_simple_pull_le16(buf); + + cfg_client_cancel(model, ctx, &status, sizeof(struct bt_mesh_cfg_mod_sub_status)); +} + +static void hb_sub_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_cfg_hb_sub_status status = {0}; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->len, + bt_hex(buf->data, buf->len)); + + status.status = net_buf_simple_pull_u8(buf); + status.src = net_buf_simple_pull_le16(buf); + status.dst = net_buf_simple_pull_le16(buf); + status.period = net_buf_simple_pull_u8(buf); + status.count = net_buf_simple_pull_u8(buf); + status.min = net_buf_simple_pull_u8(buf); + status.max = net_buf_simple_pull_u8(buf); + + cfg_client_cancel(model, ctx, &status, sizeof(struct bt_mesh_cfg_hb_sub_status)); +} + +static void hb_pub_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_cfg_hb_pub_status status = {0}; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->len, + bt_hex(buf->data, buf->len)); + + status.status = net_buf_simple_pull_u8(buf); + status.dst = net_buf_simple_pull_le16(buf); + status.count = net_buf_simple_pull_u8(buf); + status.period = net_buf_simple_pull_u8(buf); + status.ttl = net_buf_simple_pull_u8(buf); + status.feat = net_buf_simple_pull_u8(buf); + status.net_idx = net_buf_simple_pull_u8(buf); + + cfg_client_cancel(model, ctx, &status, sizeof(struct bt_mesh_cfg_hb_sub_status)); +} + +static void node_reset_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->len, + bt_hex(buf->data, buf->len)); + + cfg_client_cancel(model, ctx, NULL, 0); +} + +static void mod_sub_list(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_cfg_mod_sub_list list = {0}; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->len, + bt_hex(buf->data, buf->len)); + + list.status = net_buf_simple_pull_u8(buf); + list.elem_addr = net_buf_simple_pull_le16(buf); + if (ctx->recv_op == OP_MOD_SUB_LIST_VND) { + list.cid = net_buf_simple_pull_le16(buf); + } else { + list.cid = CID_NVAL; + } + list.mod_id = net_buf_simple_pull_le16(buf); + + list.addr = bt_mesh_alloc_buf(buf->len); + if (!list.addr) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + net_buf_simple_add_mem(list.addr, buf->data, buf->len); + + cfg_client_cancel(model, ctx, &list, sizeof(struct bt_mesh_cfg_mod_sub_list)); +} + +static void net_key_list(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_cfg_net_key_list list = {0}; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->len, + bt_hex(buf->data, buf->len)); + + list.net_idx = bt_mesh_alloc_buf(buf->len); + if (!list.net_idx) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + net_buf_simple_add_mem(list.net_idx, buf->data, buf->len); + + cfg_client_cancel(model, ctx, &list, sizeof(struct bt_mesh_cfg_net_key_list)); +} + +static void app_key_list(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_cfg_app_key_list list = {0}; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->len, + bt_hex(buf->data, buf->len)); + + list.status = net_buf_simple_pull_u8(buf); + list.net_idx = net_buf_simple_pull_le16(buf); + list.app_idx = bt_mesh_alloc_buf(buf->len); + if (!list.app_idx) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + net_buf_simple_add_mem(list.app_idx, buf->data, buf->len); + + cfg_client_cancel(model, ctx, &list, sizeof(struct bt_mesh_cfg_app_key_list)); +} + +static void node_id_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_cfg_node_id_status status = {0}; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->len, + bt_hex(buf->data, buf->len)); + + status.status = net_buf_simple_pull_u8(buf); + status.net_idx = net_buf_simple_pull_le16(buf); + status.identity = net_buf_simple_pull_u8(buf); + + cfg_client_cancel(model, ctx, &status, sizeof(struct bt_mesh_cfg_node_id_status)); +} + +static void mod_app_list(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_cfg_mod_app_list list = {0}; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->len, + bt_hex(buf->data, buf->len)); + + list.status = net_buf_simple_pull_u8(buf); + list.elem_addr = net_buf_simple_pull_le16(buf); + if (ctx->recv_op == OP_VND_MOD_APP_LIST) { + list.cid = net_buf_simple_pull_le16(buf); + } else { + list.cid = CID_NVAL; + } + list.mod_id = net_buf_simple_pull_le16(buf); + + list.app_idx = bt_mesh_alloc_buf(buf->len); + if (!list.app_idx) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + net_buf_simple_add_mem(list.app_idx, buf->data, buf->len); + + cfg_client_cancel(model, ctx, &list, sizeof(struct bt_mesh_cfg_mod_app_list)); +} + +static void kr_phase_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_cfg_key_refresh_status status = {0}; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->len, + bt_hex(buf->data, buf->len)); + + status.status = net_buf_simple_pull_u8(buf); + status.net_idx = net_buf_simple_pull_le16(buf); + status.phase = net_buf_simple_pull_u8(buf); + + cfg_client_cancel(model, ctx, &status, sizeof(struct bt_mesh_cfg_key_refresh_status)); +} + +static void lpn_pollto_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_cfg_lpn_pollto_status status = {0}; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->len, + bt_hex(buf->data, buf->len)); + + status.lpn_addr = net_buf_simple_pull_le16(buf); + status.timeout = net_buf_simple_pull_u8(buf); + status.timeout |= net_buf_simple_pull_u8(buf) << 8; + status.timeout |= net_buf_simple_pull_u8(buf) << 16; + + cfg_client_cancel(model, ctx, &status, sizeof(struct bt_mesh_cfg_lpn_pollto_status)); +} + +static void net_trans_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + state_status_u8(model, ctx, buf); +} + +const struct bt_mesh_model_op bt_mesh_cfg_cli_op[] = { + { OP_DEV_COMP_DATA_STATUS, 15, comp_data_status }, + { OP_BEACON_STATUS, 1, beacon_status }, + { OP_DEFAULT_TTL_STATUS, 1, ttl_status }, + { OP_FRIEND_STATUS, 1, friend_status }, + { OP_GATT_PROXY_STATUS, 1, gatt_proxy_status }, + { OP_RELAY_STATUS, 2, relay_status }, + { OP_NET_KEY_STATUS, 3, net_key_status }, + { OP_APP_KEY_STATUS, 4, app_key_status }, + { OP_MOD_APP_STATUS, 7, mod_app_status }, + { OP_MOD_PUB_STATUS, 12, mod_pub_status }, + { OP_MOD_SUB_STATUS, 7, mod_sub_status }, + { OP_HEARTBEAT_SUB_STATUS, 9, hb_sub_status }, + { OP_HEARTBEAT_PUB_STATUS, 10, hb_pub_status }, + { OP_NODE_RESET_STATUS, 0, node_reset_status }, + { OP_MOD_SUB_LIST, 5, mod_sub_list }, + { OP_MOD_SUB_LIST_VND, 7, mod_sub_list }, + { OP_NET_KEY_LIST, 2, net_key_list }, + { OP_APP_KEY_LIST, 3, app_key_list }, + { OP_NODE_IDENTITY_STATUS, 4, node_id_status }, + { OP_SIG_MOD_APP_LIST, 5, mod_app_list }, + { OP_VND_MOD_APP_LIST, 7, mod_app_list }, + { OP_KRP_STATUS, 4, kr_phase_status }, + { OP_LPN_TIMEOUT_STATUS, 5, lpn_pollto_status }, + { OP_NET_TRANSMIT_STATUS, 1, net_trans_status }, + BLE_MESH_MODEL_OP_END, +}; + +int bt_mesh_cfg_comp_data_get(struct bt_mesh_msg_ctx *ctx, u8_t page) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 1 + 4); + int err; + + if (!ctx || !ctx->addr) { + return -EINVAL; + } + + bt_mesh_model_msg_init(&msg, OP_DEV_COMP_DATA_GET); + net_buf_simple_add_u8(&msg, page); + + err = bt_mesh_client_send_msg(cli->model, OP_DEV_COMP_DATA_GET, ctx, + &msg, timeout_handler, config_msg_timeout, + true, NULL, NULL); + if (err) { + BT_ERR("%s, send failed (err %d)", __func__, err); + } + + return err; +} + +static int get_state_u8(struct bt_mesh_msg_ctx *ctx, u32_t op) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 0 + 4); + int err; + + bt_mesh_model_msg_init(&msg, op); + + err = bt_mesh_client_send_msg(cli->model, op, ctx, &msg, timeout_handler, + config_msg_timeout, true, NULL, NULL); + if (err) { + BT_ERR("%s, send failed (err %d)", __func__, err); + } + + return err; +} + +static int set_state_u8(struct bt_mesh_msg_ctx *ctx, u32_t op, u8_t new_val) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 1 + 4); + int err; + + bt_mesh_model_msg_init(&msg, op); + net_buf_simple_add_u8(&msg, new_val); + + err = bt_mesh_client_send_msg(cli->model, op, ctx, &msg, timeout_handler, + config_msg_timeout, true, NULL, NULL); + if (err) { + BT_ERR("%s, send failed (err %d)", __func__, err); + } + + return err; +} + +int bt_mesh_cfg_beacon_get(struct bt_mesh_msg_ctx *ctx) +{ + if (!ctx || !ctx->addr) { + return -EINVAL; + } + return get_state_u8(ctx, OP_BEACON_GET); +} + +int bt_mesh_cfg_beacon_set(struct bt_mesh_msg_ctx *ctx, u8_t val) +{ + if (!ctx || !ctx->addr) { + return -EINVAL; + } + return set_state_u8(ctx, OP_BEACON_SET, val); +} + +int bt_mesh_cfg_ttl_get(struct bt_mesh_msg_ctx *ctx) +{ + if (!ctx || !ctx->addr) { + return -EINVAL; + } + return get_state_u8(ctx, OP_DEFAULT_TTL_GET); +} + +int bt_mesh_cfg_ttl_set(struct bt_mesh_msg_ctx *ctx, u8_t val) +{ + if (!ctx || !ctx->addr) { + return -EINVAL; + } + return set_state_u8(ctx, OP_DEFAULT_TTL_SET, val); +} + +int bt_mesh_cfg_friend_get(struct bt_mesh_msg_ctx *ctx) +{ + if (!ctx || !ctx->addr) { + return -EINVAL; + } + return get_state_u8(ctx, OP_FRIEND_GET); +} + +int bt_mesh_cfg_friend_set(struct bt_mesh_msg_ctx *ctx, u8_t val) +{ + if (!ctx || !ctx->addr) { + return -EINVAL; + } + return set_state_u8(ctx, OP_FRIEND_SET, val); +} + +int bt_mesh_cfg_gatt_proxy_get(struct bt_mesh_msg_ctx *ctx) +{ + if (!ctx || !ctx->addr) { + return -EINVAL; + } + return get_state_u8(ctx, OP_GATT_PROXY_GET); +} + +int bt_mesh_cfg_gatt_proxy_set(struct bt_mesh_msg_ctx *ctx, u8_t val) +{ + if (!ctx || !ctx->addr) { + return -EINVAL; + } + return set_state_u8(ctx, OP_GATT_PROXY_SET, val); +} + +int bt_mesh_cfg_relay_get(struct bt_mesh_msg_ctx *ctx) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 0 + 4); + int err; + + if (!ctx || !ctx->addr) { + return -EINVAL; + } + + bt_mesh_model_msg_init(&msg, OP_RELAY_GET); + + err = bt_mesh_client_send_msg(cli->model, OP_RELAY_GET, ctx, &msg, + timeout_handler, config_msg_timeout, + true, NULL, NULL); + if (err) { + BT_ERR("%s, send failed (err %d)", __func__, err); + } + + return err; +} + +int bt_mesh_cfg_relay_set(struct bt_mesh_msg_ctx *ctx, u8_t new_relay, + u8_t new_transmit) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 2 + 4); + int err; + + if (!ctx || !ctx->addr) { + return -EINVAL; + } + + bt_mesh_model_msg_init(&msg, OP_RELAY_SET); + net_buf_simple_add_u8(&msg, new_relay); + net_buf_simple_add_u8(&msg, new_transmit); + + err = bt_mesh_client_send_msg(cli->model, OP_RELAY_SET, ctx, &msg, + timeout_handler, config_msg_timeout, + true, NULL, NULL); + if (err) { + BT_ERR("%s, send failed (err %d)", __func__, err); + } + + return err; +} + +int bt_mesh_cfg_net_key_add(struct bt_mesh_msg_ctx *ctx, u16_t key_net_idx, + const u8_t net_key[16]) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 18 + 4); + int err; + + if (!ctx || !ctx->addr || !net_key) { + return -EINVAL; + } + + bt_mesh_model_msg_init(&msg, OP_NET_KEY_ADD); + net_buf_simple_add_le16(&msg, key_net_idx); + net_buf_simple_add_mem(&msg, net_key, 16); + + err = bt_mesh_client_send_msg(cli->model, OP_NET_KEY_ADD, ctx, &msg, + timeout_handler, config_msg_timeout, + true, NULL, NULL); + if (err) { + BT_ERR("%s, send failed (err %d)", __func__, err); + } + + return err; +} + +int bt_mesh_cfg_app_key_add(struct bt_mesh_msg_ctx *ctx, u16_t key_net_idx, + u16_t key_app_idx, const u8_t app_key[16]) +{ + NET_BUF_SIMPLE_DEFINE(msg, 1 + 19 + 4); + int err; + + if (!ctx || !ctx->addr || !app_key) { + return -EINVAL; + } + + bt_mesh_model_msg_init(&msg, OP_APP_KEY_ADD); + key_idx_pack(&msg, key_net_idx, key_app_idx); + net_buf_simple_add_mem(&msg, app_key, 16); + + err = bt_mesh_client_send_msg(cli->model, OP_APP_KEY_ADD, ctx, &msg, + timeout_handler, config_msg_timeout, + true, NULL, NULL); + if (err) { + BT_ERR("%s, send failed (err %d)", __func__, err); + } + + return err; +} + +int bt_mesh_cfg_mod_app_bind(struct bt_mesh_msg_ctx *ctx, u16_t elem_addr, + u16_t mod_app_idx, u16_t mod_id, u16_t cid) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 8 + 4); + int err; + + if (!ctx || !ctx->addr) { + return -EINVAL; + } + + bt_mesh_model_msg_init(&msg, OP_MOD_APP_BIND); + net_buf_simple_add_le16(&msg, elem_addr); + net_buf_simple_add_le16(&msg, mod_app_idx); + if (cid != CID_NVAL) { + net_buf_simple_add_le16(&msg, cid); + } + net_buf_simple_add_le16(&msg, mod_id); + + err = bt_mesh_client_send_msg(cli->model, OP_MOD_APP_BIND, ctx, &msg, + timeout_handler, config_msg_timeout, + true, NULL, NULL); + if (err) { + BT_ERR("%s, send failed (err %d)", __func__, err); + } + + return err; +} + +static int mod_sub(u32_t op, struct bt_mesh_msg_ctx *ctx, u16_t elem_addr, + u16_t sub_addr, u16_t mod_id, u16_t cid) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 8 + 4); + int err; + + bt_mesh_model_msg_init(&msg, op); + net_buf_simple_add_le16(&msg, elem_addr); + net_buf_simple_add_le16(&msg, sub_addr); + if (cid != CID_NVAL) { + net_buf_simple_add_le16(&msg, cid); + } + net_buf_simple_add_le16(&msg, mod_id); + + err = bt_mesh_client_send_msg(cli->model, op, ctx, &msg, timeout_handler, + config_msg_timeout, true, NULL, NULL); + if (err) { + BT_ERR("%s, send failed (err %d)", __func__, err); + } + + return err; +} + +int bt_mesh_cfg_mod_sub_add(struct bt_mesh_msg_ctx *ctx, u16_t elem_addr, + u16_t sub_addr, u16_t mod_id, u16_t cid) +{ + if (!ctx || !ctx->addr) { + return -EINVAL; + } + return mod_sub(OP_MOD_SUB_ADD, ctx, elem_addr, sub_addr, mod_id, cid); +} + +int bt_mesh_cfg_mod_sub_del(struct bt_mesh_msg_ctx *ctx, u16_t elem_addr, + u16_t sub_addr, u16_t mod_id, u16_t cid) +{ + if (!ctx || !ctx->addr) { + return -EINVAL; + } + return mod_sub(OP_MOD_SUB_DEL, ctx, elem_addr, sub_addr, mod_id, cid); +} + +int bt_mesh_cfg_mod_sub_overwrite(struct bt_mesh_msg_ctx *ctx, u16_t elem_addr, + u16_t sub_addr, u16_t mod_id, u16_t cid) +{ + if (!ctx || !ctx->addr) { + return -EINVAL; + } + return mod_sub(OP_MOD_SUB_OVERWRITE, ctx, elem_addr, sub_addr, mod_id, cid); +} + +static int mod_sub_va(u32_t op, struct bt_mesh_msg_ctx *ctx, u16_t elem_addr, + const u8_t label[16], u16_t mod_id, u16_t cid) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 22 + 4); + int err; + + BT_DBG("net_idx 0x%04x addr 0x%04x elem_addr 0x%04x label %s", + ctx->net_idx, ctx->addr, elem_addr, label); + BT_DBG("mod_id 0x%04x cid 0x%04x", mod_id, cid); + + bt_mesh_model_msg_init(&msg, op); + net_buf_simple_add_le16(&msg, elem_addr); + net_buf_simple_add_mem(&msg, label, 16); + if (cid != CID_NVAL) { + net_buf_simple_add_le16(&msg, cid); + } + net_buf_simple_add_le16(&msg, mod_id); + + err = bt_mesh_client_send_msg(cli->model, op, ctx, &msg, timeout_handler, + config_msg_timeout, true, NULL, NULL); + if (err) { + BT_ERR("%s, send failed (err %d)", __func__, err); + } + + return err; +} + +int bt_mesh_cfg_mod_sub_va_add(struct bt_mesh_msg_ctx *ctx, u16_t elem_addr, + const u8_t label[16], u16_t mod_id, u16_t cid) +{ + if (!ctx || !ctx->addr || !label) { + return -EINVAL; + } + return mod_sub_va(OP_MOD_SUB_VA_ADD, ctx, elem_addr, label, mod_id, cid); +} + +int bt_mesh_cfg_mod_sub_va_del(struct bt_mesh_msg_ctx *ctx, u16_t elem_addr, + const u8_t label[16], u16_t mod_id, u16_t cid) +{ + if (!ctx || !ctx->addr || !label) { + return -EINVAL; + } + return mod_sub_va(OP_MOD_SUB_VA_DEL, ctx, elem_addr, label, mod_id, cid); +} + +int bt_mesh_cfg_mod_sub_va_overwrite(struct bt_mesh_msg_ctx *ctx, u16_t elem_addr, + const u8_t label[16], u16_t mod_id, u16_t cid) +{ + if (!ctx || !ctx->addr || !label) { + return -EINVAL; + } + return mod_sub_va(OP_MOD_SUB_VA_OVERWRITE, ctx, elem_addr, label, mod_id, cid); +} + +int bt_mesh_cfg_mod_pub_get(struct bt_mesh_msg_ctx *ctx, u16_t elem_addr, + u16_t mod_id, u16_t cid) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 6 + 4); + int err; + + if (!ctx || !ctx->addr) { + return -EINVAL; + } + + bt_mesh_model_msg_init(&msg, OP_MOD_PUB_GET); + net_buf_simple_add_le16(&msg, elem_addr); + if (cid != CID_NVAL) { + net_buf_simple_add_le16(&msg, cid); + } + net_buf_simple_add_le16(&msg, mod_id); + + err = bt_mesh_client_send_msg(cli->model, OP_MOD_PUB_GET, ctx, &msg, + timeout_handler, config_msg_timeout, + true, NULL, NULL); + if (err) { + BT_ERR("%s, send failed (err %d)", __func__, err); + } + + return err; +} + +int bt_mesh_cfg_mod_pub_set(struct bt_mesh_msg_ctx *ctx, u16_t elem_addr, + u16_t mod_id, u16_t cid, + struct bt_mesh_cfg_mod_pub *pub) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 13 + 4); + int err; + + if (!ctx || !ctx->addr || !pub) { + return -EINVAL; + } + + bt_mesh_model_msg_init(&msg, OP_MOD_PUB_SET); + net_buf_simple_add_le16(&msg, elem_addr); + net_buf_simple_add_le16(&msg, pub->addr); + net_buf_simple_add_le16(&msg, (pub->app_idx | (pub->cred_flag << 12))); + net_buf_simple_add_u8(&msg, pub->ttl); + net_buf_simple_add_u8(&msg, pub->period); + net_buf_simple_add_u8(&msg, pub->transmit); + if (cid != CID_NVAL) { + net_buf_simple_add_le16(&msg, cid); + } + net_buf_simple_add_le16(&msg, mod_id); + + err = bt_mesh_client_send_msg(cli->model, OP_MOD_PUB_SET, ctx, &msg, + timeout_handler, config_msg_timeout, + true, NULL, NULL); + if (err) { + BT_ERR("%s, send failed (err %d)", __func__, err); + } + + return err; +} + +int bt_mesh_cfg_hb_sub_set(struct bt_mesh_msg_ctx *ctx, + struct bt_mesh_cfg_hb_sub *sub) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 5 + 4); + int err; + + if (!ctx || !ctx->addr || !sub) { + return -EINVAL; + } + + bt_mesh_model_msg_init(&msg, OP_HEARTBEAT_SUB_SET); + net_buf_simple_add_le16(&msg, sub->src); + net_buf_simple_add_le16(&msg, sub->dst); + net_buf_simple_add_u8(&msg, sub->period); + + err = bt_mesh_client_send_msg(cli->model, OP_HEARTBEAT_SUB_SET, ctx, + &msg, timeout_handler, config_msg_timeout, + true, NULL, NULL); + if (err) { + BT_ERR("%s, send failed (err %d)", __func__, err); + } + + return err; +} + +int bt_mesh_cfg_hb_sub_get(struct bt_mesh_msg_ctx *ctx) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 0 + 4); + int err; + + if (!ctx || !ctx->addr) { + return -EINVAL; + } + + bt_mesh_model_msg_init(&msg, OP_HEARTBEAT_SUB_GET); + + err = bt_mesh_client_send_msg(cli->model, OP_HEARTBEAT_SUB_GET, ctx, + &msg, timeout_handler, config_msg_timeout, + true, NULL, NULL); + if (err) { + BT_ERR("%s, send failed (err %d)", __func__, err); + } + + return err; +} + +int bt_mesh_cfg_hb_pub_set(struct bt_mesh_msg_ctx *ctx, + const struct bt_mesh_cfg_hb_pub *pub) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 9 + 4); + int err; + + if (!ctx || !ctx->addr || !pub) { + return -EINVAL; + } + + bt_mesh_model_msg_init(&msg, OP_HEARTBEAT_PUB_SET); + net_buf_simple_add_le16(&msg, pub->dst); + net_buf_simple_add_u8(&msg, pub->count); + net_buf_simple_add_u8(&msg, pub->period); + net_buf_simple_add_u8(&msg, pub->ttl); + net_buf_simple_add_le16(&msg, pub->feat); + net_buf_simple_add_le16(&msg, pub->net_idx); + + err = bt_mesh_client_send_msg(cli->model, OP_HEARTBEAT_PUB_SET, ctx, + &msg, timeout_handler, config_msg_timeout, + true, NULL, NULL); + if (err) { + BT_ERR("%s, send failed (err %d)", __func__, err); + } + + return err; +} + +int bt_mesh_cfg_hb_pub_get(struct bt_mesh_msg_ctx *ctx) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 0 + 4); + int err; + + if (!ctx || !ctx->addr) { + return -EINVAL; + } + + bt_mesh_model_msg_init(&msg, OP_HEARTBEAT_PUB_GET); + + err = bt_mesh_client_send_msg(cli->model, OP_HEARTBEAT_PUB_GET, ctx, + &msg, timeout_handler, config_msg_timeout, + true, NULL, NULL); + if (err) { + BT_ERR("%s, send failed (err %d)", __func__, err); + } + + return err; +} + +int bt_mesh_cfg_node_reset(struct bt_mesh_msg_ctx *ctx) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 0 + 4); + int err; + + if (!ctx || !ctx->addr) { + return -EINVAL; + } + + bt_mesh_model_msg_init(&msg, OP_NODE_RESET); + + err = bt_mesh_client_send_msg(cli->model, OP_NODE_RESET, ctx, &msg, + timeout_handler, config_msg_timeout, + true, NULL, NULL); + if (err) { + BT_ERR("%s, send failed (err %d)", __func__, err); + } + + return err; +} + +int bt_mesh_cfg_mod_pub_va_set(struct bt_mesh_msg_ctx *ctx, u16_t elem_addr, + u16_t mod_id, u16_t cid, const u8_t label[16], + struct bt_mesh_cfg_mod_pub *pub) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 27 + 4); + int err; + + if (!ctx || !ctx->addr || !label || !pub) { + return -EINVAL; + } + + bt_mesh_model_msg_init(&msg, OP_MOD_PUB_VA_SET); + net_buf_simple_add_le16(&msg, elem_addr); + net_buf_simple_add_mem(&msg, label, 16); + net_buf_simple_add_le16(&msg, (pub->app_idx | (pub->cred_flag << 12))); + net_buf_simple_add_u8(&msg, pub->ttl); + net_buf_simple_add_u8(&msg, pub->period); + net_buf_simple_add_u8(&msg, pub->transmit); + if (cid != CID_NVAL) { + net_buf_simple_add_le16(&msg, cid); + } + net_buf_simple_add_le16(&msg, mod_id); + + err = bt_mesh_client_send_msg(cli->model, OP_MOD_PUB_VA_SET, ctx, &msg, + timeout_handler, config_msg_timeout, + true, NULL, NULL); + if (err) { + BT_ERR("%s, send failed (err %d)", __func__, err); + } + + return err; +} + +int bt_mesh_cfg_mod_sub_del_all(struct bt_mesh_msg_ctx *ctx, u16_t elem_addr, + u16_t mod_id, u16_t cid) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 6 + 4); + int err; + + if (!ctx || !ctx->addr) { + return -EINVAL; + } + + bt_mesh_model_msg_init(&msg, OP_MOD_SUB_DEL_ALL); + net_buf_simple_add_le16(&msg, elem_addr); + if (cid != CID_NVAL) { + net_buf_simple_add_le16(&msg, cid); + } + net_buf_simple_add_le16(&msg, mod_id); + + err = bt_mesh_client_send_msg(cli->model, OP_MOD_SUB_DEL_ALL, ctx, &msg, + timeout_handler, config_msg_timeout, + true, NULL, NULL); + if (err) { + BT_ERR("%s, send failed (err %d)", __func__, err); + } + + return err; +} + +static int mod_sub_get(u32_t op, struct bt_mesh_msg_ctx *ctx, + u16_t elem_addr, u16_t mod_id, u16_t cid) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 6 + 4); + int err; + + bt_mesh_model_msg_init(&msg, op); + net_buf_simple_add_le16(&msg, elem_addr); + if (cid != CID_NVAL) { + net_buf_simple_add_le16(&msg, cid); + } + net_buf_simple_add_le16(&msg, mod_id); + + err = bt_mesh_client_send_msg(cli->model, op, ctx, &msg, timeout_handler, + config_msg_timeout, true, NULL, NULL); + if (err) { + BT_ERR("%s, send failed (err %d)", __func__, err); + } + + return err; +} + +int bt_mesh_cfg_mod_sub_get(struct bt_mesh_msg_ctx *ctx, u16_t elem_addr, u16_t mod_id) +{ + if (!ctx || !ctx->addr) { + return -EINVAL; + } + return mod_sub_get(OP_MOD_SUB_GET, ctx, elem_addr, mod_id, CID_NVAL); +} + +int bt_mesh_cfg_mod_sub_get_vnd(struct bt_mesh_msg_ctx *ctx, u16_t elem_addr, + u16_t mod_id, u16_t cid) +{ + if (!ctx || !ctx->addr || cid == CID_NVAL) { + return -EINVAL; + } + return mod_sub_get(OP_MOD_SUB_GET_VND, ctx, elem_addr, mod_id, cid); +} + +int bt_mesh_cfg_net_key_update(struct bt_mesh_msg_ctx *ctx, u16_t net_idx, + const u8_t net_key[16]) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 18 + 4); + int err; + + if (!ctx || !ctx->addr || !net_key) { + return -EINVAL; + } + + bt_mesh_model_msg_init(&msg, OP_NET_KEY_UPDATE); + net_buf_simple_add_le16(&msg, net_idx); + net_buf_simple_add_mem(&msg, net_key, 16); + + err = bt_mesh_client_send_msg(cli->model, OP_NET_KEY_UPDATE, ctx, &msg, + timeout_handler, config_msg_timeout, + true, NULL, NULL); + if (err) { + BT_ERR("%s, send failed (err %d)", __func__, err); + } + + return err; +} + +int bt_mesh_cfg_net_key_delete(struct bt_mesh_msg_ctx *ctx, u16_t net_idx) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 2 + 4); + int err; + + if (!ctx || !ctx->addr) { + return -EINVAL; + } + + bt_mesh_model_msg_init(&msg, OP_NET_KEY_DEL); + net_buf_simple_add_le16(&msg, net_idx); + + err = bt_mesh_client_send_msg(cli->model, OP_NET_KEY_DEL, ctx, &msg, + timeout_handler, config_msg_timeout, + true, NULL, NULL); + if (err) { + BT_ERR("%s, send failed (err %d)", __func__, err); + } + + return err; +} + +int bt_mesh_cfg_net_key_get(struct bt_mesh_msg_ctx *ctx) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 0 + 4); + int err; + + if (!ctx || !ctx->addr) { + return -EINVAL; + } + + bt_mesh_model_msg_init(&msg, OP_NET_KEY_GET); + + err = bt_mesh_client_send_msg(cli->model, OP_NET_KEY_GET, ctx, &msg, + timeout_handler, config_msg_timeout, + true, NULL, NULL); + if (err) { + BT_ERR("%s, send failed (err %d)", __func__, err); + } + + return err; +} + +int bt_mesh_cfg_app_key_update(struct bt_mesh_msg_ctx *ctx, u16_t net_idx, + u16_t app_idx, const u8_t app_key[16]) +{ + NET_BUF_SIMPLE_DEFINE(msg, 1 + 19 + 4); + int err; + + if (!ctx || !ctx->addr || !app_key) { + return -EINVAL; + } + + bt_mesh_model_msg_init(&msg, OP_APP_KEY_UPDATE); + key_idx_pack(&msg, net_idx, app_idx); + net_buf_simple_add_mem(&msg, app_key, 16); + + err = bt_mesh_client_send_msg(cli->model, OP_APP_KEY_UPDATE, ctx, &msg, + timeout_handler, config_msg_timeout, + true, NULL, NULL); + if (err) { + BT_ERR("%s, send failed (err %d)", __func__, err); + } + + return err; +} + +int bt_mesh_cfg_app_key_delete(struct bt_mesh_msg_ctx *ctx, u16_t net_idx, u16_t app_idx) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 3 + 4); + int err; + + if (!ctx || !ctx->addr) { + return -EINVAL; + } + + bt_mesh_model_msg_init(&msg, OP_APP_KEY_DEL); + key_idx_pack(&msg, net_idx, app_idx); + + err = bt_mesh_client_send_msg(cli->model, OP_APP_KEY_DEL, ctx, &msg, + timeout_handler, config_msg_timeout, + true, NULL, NULL); + if (err) { + BT_ERR("%s, send failed (err %d)", __func__, err); + } + + return err; +} + +int bt_mesh_cfg_app_key_get(struct bt_mesh_msg_ctx *ctx, u16_t net_idx) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 2 + 4); + int err; + + if (!ctx || !ctx->addr) { + return -EINVAL; + } + + bt_mesh_model_msg_init(&msg, OP_APP_KEY_GET); + net_buf_simple_add_le16(&msg, net_idx); + + err = bt_mesh_client_send_msg(cli->model, OP_APP_KEY_GET, ctx, &msg, + timeout_handler, config_msg_timeout, + true, NULL, NULL); + if (err) { + BT_ERR("%s, send failed (err %d)", __func__, err); + } + + return err; +} + +static int node_identity_op(u32_t op, struct bt_mesh_msg_ctx *ctx, + u16_t net_idx, u8_t identity) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 3 + 4); + int err; + + bt_mesh_model_msg_init(&msg, op); + net_buf_simple_add_le16(&msg, net_idx); + if (op == OP_NODE_IDENTITY_SET) { + net_buf_simple_add_u8(&msg, identity); + } + + err = bt_mesh_client_send_msg(cli->model, op, ctx, &msg, timeout_handler, + config_msg_timeout, true, NULL, NULL); + if (err) { + BT_ERR("%s, send failed (err %d)", __func__, err); + } + + return err; +} + +int bt_mesh_cfg_node_identity_get(struct bt_mesh_msg_ctx *ctx, u16_t net_idx) +{ + if (!ctx || !ctx->addr) { + return -EINVAL; + } + return node_identity_op(OP_NODE_IDENTITY_GET, ctx, net_idx, 0xFF); +} + +int bt_mesh_cfg_node_identity_set(struct bt_mesh_msg_ctx *ctx, u16_t net_idx, u8_t identity) +{ + if (!ctx || !ctx->addr || identity > 0x01) { + return -EINVAL; + } + return node_identity_op(OP_NODE_IDENTITY_SET, ctx, net_idx, identity); +} + +int bt_mesh_cfg_mod_app_unbind(struct bt_mesh_msg_ctx *ctx, u16_t elem_addr, + u16_t app_idx, u16_t mod_id, u16_t cid) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 8 + 4); + int err; + + if (!ctx || !ctx->addr) { + return -EINVAL; + } + + bt_mesh_model_msg_init(&msg, OP_MOD_APP_UNBIND); + net_buf_simple_add_le16(&msg, elem_addr); + net_buf_simple_add_le16(&msg, app_idx); + if (cid != CID_NVAL) { + net_buf_simple_add_le16(&msg, cid); + } + net_buf_simple_add_le16(&msg, mod_id); + + err = bt_mesh_client_send_msg(cli->model, OP_MOD_APP_UNBIND, ctx, &msg, + timeout_handler, config_msg_timeout, + true, NULL, NULL); + if (err) { + BT_ERR("%s, send failed (err %d)", __func__, err); + } + + return err; +} + +static int mod_app_get(u32_t op, struct bt_mesh_msg_ctx *ctx, + u16_t elem_addr, u16_t mod_id, u16_t cid) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 6 + 4); + int err; + + bt_mesh_model_msg_init(&msg, op); + net_buf_simple_add_le16(&msg, elem_addr); + if (cid != CID_NVAL) { + net_buf_simple_add_le16(&msg, cid); + } + net_buf_simple_add_le16(&msg, mod_id); + + err = bt_mesh_client_send_msg(cli->model, op, ctx, &msg, timeout_handler, + config_msg_timeout, true, NULL, NULL); + if (err) { + BT_ERR("%s, send failed (err %d)", __func__, err); + } + + return err; +} + +int bt_mesh_cfg_mod_app_get(struct bt_mesh_msg_ctx *ctx, u16_t elem_addr, u16_t mod_id) +{ + if (!ctx || !ctx->addr) { + return -EINVAL; + } + return mod_app_get(OP_SIG_MOD_APP_GET, ctx, elem_addr, mod_id, CID_NVAL); +} + +int bt_mesh_cfg_mod_app_get_vnd(struct bt_mesh_msg_ctx *ctx, u16_t elem_addr, + u16_t mod_id, u16_t cid) +{ + if (!ctx || !ctx->addr || cid == CID_NVAL) { + return -EINVAL; + } + return mod_app_get(OP_VND_MOD_APP_GET, ctx, elem_addr, mod_id, cid); +} + +static int kr_phase_op(u32_t op, struct bt_mesh_msg_ctx *ctx, + u16_t net_idx, u8_t transition) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 3 + 4); + int err; + + bt_mesh_model_msg_init(&msg, op); + net_buf_simple_add_le16(&msg, net_idx); + if (op == OP_KRP_SET) { + net_buf_simple_add_u8(&msg, transition); + } + + err = bt_mesh_client_send_msg(cli->model, op, ctx, &msg, timeout_handler, + config_msg_timeout, true, NULL, NULL); + if (err) { + BT_ERR("%s, send failed (err %d)", __func__, err); + } + + return err; +} + +int bt_mesh_cfg_kr_phase_get(struct bt_mesh_msg_ctx *ctx, u16_t net_idx) +{ + if (!ctx || !ctx->addr) { + return -EINVAL; + } + return kr_phase_op(OP_KRP_GET, ctx, net_idx, 0xFF); +} + +int bt_mesh_cfg_kr_phase_set(struct bt_mesh_msg_ctx *ctx, u16_t net_idx, u8_t transition) +{ + if (!ctx || !ctx->addr || transition > 0x03) { + return -EINVAL; + } + return kr_phase_op(OP_KRP_SET, ctx, net_idx, transition);; +} + +int bt_mesh_cfg_lpn_timeout_get(struct bt_mesh_msg_ctx *ctx, u16_t lpn_addr) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 2 + 4); + int err; + + if (!ctx || !ctx->addr) { + return -EINVAL; + } + + bt_mesh_model_msg_init(&msg, OP_LPN_TIMEOUT_GET); + net_buf_simple_add_le16(&msg, lpn_addr); + + err = bt_mesh_client_send_msg(cli->model, OP_LPN_TIMEOUT_GET, ctx, &msg, + timeout_handler, config_msg_timeout, + true, NULL, NULL); + if (err) { + BT_ERR("%s, send failed (err %d)", __func__, err); + } + + return err; +} + +int bt_mesh_cfg_net_transmit_get(struct bt_mesh_msg_ctx *ctx) +{ + if (!ctx || !ctx->addr) { + return -EINVAL; + } + return get_state_u8(ctx, OP_NET_TRANSMIT_GET); +} + +int bt_mesh_cfg_net_transmit_set(struct bt_mesh_msg_ctx *ctx, u8_t transmit) +{ + if (!ctx || !ctx->addr) { + return -EINVAL; + } + return set_state_u8(ctx, OP_NET_TRANSMIT_SET, transmit); +} + +s32_t bt_mesh_cfg_cli_timeout_get(void) +{ + return config_msg_timeout; +} + +void bt_mesh_cfg_cli_timeout_set(s32_t timeout) +{ + config_msg_timeout = timeout; +} + +int bt_mesh_cfg_cli_init(struct bt_mesh_model *model, bool primary) +{ + config_internal_data_t *internal = NULL; + bt_mesh_config_client_t *client = NULL; + + BT_DBG("primary %u", primary); + + if (!primary) { + BT_ERR("Configuration Client only allowed in primary element"); + return -EINVAL; + } + + if (!model) { + BT_ERR("Configuration Client model is NULL"); + return -EINVAL; + } + + client = (bt_mesh_config_client_t *)model->user_data; + if (!client) { + BT_ERR("No Configuration Client context provided"); + return -EINVAL; + } + + /* TODO: call osi_free() when deinit function is invoked*/ + internal = osi_calloc(sizeof(config_internal_data_t)); + if (!internal) { + BT_ERR("Allocate memory for Configuration Client internal data fail"); + return -ENOMEM; + } + + sys_slist_init(&internal->queue); + + client->model = model; + client->op_pair_size = ARRAY_SIZE(cfg_op_pair); + client->op_pair = cfg_op_pair; + client->internal_data = internal; + + cli = client; + + /* Configuration Model security is device-key based */ + model->keys[0] = BLE_MESH_KEY_DEV; + + return 0; +} diff --git a/components/bt/ble_mesh/mesh_core/cfg_srv.c b/components/bt/ble_mesh/mesh_core/cfg_srv.c new file mode 100644 index 0000000000..4e330e61a0 --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/cfg_srv.c @@ -0,0 +1,3593 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include "sdkconfig.h" +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BLE_MESH_DEBUG_MODEL) + +#include "mesh_types.h" +#include "mesh_util.h" +#include "mesh_main.h" +#include "mesh_trace.h" +#include "cfg_srv.h" +#include "settings.h" + +#include "mesh.h" +#include "adv.h" +#include "net.h" +#include "lpn.h" +#include "transport.h" +#include "crypto.h" +#include "access.h" +#include "beacon.h" +#include "proxy.h" +#include "foundation.h" +#include "friend.h" +#include "settings.h" + +#include "mesh_common.h" +#include "btc_ble_mesh_config_model.h" + +#define DEFAULT_TTL 7 + +/* Maximum message length is 384 in BLE Mesh. Here for composition data, + * due to 1 octet opcode and 4 octets TransMIC, 379 octets can be used to + * store device composition data. + */ +#define COMP_DATA_MAX_LEN 379 + +static struct bt_mesh_cfg_srv *conf; + +static struct label { + u16_t ref; + u16_t addr; + u8_t uuid[16]; +} labels[CONFIG_BLE_MESH_LABEL_COUNT]; + +static void hb_send(struct bt_mesh_model *model) +{ + struct bt_mesh_cfg_srv *cfg = model->user_data; + u16_t feat = 0U; + struct __packed { + u8_t init_ttl; + u16_t feat; + } hb; + struct bt_mesh_msg_ctx ctx = { + .net_idx = cfg->hb_pub.net_idx, + .app_idx = BLE_MESH_KEY_UNUSED, + .addr = cfg->hb_pub.dst, + .send_ttl = cfg->hb_pub.ttl, + }; + struct bt_mesh_net_tx tx = { + .sub = bt_mesh_subnet_get(cfg->hb_pub.net_idx), + .ctx = &ctx, + .src = bt_mesh_model_elem(model)->addr, + .xmit = bt_mesh_net_transmit_get(), + }; + + hb.init_ttl = cfg->hb_pub.ttl; + + if (bt_mesh_relay_get() == BLE_MESH_RELAY_ENABLED) { + feat |= BLE_MESH_FEAT_RELAY; + } + + if (bt_mesh_gatt_proxy_get() == BLE_MESH_GATT_PROXY_ENABLED) { + feat |= BLE_MESH_FEAT_PROXY; + } + + if (bt_mesh_friend_get() == BLE_MESH_FRIEND_ENABLED) { + feat |= BLE_MESH_FEAT_FRIEND; + } + +#if defined(CONFIG_BLE_MESH_LOW_POWER) + if (bt_mesh.lpn.state != BLE_MESH_LPN_DISABLED) { + feat |= BLE_MESH_FEAT_LOW_POWER; + } +#endif + + hb.feat = sys_cpu_to_be16(feat); + + BT_DBG("InitTTL %u feat 0x%04x", cfg->hb_pub.ttl, feat); + + bt_mesh_ctl_send(&tx, TRANS_CTL_OP_HEARTBEAT, &hb, sizeof(hb), + NULL, NULL, NULL); +} + +static int comp_add_elem(struct net_buf_simple *buf, struct bt_mesh_elem *elem, + bool primary) +{ + struct bt_mesh_model *mod; + int i; + + if (net_buf_simple_tailroom(buf) < + 4 + (elem->model_count * 2U) + (elem->vnd_model_count * 2U)) { + BT_ERR("%s, Too large device composition", __func__); + return -E2BIG; + } + + net_buf_simple_add_le16(buf, elem->loc); + + net_buf_simple_add_u8(buf, elem->model_count); + net_buf_simple_add_u8(buf, elem->vnd_model_count); + + for (i = 0; i < elem->model_count; i++) { + mod = &elem->models[i]; + net_buf_simple_add_le16(buf, mod->id); + } + + for (i = 0; i < elem->vnd_model_count; i++) { + mod = &elem->vnd_models[i]; + net_buf_simple_add_le16(buf, mod->vnd.company); + net_buf_simple_add_le16(buf, mod->vnd.id); + } + + return 0; +} + +static int comp_get_page_0(struct net_buf_simple *buf) +{ + u16_t feat = 0U; + const struct bt_mesh_comp *comp; + int i; + + comp = bt_mesh_comp_get(); + + if (IS_ENABLED(CONFIG_BLE_MESH_RELAY)) { + feat |= BLE_MESH_FEAT_RELAY; + } + + if (IS_ENABLED(CONFIG_BLE_MESH_GATT_PROXY)) { + feat |= BLE_MESH_FEAT_PROXY; + } + + if (IS_ENABLED(CONFIG_BLE_MESH_FRIEND)) { + feat |= BLE_MESH_FEAT_FRIEND; + } + + if (IS_ENABLED(CONFIG_BLE_MESH_LOW_POWER)) { + feat |= BLE_MESH_FEAT_LOW_POWER; + } + + net_buf_simple_add_le16(buf, comp->cid); + net_buf_simple_add_le16(buf, comp->pid); + net_buf_simple_add_le16(buf, comp->vid); + net_buf_simple_add_le16(buf, CONFIG_BLE_MESH_CRPL); + net_buf_simple_add_le16(buf, feat); + + for (i = 0; i < comp->elem_count; i++) { + int err; + + err = comp_add_elem(buf, &comp->elem[i], i == 0); + if (err) { + return err; + } + } + + return 0; +} + +static void dev_comp_data_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct net_buf_simple *sdu = NULL; + u8_t page; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->len, + bt_hex(buf->data, buf->len)); + + page = net_buf_simple_pull_u8(buf); + if (page != 0U) { + BT_WARN("Composition page %u not available", page); + page = 0U; + } + + sdu = bt_mesh_alloc_buf(MIN(BLE_MESH_TX_SDU_MAX, COMP_DATA_MAX_LEN)); + if (!sdu) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + + bt_mesh_model_msg_init(sdu, OP_DEV_COMP_DATA_STATUS); + + net_buf_simple_add_u8(sdu, page); + if (comp_get_page_0(sdu) < 0) { + BT_ERR("%s, Unable to get composition page 0", __func__); + bt_mesh_free_buf(sdu); + return; + } + + if (bt_mesh_model_send(model, ctx, sdu, NULL, NULL)) { + BT_ERR("%s, Unable to send Config Composition Data Status", __func__); + } + + bt_mesh_free_buf(sdu); + return; +} + +static struct bt_mesh_model *get_model(struct bt_mesh_elem *elem, + struct net_buf_simple *buf, bool *vnd) +{ + if (buf->len < 4) { + u16_t id; + + id = net_buf_simple_pull_le16(buf); + + BT_DBG("ID 0x%04x addr 0x%04x", id, elem->addr); + + *vnd = false; + + return bt_mesh_model_find(elem, id); + } else { + u16_t company, id; + + company = net_buf_simple_pull_le16(buf); + id = net_buf_simple_pull_le16(buf); + + BT_DBG("Company 0x%04x ID 0x%04x addr 0x%04x", company, id, + elem->addr); + + *vnd = true; + + return bt_mesh_model_find_vnd(elem, company, id); + } +} + +static bool app_key_is_valid(u16_t app_idx) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.app_keys); i++) { + struct bt_mesh_app_key *key = &bt_mesh.app_keys[i]; + + if (key->net_idx != BLE_MESH_KEY_UNUSED && + key->app_idx == app_idx) { + return true; + } + } + + return false; +} + +static u8_t _mod_pub_set(struct bt_mesh_model *model, u16_t pub_addr, + u16_t app_idx, u8_t cred_flag, u8_t ttl, u8_t period, + u8_t retransmit, bool store) +{ + if (!model->pub) { + return STATUS_NVAL_PUB_PARAM; + } + + if (!IS_ENABLED(CONFIG_BLE_MESH_LOW_POWER) && cred_flag) { + return STATUS_FEAT_NOT_SUPP; + } + + if (!model->pub->update && period) { + return STATUS_NVAL_PUB_PARAM; + } + + if (pub_addr == BLE_MESH_ADDR_UNASSIGNED) { + if (model->pub->addr == BLE_MESH_ADDR_UNASSIGNED) { + return STATUS_SUCCESS; + } + + model->pub->addr = BLE_MESH_ADDR_UNASSIGNED; + model->pub->key = 0U; + model->pub->cred = 0U; + model->pub->ttl = 0U; + model->pub->period = 0U; + model->pub->retransmit = 0U; + model->pub->count = 0U; + + if (model->pub->update) { + k_delayed_work_cancel(&model->pub->timer); + } + + if (IS_ENABLED(CONFIG_BLE_MESH_SETTINGS) && store) { + bt_mesh_store_mod_pub(model); + } + + return STATUS_SUCCESS; + } + + if (!bt_mesh_app_key_find(app_idx)) { + return STATUS_INVALID_APPKEY; + } + + model->pub->addr = pub_addr; + model->pub->key = app_idx; + model->pub->cred = cred_flag; + model->pub->ttl = ttl; + model->pub->period = period; + model->pub->retransmit = retransmit; + + if (model->pub->update) { + s32_t period_ms; + + period_ms = bt_mesh_model_pub_period_get(model); + BT_DBG("period %u ms", period_ms); + + if (period_ms) { + k_delayed_work_submit(&model->pub->timer, period_ms); + } else { + k_delayed_work_cancel(&model->pub->timer); + } + } + + if (IS_ENABLED(CONFIG_BLE_MESH_SETTINGS) && store) { + bt_mesh_store_mod_pub(model); + } + + return STATUS_SUCCESS; +} + +static u8_t mod_bind(struct bt_mesh_model *model, u16_t key_idx) +{ + int i; + + BT_DBG("model %p key_idx 0x%03x", model, key_idx); + + if (!app_key_is_valid(key_idx)) { + return STATUS_INVALID_APPKEY; + } + + for (i = 0; i < ARRAY_SIZE(model->keys); i++) { + /* Treat existing binding as success */ + if (model->keys[i] == key_idx) { + return STATUS_SUCCESS; + } + } + + for (i = 0; i < ARRAY_SIZE(model->keys); i++) { + if (model->keys[i] == BLE_MESH_KEY_UNUSED) { + model->keys[i] = key_idx; + + if (IS_ENABLED(CONFIG_BLE_MESH_SETTINGS)) { + bt_mesh_store_mod_bind(model); + } + + return STATUS_SUCCESS; + } + } + + return STATUS_INSUFF_RESOURCES; +} + +static u8_t mod_unbind(struct bt_mesh_model *model, u16_t key_idx, bool store) +{ + int i; + + BT_DBG("model %p key_idx 0x%03x store %u", model, key_idx, store); + + if (!app_key_is_valid(key_idx)) { + return STATUS_INVALID_APPKEY; + } + + for (i = 0; i < ARRAY_SIZE(model->keys); i++) { + if (model->keys[i] != key_idx) { + continue; + } + + model->keys[i] = BLE_MESH_KEY_UNUSED; + + if (IS_ENABLED(CONFIG_BLE_MESH_SETTINGS) && store) { + bt_mesh_store_mod_bind(model); + } + + if (model->pub && model->pub->key == key_idx) { + _mod_pub_set(model, BLE_MESH_ADDR_UNASSIGNED, + 0, 0, 0, 0, 0, store); + } + } + + return STATUS_SUCCESS; +} + +struct bt_mesh_app_key *bt_mesh_app_key_alloc(u16_t app_idx) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.app_keys); i++) { + struct bt_mesh_app_key *key = &bt_mesh.app_keys[i]; + + if (key->net_idx == BLE_MESH_KEY_UNUSED) { + return key; + } + } + + return NULL; +} + +static u8_t app_key_set(u16_t net_idx, u16_t app_idx, const u8_t val[16], + bool update) +{ + struct bt_mesh_app_keys *keys; + struct bt_mesh_app_key *key; + struct bt_mesh_subnet *sub; + + BT_DBG("net_idx 0x%04x app_idx %04x update %u val %s", + net_idx, app_idx, update, bt_hex(val, 16)); + + sub = bt_mesh_subnet_get(net_idx); + if (!sub) { + return STATUS_INVALID_NETKEY; + } + + key = bt_mesh_app_key_find(app_idx); + if (update) { + if (!key) { + return STATUS_INVALID_APPKEY; + } + + if (key->net_idx != net_idx) { + return STATUS_INVALID_BINDING; + } + + keys = &key->keys[1]; + + /* The AppKey Update message shall generate an error when node + * is in normal operation, Phase 2, or Phase 3 or in Phase 1 + * when the AppKey Update message on a valid AppKeyIndex when + * the AppKey value is different. + */ + if (sub->kr_phase != BLE_MESH_KR_PHASE_1) { + return STATUS_CANNOT_UPDATE; + } + + if (key->updated) { + if (memcmp(keys->val, val, 16)) { + return STATUS_CANNOT_UPDATE; + } else { + return STATUS_SUCCESS; + } + } + + key->updated = true; + } else { + if (key) { + if (key->net_idx == net_idx && + !memcmp(key->keys[0].val, val, 16)) { + return STATUS_SUCCESS; + } + + if (key->net_idx == net_idx) { + return STATUS_IDX_ALREADY_STORED; + } else { + return STATUS_INVALID_NETKEY; + } + } + + key = bt_mesh_app_key_alloc(app_idx); + if (!key) { + return STATUS_INSUFF_RESOURCES; + } + + keys = &key->keys[0]; + } + + if (bt_mesh_app_id(val, &keys->id)) { + if (update) { + key->updated = false; + } + + return STATUS_STORAGE_FAIL; + } + + BT_DBG("app_idx 0x%04x AID 0x%02x", app_idx, keys->id); + + key->net_idx = net_idx; + key->app_idx = app_idx; + memcpy(keys->val, val, 16); + + if (IS_ENABLED(CONFIG_BLE_MESH_SETTINGS)) { + BT_DBG("Storing AppKey persistently"); + bt_mesh_store_app_key(key); + } + + return STATUS_SUCCESS; +} + +static void app_key_add(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 4 + 4); + u16_t key_net_idx, key_app_idx; + u8_t status; + + key_idx_unpack(buf, &key_net_idx, &key_app_idx); + + BT_DBG("AppIdx 0x%04x NetIdx 0x%04x", key_app_idx, key_net_idx); + + bt_mesh_model_msg_init(&msg, OP_APP_KEY_STATUS); + + status = app_key_set(key_net_idx, key_app_idx, buf->data, false); + BT_DBG("status 0x%02x", status); + net_buf_simple_add_u8(&msg, status); + + key_idx_pack(&msg, key_net_idx, key_app_idx); + + if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) { + BT_ERR("%s, Unable to send Config AppKey Status", __func__); + return; + } + +#if defined(CONFIG_BLE_MESH_FAST_PROV) + bt_mesh_callback_cfg_server_event_to_btc(0x0, model, ctx, + (u8_t *)&key_app_idx, sizeof(u16_t)); +#endif +} + +static void app_key_update(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 4 + 4); + u16_t key_net_idx, key_app_idx; + u8_t status; + + key_idx_unpack(buf, &key_net_idx, &key_app_idx); + + BT_DBG("AppIdx 0x%04x NetIdx 0x%04x", key_app_idx, key_net_idx); + + bt_mesh_model_msg_init(&msg, OP_APP_KEY_STATUS); + + status = app_key_set(key_net_idx, key_app_idx, buf->data, true); + BT_DBG("status 0x%02x", status); + net_buf_simple_add_u8(&msg, status); + + key_idx_pack(&msg, key_net_idx, key_app_idx); + + if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) { + BT_ERR("%s, Unable to send Config AppKey Status", __func__); + } +} + +struct unbind_data { + u16_t app_idx; + bool store; +}; + +static void _mod_unbind(struct bt_mesh_model *mod, struct bt_mesh_elem *elem, + bool vnd, bool primary, void *user_data) +{ + struct unbind_data *data = user_data; + + mod_unbind(mod, data->app_idx, data->store); +} + +void bt_mesh_app_key_del(struct bt_mesh_app_key *key, bool store) +{ + struct unbind_data data = { .app_idx = key->app_idx, .store = store }; + + BT_DBG("AppIdx 0x%03x store %u", key->app_idx, store); + + bt_mesh_model_foreach(_mod_unbind, &data); + + if (IS_ENABLED(CONFIG_BLE_MESH_SETTINGS) && store) { + bt_mesh_clear_app_key(key); + } + + key->net_idx = BLE_MESH_KEY_UNUSED; + (void)memset(key->keys, 0, sizeof(key->keys)); +} + +static void app_key_del(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 4 + 4); + u16_t key_net_idx, key_app_idx; + struct bt_mesh_app_key *key; + u8_t status; + + key_idx_unpack(buf, &key_net_idx, &key_app_idx); + + BT_DBG("AppIdx 0x%04x NetIdx 0x%04x", key_app_idx, key_net_idx); + + if (!bt_mesh_subnet_get(key_net_idx)) { + status = STATUS_INVALID_NETKEY; + goto send_status; + } + + key = bt_mesh_app_key_find(key_app_idx); + if (!key) { + /* Treat as success since the client might have missed a + * previous response and is resending the request. + */ + status = STATUS_SUCCESS; + goto send_status; + } + + if (key->net_idx != key_net_idx) { + status = STATUS_INVALID_BINDING; + goto send_status; + } + + bt_mesh_app_key_del(key, true); + status = STATUS_SUCCESS; + +send_status: + bt_mesh_model_msg_init(&msg, OP_APP_KEY_STATUS); + + net_buf_simple_add_u8(&msg, status); + + key_idx_pack(&msg, key_net_idx, key_app_idx); + + if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) { + BT_ERR("%s, Unable to send Config AppKey Status", __func__); + } +} + +/* Index list length: 3 bytes for every pair and 2 bytes for an odd idx */ +#define IDX_LEN(num) (((num) / 2) * 3 + ((num) % 2) * 2) + +static void app_key_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 3 + 4 + + IDX_LEN(CONFIG_BLE_MESH_APP_KEY_COUNT)); + u16_t get_idx, i, prev; + u8_t status; + + get_idx = net_buf_simple_pull_le16(buf); + if (get_idx > 0xfff) { + BT_ERR("%s, Invalid NetKeyIndex 0x%04x", __func__, get_idx); + return; + } + + BT_DBG("idx 0x%04x", get_idx); + + bt_mesh_model_msg_init(&msg, OP_APP_KEY_LIST); + + if (!bt_mesh_subnet_get(get_idx)) { + status = STATUS_INVALID_NETKEY; + } else { + status = STATUS_SUCCESS; + } + + net_buf_simple_add_u8(&msg, status); + net_buf_simple_add_le16(&msg, get_idx); + + if (status != STATUS_SUCCESS) { + goto send_status; + } + + prev = BLE_MESH_KEY_UNUSED; + for (i = 0U; i < ARRAY_SIZE(bt_mesh.app_keys); i++) { + struct bt_mesh_app_key *key = &bt_mesh.app_keys[i]; + + if (key->net_idx != get_idx) { + continue; + } + + if (prev == BLE_MESH_KEY_UNUSED) { + prev = key->app_idx; + continue; + } + + key_idx_pack(&msg, prev, key->app_idx); + prev = BLE_MESH_KEY_UNUSED; + } + + if (prev != BLE_MESH_KEY_UNUSED) { + net_buf_simple_add_le16(&msg, prev); + } + +send_status: + if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) { + BT_ERR("%s, Unable to send Config AppKey List", __func__); + } +} + +static void beacon_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + /* Needed size: opcode (2 bytes) + msg + MIC */ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 1 + 4); + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->len, + bt_hex(buf->data, buf->len)); + + bt_mesh_model_msg_init(&msg, OP_BEACON_STATUS); + net_buf_simple_add_u8(&msg, bt_mesh_beacon_get()); + + if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) { + BT_ERR("%s, Unable to send Config Beacon Status", __func__); + } +} + +static void beacon_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + /* Needed size: opcode (2 bytes) + msg + MIC */ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 1 + 4); + struct bt_mesh_cfg_srv *cfg = model->user_data; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->len, + bt_hex(buf->data, buf->len)); + + if (!cfg) { + BT_WARN("No Configuration Server context available"); + } else if (buf->data[0] == 0x00 || buf->data[0] == 0x01) { + if (buf->data[0] != cfg->beacon) { + cfg->beacon = buf->data[0]; + + if (IS_ENABLED(CONFIG_BLE_MESH_SETTINGS)) { + bt_mesh_store_cfg(); + } + +#if CONFIG_BLE_MESH_NODE + if (cfg->beacon) { + bt_mesh_beacon_enable(); + } else { + bt_mesh_beacon_disable(); + } +#endif + } + } else { + BT_WARN("Invalid Config Beacon value 0x%02x", buf->data[0]); + return; + } + + bt_mesh_model_msg_init(&msg, OP_BEACON_STATUS); + net_buf_simple_add_u8(&msg, bt_mesh_beacon_get()); + + if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) { + BT_ERR("%s, Unable to send Config Beacon Status", __func__); + } +} + +static void default_ttl_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + /* Needed size: opcode (2 bytes) + msg + MIC */ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 1 + 4); + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->len, + bt_hex(buf->data, buf->len)); + + bt_mesh_model_msg_init(&msg, OP_DEFAULT_TTL_STATUS); + net_buf_simple_add_u8(&msg, bt_mesh_default_ttl_get()); + + if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) { + BT_ERR("%s, Unable to send Config Default TTL Status", __func__); + } +} + +static void default_ttl_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + /* Needed size: opcode (2 bytes) + msg + MIC */ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 1 + 4); + struct bt_mesh_cfg_srv *cfg = model->user_data; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->len, + bt_hex(buf->data, buf->len)); + + if (!cfg) { + BT_WARN("No Configuration Server context available"); + } else if (buf->data[0] <= BLE_MESH_TTL_MAX && buf->data[0] != 0x01) { + if (cfg->default_ttl != buf->data[0]) { + cfg->default_ttl = buf->data[0]; + + if (IS_ENABLED(CONFIG_BLE_MESH_SETTINGS)) { + bt_mesh_store_cfg(); + } + } + } else { + BT_WARN("Prohibited Default TTL value 0x%02x", buf->data[0]); + return; + } + + bt_mesh_model_msg_init(&msg, OP_DEFAULT_TTL_STATUS); + net_buf_simple_add_u8(&msg, bt_mesh_default_ttl_get()); + + if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) { + BT_ERR("%s, Unable to send Config Default TTL Status", __func__); + } +} + +static void send_gatt_proxy_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx) +{ + /* Needed size: opcode (2 bytes) + msg + MIC */ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 1 + 4); + + bt_mesh_model_msg_init(&msg, OP_GATT_PROXY_STATUS); + net_buf_simple_add_u8(&msg, bt_mesh_gatt_proxy_get()); + + if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) { + BT_ERR("%s, Unable to send Config GATT Proxy Status", __func__); + } +} + +static void gatt_proxy_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->len, + bt_hex(buf->data, buf->len)); + + send_gatt_proxy_status(model, ctx); +} + +static void gatt_proxy_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_cfg_srv *cfg = model->user_data; + struct bt_mesh_subnet *sub; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->len, + bt_hex(buf->data, buf->len)); + + if (buf->data[0] != 0x00 && buf->data[0] != 0x01) { + BT_WARN("Invalid GATT Proxy value 0x%02x", buf->data[0]); + return; + } + + if (!IS_ENABLED(CONFIG_BLE_MESH_GATT_PROXY) || + bt_mesh_gatt_proxy_get() == BLE_MESH_GATT_PROXY_NOT_SUPPORTED) { + goto send_status; + } + + if (!cfg) { + BT_WARN("No Configuration Server context available"); + goto send_status; + } + + BT_DBG("GATT Proxy 0x%02x -> 0x%02x", cfg->gatt_proxy, buf->data[0]); + + if (cfg->gatt_proxy == buf->data[0]) { + goto send_status; + } + + cfg->gatt_proxy = buf->data[0]; + + if (IS_ENABLED(CONFIG_BLE_MESH_SETTINGS)) { + bt_mesh_store_cfg(); + } + +#if CONFIG_BLE_MESH_NODE + if (cfg->gatt_proxy == BLE_MESH_GATT_PROXY_DISABLED) { + int i; + + /* Section 4.2.11.1: "When the GATT Proxy state is set to + * 0x00, the Node Identity state for all subnets shall be set + * to 0x00 and shall not be changed." + */ + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + struct bt_mesh_subnet *sub = &bt_mesh.sub[i]; + + if (sub->net_idx != BLE_MESH_KEY_UNUSED) { + bt_mesh_proxy_identity_stop(sub); + } + } + + /* Section 4.2.11: "Upon transition from GATT Proxy state 0x01 + * to GATT Proxy state 0x00 the GATT Bearer Server shall + * disconnect all GATT Bearer Clients. + */ + bt_mesh_proxy_gatt_disconnect(); + } + + bt_mesh_adv_update(); +#endif + + sub = bt_mesh_subnet_get(cfg->hb_pub.net_idx); + if ((cfg->hb_pub.feat & BLE_MESH_FEAT_PROXY) && sub) { + hb_send(model); + } + +send_status: + send_gatt_proxy_status(model, ctx); +} + +static void net_transmit_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + /* Needed size: opcode (2 bytes) + msg + MIC */ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 1 + 4); + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->len, + bt_hex(buf->data, buf->len)); + + bt_mesh_model_msg_init(&msg, OP_NET_TRANSMIT_STATUS); + net_buf_simple_add_u8(&msg, bt_mesh_net_transmit_get()); + + if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) { + BT_ERR("%s, Unable to send Config Network Transmit Status", __func__); + } +} + +static void net_transmit_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + /* Needed size: opcode (2 bytes) + msg + MIC */ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 1 + 4); + struct bt_mesh_cfg_srv *cfg = model->user_data; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->len, + bt_hex(buf->data, buf->len)); + + BT_DBG("Transmit 0x%02x (count %u interval %ums)", buf->data[0], + BLE_MESH_TRANSMIT_COUNT(buf->data[0]), + BLE_MESH_TRANSMIT_INT(buf->data[0])); + + if (!cfg) { + BT_WARN("No Configuration Server context available"); + } else { + cfg->net_transmit = buf->data[0]; + + if (IS_ENABLED(CONFIG_BLE_MESH_SETTINGS)) { + bt_mesh_store_cfg(); + } + } + + bt_mesh_model_msg_init(&msg, OP_NET_TRANSMIT_STATUS); + net_buf_simple_add_u8(&msg, bt_mesh_net_transmit_get()); + + if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) { + BT_ERR("%s, Unable to send Config Network Transmit Status", __func__); + } +} + +static void relay_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + /* Needed size: opcode (2 bytes) + msg + MIC */ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 2 + 4); + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->len, + bt_hex(buf->data, buf->len)); + + bt_mesh_model_msg_init(&msg, OP_RELAY_STATUS); + net_buf_simple_add_u8(&msg, bt_mesh_relay_get()); + net_buf_simple_add_u8(&msg, bt_mesh_relay_retransmit_get()); + + if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) { + BT_ERR("%s, Unable to send Config Relay Status", __func__); + } +} + +static void relay_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + /* Needed size: opcode (2 bytes) + msg + MIC */ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 2 + 4); + struct bt_mesh_cfg_srv *cfg = model->user_data; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->len, + bt_hex(buf->data, buf->len)); + + if (!cfg) { + BT_WARN("No Configuration Server context available"); + } else if (buf->data[0] == 0x00 || buf->data[0] == 0x01) { + struct bt_mesh_subnet *sub; + bool change; + + if (cfg->relay == BLE_MESH_RELAY_NOT_SUPPORTED) { + change = false; + } else { + change = (cfg->relay != buf->data[0]); + cfg->relay = buf->data[0]; + cfg->relay_retransmit = buf->data[1]; + + if (IS_ENABLED(CONFIG_BLE_MESH_SETTINGS)) { + bt_mesh_store_cfg(); + } + } + + BT_DBG("Relay 0x%02x (%s) xmit 0x%02x (count %u interval %u)", + cfg->relay, change ? "changed" : "not changed", + cfg->relay_retransmit, + BLE_MESH_TRANSMIT_COUNT(cfg->relay_retransmit), + BLE_MESH_TRANSMIT_INT(cfg->relay_retransmit)); + + sub = bt_mesh_subnet_get(cfg->hb_pub.net_idx); + if ((cfg->hb_pub.feat & BLE_MESH_FEAT_RELAY) && sub && change) { + hb_send(model); + } + } else { + BT_WARN("Invalid Relay value 0x%02x", buf->data[0]); + return; + } + + bt_mesh_model_msg_init(&msg, OP_RELAY_STATUS); + net_buf_simple_add_u8(&msg, bt_mesh_relay_get()); + net_buf_simple_add_u8(&msg, bt_mesh_relay_retransmit_get()); + + if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) { + BT_ERR("%s, Unable to send Config Relay Status", __func__); + } +} + +static void send_mod_pub_status(struct bt_mesh_model *cfg_mod, + struct bt_mesh_msg_ctx *ctx, + u16_t elem_addr, u16_t pub_addr, + bool vnd, struct bt_mesh_model *mod, + u8_t status, u8_t *mod_id) +{ + /* Needed size: opcode (2 bytes) + msg + MIC */ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 14 + 4); + + bt_mesh_model_msg_init(&msg, OP_MOD_PUB_STATUS); + + net_buf_simple_add_u8(&msg, status); + net_buf_simple_add_le16(&msg, elem_addr); + + if (status != STATUS_SUCCESS) { + (void)memset(net_buf_simple_add(&msg, 7), 0, 7); + } else { + u16_t idx_cred; + + net_buf_simple_add_le16(&msg, pub_addr); + + idx_cred = mod->pub->key | (u16_t)mod->pub->cred << 12; + net_buf_simple_add_le16(&msg, idx_cred); + net_buf_simple_add_u8(&msg, mod->pub->ttl); + net_buf_simple_add_u8(&msg, mod->pub->period); + net_buf_simple_add_u8(&msg, mod->pub->retransmit); + } + + if (vnd) { + memcpy(net_buf_simple_add(&msg, 4), mod_id, 4); + } else { + memcpy(net_buf_simple_add(&msg, 2), mod_id, 2); + } + + if (bt_mesh_model_send(cfg_mod, ctx, &msg, NULL, NULL)) { + BT_ERR("%s, Unable to send Config Model Publication Status", __func__); + } +} + +static void mod_pub_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + u16_t elem_addr, pub_addr = 0U; + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u8_t *mod_id, status; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BLE_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_ERR("%s, Prohibited element address 0x%04x", __func__, elem_addr); + return; + } + + mod_id = buf->data; + + BT_DBG("elem_addr 0x%04x", elem_addr); + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->len == 4U); + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + status = STATUS_INVALID_MODEL; + goto send_status; + } + + if (!mod->pub) { + status = STATUS_NVAL_PUB_PARAM; + goto send_status; + } + + pub_addr = mod->pub->addr; + status = STATUS_SUCCESS; + +send_status: + send_mod_pub_status(model, ctx, elem_addr, pub_addr, vnd, mod, + status, mod_id); +} + +static void mod_pub_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + u8_t retransmit, status, pub_ttl, pub_period, cred_flag; + u16_t elem_addr, pub_addr, pub_app_idx; + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u8_t *mod_id; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BLE_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_ERR("%s, Prohibited element address 0x%04x", __func__, elem_addr); + return; + } + + pub_addr = net_buf_simple_pull_le16(buf); + pub_app_idx = net_buf_simple_pull_le16(buf); + cred_flag = ((pub_app_idx >> 12) & BIT_MASK(1)); + pub_app_idx &= BIT_MASK(12); + + pub_ttl = net_buf_simple_pull_u8(buf); + if (pub_ttl > BLE_MESH_TTL_MAX && pub_ttl != BLE_MESH_TTL_DEFAULT) { + BT_ERR("%s, Invalid TTL value 0x%02x", __func__, pub_ttl); + return; + } + + pub_period = net_buf_simple_pull_u8(buf); + retransmit = net_buf_simple_pull_u8(buf); + mod_id = buf->data; + + BT_DBG("elem_addr 0x%04x pub_addr 0x%04x cred_flag %u", + elem_addr, pub_addr, cred_flag); + BT_DBG("pub_app_idx 0x%03x, pub_ttl %u pub_period 0x%02x", + pub_app_idx, pub_ttl, pub_period); + BT_DBG("retransmit 0x%02x (count %u interval %ums)", retransmit, + BLE_MESH_PUB_TRANSMIT_COUNT(retransmit), + BLE_MESH_PUB_TRANSMIT_INT(retransmit)); + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->len == 4U); + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + status = STATUS_INVALID_MODEL; + goto send_status; + } + + status = _mod_pub_set(mod, pub_addr, pub_app_idx, cred_flag, pub_ttl, + pub_period, retransmit, true); + +send_status: + send_mod_pub_status(model, ctx, elem_addr, pub_addr, vnd, mod, + status, mod_id); +} + +#if CONFIG_BLE_MESH_LABEL_COUNT > 0 +static u8_t va_add(u8_t *label_uuid, u16_t *addr) +{ + struct label *free_slot = NULL; + int i; + + for (i = 0; i < ARRAY_SIZE(labels); i++) { + if (!labels[i].ref) { + free_slot = &labels[i]; + continue; + } + + if (!memcmp(labels[i].uuid, label_uuid, 16)) { + *addr = labels[i].addr; + labels[i].ref++; + return STATUS_SUCCESS; + } + } + + if (!free_slot) { + return STATUS_INSUFF_RESOURCES; + } + + if (bt_mesh_virtual_addr(label_uuid, addr) < 0) { + return STATUS_UNSPECIFIED; + } + + free_slot->ref = 1U; + free_slot->addr = *addr; + memcpy(free_slot->uuid, label_uuid, 16); + + return STATUS_SUCCESS; +} + +static u8_t va_del(u8_t *label_uuid, u16_t *addr) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(labels); i++) { + if (!memcmp(labels[i].uuid, label_uuid, 16)) { + if (addr) { + *addr = labels[i].addr; + } + + labels[i].ref--; + return STATUS_SUCCESS; + } + } + + if (addr) { + *addr = BLE_MESH_ADDR_UNASSIGNED; + } + + return STATUS_CANNOT_REMOVE; +} + +static size_t mod_sub_list_clear(struct bt_mesh_model *mod) +{ + u8_t *label_uuid; + size_t clear_count; + int i; + + /* Unref stored labels related to this model */ + for (i = 0, clear_count = 0; i < ARRAY_SIZE(mod->groups); i++) { + if (!BLE_MESH_ADDR_IS_VIRTUAL(mod->groups[i])) { + if (mod->groups[i] != BLE_MESH_ADDR_UNASSIGNED) { + mod->groups[i] = BLE_MESH_ADDR_UNASSIGNED; + clear_count++; + } + + continue; + } + + label_uuid = bt_mesh_label_uuid_get(mod->groups[i]); + + mod->groups[i] = BLE_MESH_ADDR_UNASSIGNED; + clear_count++; + + if (label_uuid) { + va_del(label_uuid, NULL); + } else { + BT_ERR("%s, Label UUID not found", __func__); + } + } + + return clear_count; +} + +static void mod_pub_va_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + u8_t retransmit, status, pub_ttl, pub_period, cred_flag; + u16_t elem_addr, pub_addr, pub_app_idx; + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u8_t *label_uuid; + u8_t *mod_id; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BLE_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_ERR("%s, Prohibited element address 0x%04x", __func__, elem_addr); + return; + } + + label_uuid = net_buf_simple_pull_mem(buf, 16); + pub_app_idx = net_buf_simple_pull_le16(buf); + cred_flag = ((pub_app_idx >> 12) & BIT_MASK(1)); + pub_app_idx &= BIT_MASK(12); + pub_ttl = net_buf_simple_pull_u8(buf); + if (pub_ttl > BLE_MESH_TTL_MAX && pub_ttl != BLE_MESH_TTL_DEFAULT) { + BT_ERR("%s, Invalid TTL value 0x%02x", __func__, pub_ttl); + return; + } + + pub_period = net_buf_simple_pull_u8(buf); + retransmit = net_buf_simple_pull_u8(buf); + mod_id = buf->data; + + BT_DBG("elem_addr 0x%04x cred_flag %u", elem_addr, cred_flag); + BT_DBG("pub_app_idx 0x%03x, pub_ttl %u pub_period 0x%02x", + pub_app_idx, pub_ttl, pub_period); + BT_DBG("retransmit 0x%02x (count %u interval %ums)", retransmit, + BLE_MESH_PUB_TRANSMIT_COUNT(retransmit), + BLE_MESH_PUB_TRANSMIT_INT(retransmit)); + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->len == 4U); + pub_addr = 0U; + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + pub_addr = 0U; + status = STATUS_INVALID_MODEL; + goto send_status; + } + + status = va_add(label_uuid, &pub_addr); + if (status == STATUS_SUCCESS) { + status = _mod_pub_set(mod, pub_addr, pub_app_idx, cred_flag, + pub_ttl, pub_period, retransmit, true); + } + +send_status: + send_mod_pub_status(model, ctx, elem_addr, pub_addr, vnd, mod, + status, mod_id); +} +#else +static size_t mod_sub_list_clear(struct bt_mesh_model *mod) +{ + size_t clear_count; + int i; + + /* Unref stored labels related to this model */ + for (i = 0, clear_count = 0; i < ARRAY_SIZE(mod->groups); i++) { + if (mod->groups[i] != BLE_MESH_ADDR_UNASSIGNED) { + mod->groups[i] = BLE_MESH_ADDR_UNASSIGNED; + clear_count++; + } + } + + return clear_count; +} + +static void mod_pub_va_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + u8_t *mod_id, status; + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u16_t elem_addr, pub_addr = 0U; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BLE_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_ERR("%s, Prohibited element address 0x%04x", __func__, elem_addr); + return; + } + + net_buf_simple_pull(buf, 16); + mod_id = net_buf_simple_pull(buf, 4); + + BT_DBG("elem_addr 0x%04x", elem_addr); + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->len == 4U); + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + status = STATUS_INVALID_MODEL; + goto send_status; + } + + if (!mod->pub) { + status = STATUS_NVAL_PUB_PARAM; + goto send_status; + } + + pub_addr = mod->pub->addr; + status = STATUS_INSUFF_RESOURCES; + +send_status: + send_mod_pub_status(model, ctx, elem_addr, pub_addr, vnd, mod, + status, mod_id); +} +#endif /* CONFIG_BLE_MESH_LABEL_COUNT > 0 */ + +static void send_mod_sub_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, u8_t status, + u16_t elem_addr, u16_t sub_addr, u8_t *mod_id, + bool vnd) +{ + /* Needed size: opcode (2 bytes) + msg + MIC */ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 9 + 4); + + BT_DBG("status 0x%02x elem_addr 0x%04x sub_addr 0x%04x", status, + elem_addr, sub_addr); + + bt_mesh_model_msg_init(&msg, OP_MOD_SUB_STATUS); + + net_buf_simple_add_u8(&msg, status); + net_buf_simple_add_le16(&msg, elem_addr); + net_buf_simple_add_le16(&msg, sub_addr); + + if (vnd) { + memcpy(net_buf_simple_add(&msg, 4), mod_id, 4); + } else { + memcpy(net_buf_simple_add(&msg, 2), mod_id, 2); + } + + if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) { + BT_ERR("%s, Unable to send Config Model Subscription Status", __func__); + } +} + +static void mod_sub_add(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + u16_t elem_addr, sub_addr; + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u8_t *mod_id; + u8_t status; + bool vnd; + int i; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BLE_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_ERR("%s, Prohibited element address 0x%04x", __func__, elem_addr); + return; + } + + sub_addr = net_buf_simple_pull_le16(buf); + + BT_DBG("elem_addr 0x%04x, sub_addr 0x%04x", elem_addr, sub_addr); + + mod_id = buf->data; + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->len == 4U); + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + status = STATUS_INVALID_MODEL; + goto send_status; + } + + if (!BLE_MESH_ADDR_IS_GROUP(sub_addr)) { + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + if (bt_mesh_model_find_group(mod, sub_addr)) { + /* Tried to add existing subscription */ + BT_DBG("found existing subscription"); + status = STATUS_SUCCESS; + goto send_status; + } + + for (i = 0; i < ARRAY_SIZE(mod->groups); i++) { + if (mod->groups[i] == BLE_MESH_ADDR_UNASSIGNED) { + mod->groups[i] = sub_addr; + break; + } + } + + if (i == ARRAY_SIZE(mod->groups)) { + status = STATUS_INSUFF_RESOURCES; + } else { + status = STATUS_SUCCESS; + + if (IS_ENABLED(CONFIG_BLE_MESH_SETTINGS)) { + bt_mesh_store_mod_sub(mod); + } + + if (IS_ENABLED(CONFIG_BLE_MESH_LOW_POWER)) { + bt_mesh_lpn_group_add(sub_addr); + } + } + +send_status: + send_mod_sub_status(model, ctx, status, elem_addr, sub_addr, + mod_id, vnd); +} + +static void mod_sub_del(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + u16_t elem_addr, sub_addr; + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u8_t *mod_id; + u16_t *match; + u8_t status; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BLE_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_ERR("%s, Prohibited element address 0x%04x", __func__, elem_addr); + return; + } + + sub_addr = net_buf_simple_pull_le16(buf); + + BT_DBG("elem_addr 0x%04x sub_addr 0x%04x", elem_addr, sub_addr); + + mod_id = buf->data; + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->len == 4U); + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + status = STATUS_INVALID_MODEL; + goto send_status; + } + + if (!BLE_MESH_ADDR_IS_GROUP(sub_addr)) { + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + /* An attempt to remove a non-existing address shall be treated + * as a success. + */ + status = STATUS_SUCCESS; + + if (IS_ENABLED(CONFIG_BLE_MESH_LOW_POWER)) { + bt_mesh_lpn_group_del(&sub_addr, 1); + } + + match = bt_mesh_model_find_group(mod, sub_addr); + if (match) { + *match = BLE_MESH_ADDR_UNASSIGNED; + + if (IS_ENABLED(CONFIG_BLE_MESH_SETTINGS)) { + bt_mesh_store_mod_sub(mod); + } + } + +send_status: + send_mod_sub_status(model, ctx, status, elem_addr, sub_addr, + mod_id, vnd); +} + +static void mod_sub_overwrite(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + u16_t elem_addr, sub_addr; + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u8_t *mod_id; + u8_t status; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BLE_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_ERR("%s, Prohibited element address 0x%04x", __func__, elem_addr); + return; + } + + sub_addr = net_buf_simple_pull_le16(buf); + + BT_DBG("elem_addr 0x%04x sub_addr 0x%04x", elem_addr, sub_addr); + + mod_id = buf->data; + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->len == 4U); + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + status = STATUS_INVALID_MODEL; + goto send_status; + } + + if (!BLE_MESH_ADDR_IS_GROUP(sub_addr)) { + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + if (IS_ENABLED(CONFIG_BLE_MESH_LOW_POWER)) { + bt_mesh_lpn_group_del(mod->groups, ARRAY_SIZE(mod->groups)); + } + + mod_sub_list_clear(mod); + + if (ARRAY_SIZE(mod->groups) > 0) { + mod->groups[0] = sub_addr; + status = STATUS_SUCCESS; + + if (IS_ENABLED(CONFIG_BLE_MESH_SETTINGS)) { + bt_mesh_store_mod_sub(mod); + } + + if (IS_ENABLED(CONFIG_BLE_MESH_LOW_POWER)) { + bt_mesh_lpn_group_add(sub_addr); + } + } else { + status = STATUS_INSUFF_RESOURCES; + } + + +send_status: + send_mod_sub_status(model, ctx, status, elem_addr, sub_addr, + mod_id, vnd); +} + +static void mod_sub_del_all(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u16_t elem_addr; + u8_t *mod_id; + u8_t status; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BLE_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_ERR("%s, Prohibited element address 0x%04x", __func__, elem_addr); + return; + } + + BT_DBG("elem_addr 0x%04x", elem_addr); + + mod_id = buf->data; + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->len == 4U); + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + status = STATUS_INVALID_MODEL; + goto send_status; + } + + if (IS_ENABLED(CONFIG_BLE_MESH_LOW_POWER)) { + bt_mesh_lpn_group_del(mod->groups, ARRAY_SIZE(mod->groups)); + } + + mod_sub_list_clear(mod); + + if (IS_ENABLED(CONFIG_BLE_MESH_SETTINGS)) { + bt_mesh_store_mod_sub(mod); + } + + status = STATUS_SUCCESS; + +send_status: + send_mod_sub_status(model, ctx, status, elem_addr, + BLE_MESH_ADDR_UNASSIGNED, mod_id, vnd); +} + +static void mod_sub_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 5 + 4 + + CONFIG_BLE_MESH_MODEL_GROUP_COUNT * 2); + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u16_t addr, id; + int i; + + addr = net_buf_simple_pull_le16(buf); + if (!BLE_MESH_ADDR_IS_UNICAST(addr)) { + BT_ERR("%s, Prohibited element address 0x%04x", __func__, addr); + return; + } + + id = net_buf_simple_pull_le16(buf); + + BT_DBG("addr 0x%04x id 0x%04x", addr, id); + + bt_mesh_model_msg_init(&msg, OP_MOD_SUB_LIST); + + elem = bt_mesh_elem_find(addr); + if (!elem) { + net_buf_simple_add_u8(&msg, STATUS_INVALID_ADDRESS); + net_buf_simple_add_le16(&msg, addr); + net_buf_simple_add_le16(&msg, id); + goto send_list; + } + + mod = bt_mesh_model_find(elem, id); + if (!mod) { + net_buf_simple_add_u8(&msg, STATUS_INVALID_MODEL); + net_buf_simple_add_le16(&msg, addr); + net_buf_simple_add_le16(&msg, id); + goto send_list; + } + + net_buf_simple_add_u8(&msg, STATUS_SUCCESS); + + net_buf_simple_add_le16(&msg, addr); + net_buf_simple_add_le16(&msg, id); + + for (i = 0; i < ARRAY_SIZE(mod->groups); i++) { + if (mod->groups[i] != BLE_MESH_ADDR_UNASSIGNED) { + net_buf_simple_add_le16(&msg, mod->groups[i]); + } + } + +send_list: + if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) { + BT_ERR("%s, Unable to send Config Model Subscription List", __func__); + } +} + +static void mod_sub_get_vnd(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 7 + 4 + + CONFIG_BLE_MESH_MODEL_GROUP_COUNT * 2); + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u16_t company, addr, id; + int i; + + addr = net_buf_simple_pull_le16(buf); + if (!BLE_MESH_ADDR_IS_UNICAST(addr)) { + BT_ERR("%s, Prohibited element address 0x%04x", __func__, addr); + return; + } + + company = net_buf_simple_pull_le16(buf); + id = net_buf_simple_pull_le16(buf); + + BT_DBG("addr 0x%04x company 0x%04x id 0x%04x", addr, company, id); + + bt_mesh_model_msg_init(&msg, OP_MOD_SUB_LIST_VND); + + elem = bt_mesh_elem_find(addr); + if (!elem) { + net_buf_simple_add_u8(&msg, STATUS_INVALID_ADDRESS); + net_buf_simple_add_le16(&msg, addr); + net_buf_simple_add_le16(&msg, company); + net_buf_simple_add_le16(&msg, id); + goto send_list; + } + + mod = bt_mesh_model_find_vnd(elem, company, id); + if (!mod) { + net_buf_simple_add_u8(&msg, STATUS_INVALID_MODEL); + net_buf_simple_add_le16(&msg, addr); + net_buf_simple_add_le16(&msg, company); + net_buf_simple_add_le16(&msg, id); + goto send_list; + } + + net_buf_simple_add_u8(&msg, STATUS_SUCCESS); + + net_buf_simple_add_le16(&msg, addr); + net_buf_simple_add_le16(&msg, company); + net_buf_simple_add_le16(&msg, id); + + for (i = 0; i < ARRAY_SIZE(mod->groups); i++) { + if (mod->groups[i] != BLE_MESH_ADDR_UNASSIGNED) { + net_buf_simple_add_le16(&msg, mod->groups[i]); + } + } + +send_list: + if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) { + BT_ERR("%s, Unable to send Config Vendor Model Subscription List", __func__); + } +} + +#if CONFIG_BLE_MESH_LABEL_COUNT > 0 +static void mod_sub_va_add(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + u16_t elem_addr, sub_addr; + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u8_t *label_uuid; + u8_t *mod_id; + u8_t status; + bool vnd; + int i; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BLE_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_ERR("%s, Prohibited element address 0x%04x", __func__, elem_addr); + return; + } + + label_uuid = net_buf_simple_pull_mem(buf, 16); + + BT_DBG("elem_addr 0x%04x", elem_addr); + + mod_id = buf->data; + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->len == 4U); + sub_addr = BLE_MESH_ADDR_UNASSIGNED; + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + sub_addr = BLE_MESH_ADDR_UNASSIGNED; + status = STATUS_INVALID_MODEL; + goto send_status; + } + + status = va_add(label_uuid, &sub_addr); + if (status != STATUS_SUCCESS) { + goto send_status; + } + + if (bt_mesh_model_find_group(mod, sub_addr)) { + /* Tried to add existing subscription */ + status = STATUS_SUCCESS; + goto send_status; + } + + for (i = 0; i < ARRAY_SIZE(mod->groups); i++) { + if (mod->groups[i] == BLE_MESH_ADDR_UNASSIGNED) { + mod->groups[i] = sub_addr; + break; + } + } + + if (i == ARRAY_SIZE(mod->groups)) { + status = STATUS_INSUFF_RESOURCES; + } else { + if (IS_ENABLED(CONFIG_BLE_MESH_LOW_POWER)) { + bt_mesh_lpn_group_add(sub_addr); + } + + if (IS_ENABLED(CONFIG_BLE_MESH_SETTINGS)) { + bt_mesh_store_mod_sub(mod); + } + + status = STATUS_SUCCESS; + } + +send_status: + send_mod_sub_status(model, ctx, status, elem_addr, sub_addr, + mod_id, vnd); +} + +static void mod_sub_va_del(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + u16_t elem_addr, sub_addr; + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u8_t *label_uuid; + u8_t *mod_id; + u16_t *match; + u8_t status; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BLE_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_ERR("%s, Prohibited element address 0x%04x", __func__, elem_addr); + return; + } + + label_uuid = net_buf_simple_pull_mem(buf, 16); + + BT_DBG("elem_addr 0x%04x", elem_addr); + + mod_id = buf->data; + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->len == 4U); + sub_addr = BLE_MESH_ADDR_UNASSIGNED; + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + sub_addr = BLE_MESH_ADDR_UNASSIGNED; + status = STATUS_INVALID_MODEL; + goto send_status; + } + + status = va_del(label_uuid, &sub_addr); + if (sub_addr == BLE_MESH_ADDR_UNASSIGNED) { + goto send_status; + } + + if (IS_ENABLED(CONFIG_BLE_MESH_LOW_POWER)) { + bt_mesh_lpn_group_del(&sub_addr, 1); + } + + match = bt_mesh_model_find_group(mod, sub_addr); + if (match) { + *match = BLE_MESH_ADDR_UNASSIGNED; + + if (IS_ENABLED(CONFIG_BLE_MESH_SETTINGS)) { + bt_mesh_store_mod_sub(mod); + } + + status = STATUS_SUCCESS; + } else { + status = STATUS_CANNOT_REMOVE; + } + +send_status: + send_mod_sub_status(model, ctx, status, elem_addr, sub_addr, + mod_id, vnd); +} + +static void mod_sub_va_overwrite(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + u16_t elem_addr, sub_addr = BLE_MESH_ADDR_UNASSIGNED; + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u8_t *label_uuid; + u8_t *mod_id; + u8_t status; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BLE_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_ERR("%s, Prohibited element address 0x%04x", __func__, elem_addr); + return; + } + + label_uuid = net_buf_simple_pull_mem(buf, 16); + + BT_DBG("elem_addr 0x%04x", elem_addr); + + mod_id = buf->data; + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->len == 4U); + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + status = STATUS_INVALID_MODEL; + goto send_status; + } + + if (IS_ENABLED(CONFIG_BLE_MESH_LOW_POWER)) { + bt_mesh_lpn_group_del(mod->groups, ARRAY_SIZE(mod->groups)); + } + + mod_sub_list_clear(mod); + + if (ARRAY_SIZE(mod->groups) > 0) { + status = va_add(label_uuid, &sub_addr); + if (status == STATUS_SUCCESS) { + mod->groups[0] = sub_addr; + + if (IS_ENABLED(CONFIG_BLE_MESH_SETTINGS)) { + bt_mesh_store_mod_sub(mod); + } + + if (IS_ENABLED(CONFIG_BLE_MESH_LOW_POWER)) { + bt_mesh_lpn_group_add(sub_addr); + } + } + } else { + status = STATUS_INSUFF_RESOURCES; + } + +send_status: + send_mod_sub_status(model, ctx, status, elem_addr, sub_addr, + mod_id, vnd); +} +#else +static void mod_sub_va_add(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u16_t elem_addr; + u8_t *mod_id; + u8_t status; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BLE_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_ERR("%s, Prohibited element address 0x%04x", __func__, elem_addr); + return; + } + + net_buf_simple_pull(buf, 16); + + mod_id = buf->data; + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->len == 4U); + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + status = STATUS_INVALID_MODEL; + goto send_status; + } + + status = STATUS_INSUFF_RESOURCES; + +send_status: + send_mod_sub_status(model, ctx, status, elem_addr, + BLE_MESH_ADDR_UNASSIGNED, mod_id, vnd); +} + +static void mod_sub_va_del(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_elem *elem; + u16_t elem_addr; + u8_t *mod_id; + u8_t status; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BLE_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_ERR("%s, Prohibited element address 0x%04x", __func__, elem_addr); + return; + } + + net_buf_simple_pull(buf, 16); + + mod_id = buf->data; + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + vnd = (buf->len == 4U); + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + if (!get_model(elem, buf, &vnd)) { + status = STATUS_INVALID_MODEL; + goto send_status; + } + + status = STATUS_INSUFF_RESOURCES; + +send_status: + send_mod_sub_status(model, ctx, status, elem_addr, + BLE_MESH_ADDR_UNASSIGNED, mod_id, vnd); +} + +static void mod_sub_va_overwrite(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_elem *elem; + u16_t elem_addr; + u8_t *mod_id; + u8_t status; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BLE_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_ERR("%s, Prohibited element address 0x%04x", __func__, elem_addr); + return; + } + + net_buf_simple_pull(buf, 18); + + mod_id = buf->data; + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + vnd = (buf->len == 4U); + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + if (!get_model(elem, buf, &vnd)) { + status = STATUS_INVALID_MODEL; + goto send_status; + } + + status = STATUS_INSUFF_RESOURCES; + +send_status: + send_mod_sub_status(model, ctx, status, elem_addr, + BLE_MESH_ADDR_UNASSIGNED, mod_id, vnd); +} +#endif /* CONFIG_BLE_MESH_LABEL_COUNT > 0 */ + +static void send_net_key_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + u16_t idx, u8_t status) +{ + /* Needed size: opcode (2 bytes) + msg + MIC */ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 3 + 4); + + bt_mesh_model_msg_init(&msg, OP_NET_KEY_STATUS); + + net_buf_simple_add_u8(&msg, status); + net_buf_simple_add_le16(&msg, idx); + + if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) { + BT_ERR("%s, Unable to send Config NetKey Status", __func__); + } +} + +static void net_key_add(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_subnet *sub; + u16_t idx; + int err; + + idx = net_buf_simple_pull_le16(buf); + if (idx > 0xfff) { + BT_ERR("%s, Invalid NetKeyIndex 0x%04x", __func__, idx); + return; + } + + BT_DBG("idx 0x%04x", idx); + + sub = bt_mesh_subnet_get(idx); + if (!sub) { + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + if (bt_mesh.sub[i].net_idx == BLE_MESH_KEY_UNUSED) { + sub = &bt_mesh.sub[i]; + break; + } + } + + if (!sub) { + send_net_key_status(model, ctx, idx, + STATUS_INSUFF_RESOURCES); + return; + } + } + + /* Check for already existing subnet */ + if (sub->net_idx == idx) { + u8_t status; + + if (memcmp(buf->data, sub->keys[0].net, 16)) { + status = STATUS_IDX_ALREADY_STORED; + } else { + status = STATUS_SUCCESS; + } + + send_net_key_status(model, ctx, idx, status); + return; + } + + err = bt_mesh_net_keys_create(&sub->keys[0], buf->data); + if (err) { + send_net_key_status(model, ctx, idx, STATUS_UNSPECIFIED); + return; + } + + sub->net_idx = idx; + + if (IS_ENABLED(CONFIG_BLE_MESH_SETTINGS)) { + BT_DBG("Storing NetKey persistently"); + bt_mesh_store_subnet(sub); + } + + /* Make sure we have valid beacon data to be sent */ + bt_mesh_net_beacon_update(sub); + + if (IS_ENABLED(CONFIG_BLE_MESH_GATT_PROXY)) { + sub->node_id = BLE_MESH_NODE_IDENTITY_STOPPED; +#if CONFIG_BLE_MESH_NODE + bt_mesh_proxy_beacon_send(sub); + bt_mesh_adv_update(); +#endif + } else { + sub->node_id = BLE_MESH_NODE_IDENTITY_NOT_SUPPORTED; + } + + send_net_key_status(model, ctx, idx, STATUS_SUCCESS); +} + +static void net_key_update(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_subnet *sub; + u16_t idx; + int err; + + idx = net_buf_simple_pull_le16(buf); + if (idx > 0xfff) { + BT_ERR("%s, Invalid NetKeyIndex 0x%04x", __func__, idx); + return; + } + + BT_DBG("idx 0x%04x", idx); + + sub = bt_mesh_subnet_get(idx); + if (!sub) { + send_net_key_status(model, ctx, idx, STATUS_INVALID_NETKEY); + return; + } + + /* The node shall successfully process a NetKey Update message on a + * valid NetKeyIndex when the NetKey value is different and the Key + * Refresh procedure has not been started, or when the NetKey value is + * the same in Phase 1. The NetKey Update message shall generate an + * error when the node is in Phase 2, or Phase 3. + */ + switch (sub->kr_phase) { + case BLE_MESH_KR_NORMAL: + if (!memcmp(buf->data, sub->keys[0].net, 16)) { + return; + } + break; + case BLE_MESH_KR_PHASE_1: + if (!memcmp(buf->data, sub->keys[1].net, 16)) { + send_net_key_status(model, ctx, idx, STATUS_SUCCESS); + return; + } + /* fall through */ + case BLE_MESH_KR_PHASE_2: + case BLE_MESH_KR_PHASE_3: + send_net_key_status(model, ctx, idx, STATUS_CANNOT_UPDATE); + return; + } + + err = bt_mesh_net_keys_create(&sub->keys[1], buf->data); + if (!err && (IS_ENABLED(CONFIG_BLE_MESH_LOW_POWER) || + IS_ENABLED(CONFIG_BLE_MESH_FRIEND))) { + err = friend_cred_update(sub); + } + + if (err) { + send_net_key_status(model, ctx, idx, STATUS_UNSPECIFIED); + return; + } + + sub->kr_phase = BLE_MESH_KR_PHASE_1; + + if (IS_ENABLED(CONFIG_BLE_MESH_SETTINGS)) { + BT_DBG("Storing NetKey persistently"); + bt_mesh_store_subnet(sub); + } + + bt_mesh_net_beacon_update(sub); + + send_net_key_status(model, ctx, idx, STATUS_SUCCESS); +} + +static void hb_pub_disable(struct bt_mesh_cfg_srv *cfg) +{ + BT_DBG("%s", __func__); + + cfg->hb_pub.dst = BLE_MESH_ADDR_UNASSIGNED; + cfg->hb_pub.count = 0U; + cfg->hb_pub.ttl = 0U; + cfg->hb_pub.period = 0U; + + k_delayed_work_cancel(&cfg->hb_pub.timer); +} + +static void net_key_del(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_subnet *sub; + u16_t del_idx; + u8_t status; + + del_idx = net_buf_simple_pull_le16(buf); + if (del_idx > 0xfff) { + BT_ERR("%s, Invalid NetKeyIndex 0x%04x", __func__, del_idx); + return; + } + + BT_DBG("idx 0x%04x", del_idx); + + sub = bt_mesh_subnet_get(del_idx); + if (!sub) { + /* This could be a retry of a previous attempt that had its + * response lost, so pretend that it was a success. + */ + status = STATUS_SUCCESS; + goto send_status; + } + + /* The key that the message was encrypted with cannot be removed. + * The NetKey List must contain a minimum of one NetKey. + */ + if (ctx->net_idx == del_idx) { + status = STATUS_CANNOT_REMOVE; + goto send_status; + } + + bt_mesh_subnet_del(sub, true); + status = STATUS_SUCCESS; + +send_status: + send_net_key_status(model, ctx, del_idx, status); +} + +static void net_key_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + NET_BUF_SIMPLE_DEFINE(msg, + 2 + 4 + IDX_LEN(CONFIG_BLE_MESH_SUBNET_COUNT)); + u16_t prev, i; + + bt_mesh_model_msg_init(&msg, OP_NET_KEY_LIST); + + prev = BLE_MESH_KEY_UNUSED; + for (i = 0U; i < ARRAY_SIZE(bt_mesh.sub); i++) { + struct bt_mesh_subnet *sub = &bt_mesh.sub[i]; + + if (sub->net_idx == BLE_MESH_KEY_UNUSED) { + continue; + } + + if (prev == BLE_MESH_KEY_UNUSED) { + prev = sub->net_idx; + continue; + } + + key_idx_pack(&msg, prev, sub->net_idx); + prev = BLE_MESH_KEY_UNUSED; + } + + if (prev != BLE_MESH_KEY_UNUSED) { + net_buf_simple_add_le16(&msg, prev); + } + + if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) { + BT_ERR("%s, Unable to send Config NetKey List", __func__); + } +} + +static void node_identity_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + /* Needed size: opcode (2 bytes) + msg + MIC */ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 4 + 4); + struct bt_mesh_subnet *sub; + u8_t node_id; + u16_t idx; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->len, + bt_hex(buf->data, buf->len)); + + idx = net_buf_simple_pull_le16(buf); + if (idx > 0xfff) { + BT_ERR("%s, Invalid NetKeyIndex 0x%04x", __func__, idx); + return; + } + + bt_mesh_model_msg_init(&msg, OP_NODE_IDENTITY_STATUS); + + sub = bt_mesh_subnet_get(idx); + if (!sub) { + net_buf_simple_add_u8(&msg, STATUS_INVALID_NETKEY); + node_id = 0x00; + } else { + net_buf_simple_add_u8(&msg, STATUS_SUCCESS); + node_id = sub->node_id; + } + + net_buf_simple_add_le16(&msg, idx); + net_buf_simple_add_u8(&msg, node_id); + + if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) { + BT_ERR("%s, Unable to send Config Node Identity Status", __func__); + } +} + +static void node_identity_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + /* Needed size: opcode (2 bytes) + msg + MIC */ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 4 + 4); + struct bt_mesh_subnet *sub; + u8_t node_id; + u16_t idx; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->len, + bt_hex(buf->data, buf->len)); + + idx = net_buf_simple_pull_le16(buf); + if (idx > 0xfff) { + BT_WARN("%s, Invalid NetKeyIndex 0x%04x", __func__, idx); + return; + } + + node_id = net_buf_simple_pull_u8(buf); + if (node_id != 0x00 && node_id != 0x01) { + BT_WARN("%s, Invalid Node ID value 0x%02x", __func__, node_id); + return; + } + + bt_mesh_model_msg_init(&msg, OP_NODE_IDENTITY_STATUS); + + sub = bt_mesh_subnet_get(idx); + if (!sub) { + net_buf_simple_add_u8(&msg, STATUS_INVALID_NETKEY); + net_buf_simple_add_le16(&msg, idx); + net_buf_simple_add_u8(&msg, node_id); + } else { + net_buf_simple_add_u8(&msg, STATUS_SUCCESS); + net_buf_simple_add_le16(&msg, idx); +#if CONFIG_BLE_MESH_NODE + /* Section 4.2.11.1: "When the GATT Proxy state is set to + * 0x00, the Node Identity state for all subnets shall be set + * to 0x00 and shall not be changed." + */ + if (IS_ENABLED(CONFIG_BLE_MESH_GATT_PROXY) && + bt_mesh_gatt_proxy_get() == BLE_MESH_GATT_PROXY_ENABLED) { + if (node_id) { + bt_mesh_proxy_identity_start(sub); + } else { + bt_mesh_proxy_identity_stop(sub); + } + bt_mesh_adv_update(); + } +#endif + net_buf_simple_add_u8(&msg, sub->node_id); + } + + if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) { + BT_ERR("%s, Unable to send Config Node Identity Status", __func__); + } +} + +static void create_mod_app_status(struct net_buf_simple *msg, + struct bt_mesh_model *mod, bool vnd, + u16_t elem_addr, u16_t app_idx, + u8_t status, u8_t *mod_id) +{ + bt_mesh_model_msg_init(msg, OP_MOD_APP_STATUS); + + net_buf_simple_add_u8(msg, status); + net_buf_simple_add_le16(msg, elem_addr); + net_buf_simple_add_le16(msg, app_idx); + + if (vnd) { + memcpy(net_buf_simple_add(msg, 4), mod_id, 4); + } else { + memcpy(net_buf_simple_add(msg, 2), mod_id, 2); + } +} + +static void mod_app_bind(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 9 + 4); + u16_t elem_addr, key_app_idx; + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u8_t *mod_id, status; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BLE_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_ERR("%s, Prohibited element address 0x%04x", __func__, elem_addr); + return; + } + + key_app_idx = net_buf_simple_pull_le16(buf); + mod_id = buf->data; + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->len == 4U); + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + status = STATUS_INVALID_MODEL; + goto send_status; + } + + /* Configuration Server only allows device key based access */ + if (model == mod) { + BT_ERR("%s, Client tried to bind AppKey to Configuration Model", __func__); + status = STATUS_CANNOT_BIND; + goto send_status; + } + + status = mod_bind(mod, key_app_idx); + +send_status: + BT_DBG("status 0x%02x", status); + create_mod_app_status(&msg, mod, vnd, elem_addr, key_app_idx, status, + mod_id); + + if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) { + BT_ERR("%s, Unable to send Config Model App Bind Status", __func__); + } +} + +static void mod_app_unbind(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 9 + 4); + u16_t elem_addr, key_app_idx; + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u8_t *mod_id, status; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BLE_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_ERR("%s, Prohibited element address 0x%04x", __func__, elem_addr); + return; + } + + key_app_idx = net_buf_simple_pull_le16(buf); + mod_id = buf->data; + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->len == 4U); + status = STATUS_INVALID_ADDRESS; + goto send_status; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + status = STATUS_INVALID_MODEL; + goto send_status; + } + + status = mod_unbind(mod, key_app_idx, true); + +send_status: + BT_DBG("status 0x%02x", status); + create_mod_app_status(&msg, mod, vnd, elem_addr, key_app_idx, status, + mod_id); + + if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) { + BT_ERR("%s, Unable to send Config Model App Unbind Status", __func__); + } +} + +#define KEY_LIST_LEN (CONFIG_BLE_MESH_MODEL_KEY_COUNT * 2) + +static void mod_app_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 9 + KEY_LIST_LEN + 4); + struct bt_mesh_model *mod; + struct bt_mesh_elem *elem; + u8_t *mod_id, status; + u16_t elem_addr; + bool vnd; + + elem_addr = net_buf_simple_pull_le16(buf); + if (!BLE_MESH_ADDR_IS_UNICAST(elem_addr)) { + BT_ERR("%s, Prohibited element address 0x%04x", __func__, elem_addr); + return; + } + + mod_id = buf->data; + + BT_DBG("elem_addr 0x%04x", elem_addr); + + elem = bt_mesh_elem_find(elem_addr); + if (!elem) { + mod = NULL; + vnd = (buf->len == 4U); + status = STATUS_INVALID_ADDRESS; + goto send_list; + } + + mod = get_model(elem, buf, &vnd); + if (!mod) { + status = STATUS_INVALID_MODEL; + goto send_list; + } + + status = STATUS_SUCCESS; + +send_list: + if (vnd) { + bt_mesh_model_msg_init(&msg, OP_VND_MOD_APP_LIST); + } else { + bt_mesh_model_msg_init(&msg, OP_SIG_MOD_APP_LIST); + } + + net_buf_simple_add_u8(&msg, status); + net_buf_simple_add_le16(&msg, elem_addr); + + if (vnd) { + net_buf_simple_add_mem(&msg, mod_id, 4); + } else { + net_buf_simple_add_mem(&msg, mod_id, 2); + } + + if (mod) { + int i; + + for (i = 0; i < ARRAY_SIZE(mod->keys); i++) { + if (mod->keys[i] != BLE_MESH_KEY_UNUSED) { + net_buf_simple_add_le16(&msg, mod->keys[i]); + } + } + } + + if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) { + BT_ERR("%s, Unable to send Config Model Application List", __func__); + } +} + +static void node_reset(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + /* Needed size: opcode (2 bytes) + msg + MIC */ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 0 + 4); + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->len, + bt_hex(buf->data, buf->len)); + + + bt_mesh_model_msg_init(&msg, OP_NODE_RESET_STATUS); + + /* Send the response first since we wont have any keys left to + * send it later. + */ + if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) { + BT_ERR("%s, Unable to send Config Node Reset Status", __func__); + } + +#if CONFIG_BLE_MESH_NODE + bt_mesh_reset(); +#endif +} + +static void send_friend_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx) +{ + /* Needed size: opcode (2 bytes) + msg + MIC */ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 1 + 4); + struct bt_mesh_cfg_srv *cfg = model->user_data; + + bt_mesh_model_msg_init(&msg, OP_FRIEND_STATUS); + net_buf_simple_add_u8(&msg, cfg->frnd); + + if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) { + BT_ERR("%s, Unable to send Config Friend Status", __func__); + } +} + +static void friend_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->len, + bt_hex(buf->data, buf->len)); + + send_friend_status(model, ctx); +} + +static void friend_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_cfg_srv *cfg = model->user_data; + struct bt_mesh_subnet *sub; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->len, + bt_hex(buf->data, buf->len)); + + if (buf->data[0] != 0x00 && buf->data[0] != 0x01) { + BT_WARN("Invalid Friend value 0x%02x", buf->data[0]); + return; + } + + if (!cfg) { + BT_WARN("No Configuration Server context available"); + goto send_status; + } + + BT_DBG("Friend 0x%02x -> 0x%02x", cfg->frnd, buf->data[0]); + + if (cfg->frnd == buf->data[0]) { + goto send_status; + } + + if (IS_ENABLED(CONFIG_BLE_MESH_FRIEND)) { + cfg->frnd = buf->data[0]; + + if (IS_ENABLED(CONFIG_BLE_MESH_SETTINGS)) { + bt_mesh_store_cfg(); + } + + if (cfg->frnd == BLE_MESH_FRIEND_DISABLED) { + bt_mesh_friend_clear_net_idx(BLE_MESH_KEY_ANY); + } + } + + sub = bt_mesh_subnet_get(cfg->hb_pub.net_idx); + if ((cfg->hb_pub.feat & BLE_MESH_FEAT_FRIEND) && sub) { + hb_send(model); + } + +send_status: + send_friend_status(model, ctx); +} + +static void lpn_timeout_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + /* Needed size: opcode (2 bytes) + msg + MIC */ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 5 + 4); + struct bt_mesh_friend *frnd; + u16_t lpn_addr; + s32_t timeout; + + lpn_addr = net_buf_simple_pull_le16(buf); + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x lpn_addr 0x%02x", + ctx->net_idx, ctx->app_idx, ctx->addr, lpn_addr); + + if (!BLE_MESH_ADDR_IS_UNICAST(lpn_addr)) { + BT_WARN("Invalid LPNAddress; ignoring msg"); + return; + } + + bt_mesh_model_msg_init(&msg, OP_LPN_TIMEOUT_STATUS); + net_buf_simple_add_le16(&msg, lpn_addr); + + if (!IS_ENABLED(CONFIG_BLE_MESH_FRIEND)) { + timeout = 0; + goto send_rsp; + } + + frnd = bt_mesh_friend_find(BLE_MESH_KEY_ANY, lpn_addr, true, true); + if (!frnd) { + timeout = 0; + goto send_rsp; + } + + timeout = k_delayed_work_remaining_get(&frnd->timer) / 100; + +send_rsp: + net_buf_simple_add_u8(&msg, timeout); + net_buf_simple_add_u8(&msg, timeout >> 8); + net_buf_simple_add_u8(&msg, timeout >> 16); + + if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) { + BT_ERR("%s, Unable to send Config LPN PollTimeout Status", __func__); + } +} + +static void send_krp_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + u16_t idx, u8_t phase, u8_t status) +{ + /* Needed size: opcode (2 bytes) + msg + MIC */ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 4 + 4); + + bt_mesh_model_msg_init(&msg, OP_KRP_STATUS); + + net_buf_simple_add_u8(&msg, status); + net_buf_simple_add_le16(&msg, idx); + net_buf_simple_add_u8(&msg, phase); + + if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) { + BT_ERR("%s, Unable to send Config Key Refresh Phase Status", __func__); + } +} + +static void krp_get(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_subnet *sub; + u16_t idx; + + idx = net_buf_simple_pull_le16(buf); + if (idx > 0xfff) { + BT_ERR("%s, Invalid NetKeyIndex 0x%04x", __func__, idx); + return; + } + + BT_DBG("idx 0x%04x", idx); + + sub = bt_mesh_subnet_get(idx); + if (!sub) { + send_krp_status(model, ctx, idx, 0x00, STATUS_INVALID_NETKEY); + } else { + send_krp_status(model, ctx, idx, sub->kr_phase, + STATUS_SUCCESS); + } +} + +static void krp_set(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_subnet *sub; + u8_t phase; + u16_t idx; + + idx = net_buf_simple_pull_le16(buf); + phase = net_buf_simple_pull_u8(buf); + + if (idx > 0xfff) { + BT_ERR("%s, Invalid NetKeyIndex 0x%04x", __func__, idx); + return; + } + + BT_DBG("idx 0x%04x transition 0x%02x", idx, phase); + + sub = bt_mesh_subnet_get(idx); + if (!sub) { + send_krp_status(model, ctx, idx, 0x00, STATUS_INVALID_NETKEY); + return; + } + + BT_DBG("%u -> %u", sub->kr_phase, phase); + + if (phase < BLE_MESH_KR_PHASE_2 || phase > BLE_MESH_KR_PHASE_3 || + (sub->kr_phase == BLE_MESH_KR_NORMAL && + phase == BLE_MESH_KR_PHASE_2)) { + BT_WARN("%s, Prohibited transition %u -> %u", __func__, sub->kr_phase, phase); + return; + } + + if (sub->kr_phase == BLE_MESH_KR_PHASE_1 && + phase == BLE_MESH_KR_PHASE_2) { + sub->kr_phase = BLE_MESH_KR_PHASE_2; + sub->kr_flag = 1; + bt_mesh_net_beacon_update(sub); + } else if ((sub->kr_phase == BLE_MESH_KR_PHASE_1 || + sub->kr_phase == BLE_MESH_KR_PHASE_2) && + phase == BLE_MESH_KR_PHASE_3) { + bt_mesh_net_revoke_keys(sub); + if (IS_ENABLED(CONFIG_BLE_MESH_LOW_POWER) || + IS_ENABLED(CONFIG_BLE_MESH_FRIEND)) { + friend_cred_refresh(ctx->net_idx); + } + sub->kr_phase = BLE_MESH_KR_NORMAL; + sub->kr_flag = 0; + bt_mesh_net_beacon_update(sub); + } + + send_krp_status(model, ctx, idx, sub->kr_phase, STATUS_SUCCESS); +} + +static u8_t hb_log(u16_t val) +{ + if (!val) { + return 0x00; + } else if (val == 0xffff) { + return 0xff; + } else { + return 32 - __builtin_clz(val); + } +} + +static u8_t hb_pub_count_log(u16_t val) +{ + if (!val) { + return 0x00; + } else if (val == 0x01) { + return 0x01; + } else if (val == 0xffff) { + return 0xff; + } else { + return 32 - __builtin_clz(val - 1) + 1; + } +} + +static u16_t hb_pwr2(u8_t val, u8_t sub) +{ + if (!val) { + return 0x0000; + } else if (val == 0xff || val == 0x11) { + return 0xffff; + } else { + return (1 << (val - sub)); + } +} + +struct hb_pub_param { + u16_t dst; + u8_t count_log; + u8_t period_log; + u8_t ttl; + u16_t feat; + u16_t net_idx; +} __packed; + +static void hb_pub_send_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, u8_t status, + struct hb_pub_param *orig_msg) +{ + /* Needed size: opcode (1 byte) + msg + MIC */ + NET_BUF_SIMPLE_DEFINE(msg, 1 + 10 + 4); + struct bt_mesh_cfg_srv *cfg = model->user_data; + + BT_DBG("src 0x%04x status 0x%02x", ctx->addr, status); + + bt_mesh_model_msg_init(&msg, OP_HEARTBEAT_PUB_STATUS); + + net_buf_simple_add_u8(&msg, status); + + if (orig_msg) { + memcpy(net_buf_simple_add(&msg, sizeof(*orig_msg)), orig_msg, + sizeof(*orig_msg)); + goto send; + } + + net_buf_simple_add_le16(&msg, cfg->hb_pub.dst); + net_buf_simple_add_u8(&msg, hb_pub_count_log(cfg->hb_pub.count)); + net_buf_simple_add_u8(&msg, cfg->hb_pub.period); + net_buf_simple_add_u8(&msg, cfg->hb_pub.ttl); + net_buf_simple_add_le16(&msg, cfg->hb_pub.feat); + net_buf_simple_add_le16(&msg, cfg->hb_pub.net_idx); + +send: + if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) { + BT_ERR("%s, Unable to send Config Heartbeat Publication Status", __func__); + } +} + +static void heartbeat_pub_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + BT_DBG("src 0x%04x", ctx->addr); + + hb_pub_send_status(model, ctx, STATUS_SUCCESS, NULL); +} + +static void heartbeat_pub_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct hb_pub_param *param = (void *)buf->data; + struct bt_mesh_cfg_srv *cfg = model->user_data; + u16_t dst, feat, idx; + u8_t status; + + BT_DBG("src 0x%04x", ctx->addr); + + dst = sys_le16_to_cpu(param->dst); + /* All other address types but virtual are valid */ + if (BLE_MESH_ADDR_IS_VIRTUAL(dst)) { + status = STATUS_INVALID_ADDRESS; + goto failed; + } + + if (param->count_log > 0x11 && param->count_log != 0xff) { + status = STATUS_CANNOT_SET; + goto failed; + } + + if (param->period_log > 0x10) { + status = STATUS_CANNOT_SET; + goto failed; + } + + if (param->ttl > BLE_MESH_TTL_MAX && param->ttl != BLE_MESH_TTL_DEFAULT) { + BT_ERR("%s, Invalid TTL value 0x%02x", __func__, param->ttl); + return; + } + + feat = sys_le16_to_cpu(param->feat); + + idx = sys_le16_to_cpu(param->net_idx); + if (idx > 0xfff) { + BT_ERR("%s, Invalid NetKeyIndex 0x%04x", __func__, idx); + return; + } + + if (!bt_mesh_subnet_get(idx)) { + status = STATUS_INVALID_NETKEY; + goto failed; + } + + cfg->hb_pub.dst = dst; + cfg->hb_pub.period = param->period_log; + cfg->hb_pub.feat = feat & BLE_MESH_FEAT_SUPPORTED; + cfg->hb_pub.net_idx = idx; + + if (dst == BLE_MESH_ADDR_UNASSIGNED) { + hb_pub_disable(cfg); + } else { + /* 2^(n-1) */ + cfg->hb_pub.count = hb_pwr2(param->count_log, 1); + cfg->hb_pub.ttl = param->ttl; + + BT_DBG("period %u ms", hb_pwr2(param->period_log, 1) * 1000U); + + /* Note: Send heartbeat message here will cause wrong heartbeat status message */ +#if 0 + /* The first Heartbeat message shall be published as soon + * as possible after the Heartbeat Publication Period state + * has been configured for periodic publishing. + */ + if (param->period_log && param->count_log) { + k_work_submit(&cfg->hb_pub.timer.work); + } else { + k_delayed_work_cancel(&cfg->hb_pub.timer); + } +#endif + } + + if (IS_ENABLED(CONFIG_BLE_MESH_SETTINGS)) { + bt_mesh_store_hb_pub(); + } + + hb_pub_send_status(model, ctx, STATUS_SUCCESS, NULL); + + /* The first Heartbeat message shall be published as soon + * as possible after the Heartbeat Publication Period state + * has been configured for periodic publishing. + */ + if (dst != BLE_MESH_ADDR_UNASSIGNED) { + if (param->period_log && param->count_log) { + k_work_submit(&cfg->hb_pub.timer.work); + } else { + k_delayed_work_cancel(&cfg->hb_pub.timer); + } + } + + return; + +failed: + hb_pub_send_status(model, ctx, status, param); +} + +static void hb_sub_send_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, u8_t status) +{ + /* Needed size: opcode (2 bytes) + msg + MIC */ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 9 + 4); + struct bt_mesh_cfg_srv *cfg = model->user_data; + u16_t period; + s64_t uptime; + + BT_DBG("src 0x%04x status 0x%02x", ctx->addr, status); + + uptime = k_uptime_get(); + if (uptime > cfg->hb_sub.expiry) { + period = 0U; + } else { + period = (cfg->hb_sub.expiry - uptime) / 1000; + } + + bt_mesh_model_msg_init(&msg, OP_HEARTBEAT_SUB_STATUS); + + net_buf_simple_add_u8(&msg, status); + net_buf_simple_add_le16(&msg, cfg->hb_sub.src); + net_buf_simple_add_le16(&msg, cfg->hb_sub.dst); + if (cfg->hb_sub.src == BLE_MESH_ADDR_UNASSIGNED || + cfg->hb_sub.dst == BLE_MESH_ADDR_UNASSIGNED) { + memset(net_buf_simple_add(&msg, 4), 0, 4); + } else { + net_buf_simple_add_u8(&msg, hb_log(period)); + net_buf_simple_add_u8(&msg, hb_log(cfg->hb_sub.count)); + net_buf_simple_add_u8(&msg, cfg->hb_sub.min_hops); + net_buf_simple_add_u8(&msg, cfg->hb_sub.max_hops); + } + + if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) { + BT_ERR("%s, Unable to send Config Heartbeat Subscription Status", __func__); + } +} + +static void heartbeat_sub_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + BT_DBG("src 0x%04x", ctx->addr); + + hb_sub_send_status(model, ctx, STATUS_SUCCESS); +} + +static void heartbeat_sub_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_cfg_srv *cfg = model->user_data; + u16_t sub_src, sub_dst; + u8_t sub_period; + s32_t period_ms; + + BT_DBG("src 0x%04x", ctx->addr); + + sub_src = net_buf_simple_pull_le16(buf); + sub_dst = net_buf_simple_pull_le16(buf); + sub_period = net_buf_simple_pull_u8(buf); + + BT_DBG("sub_src 0x%04x sub_dst 0x%04x period 0x%02x", + sub_src, sub_dst, sub_period); + + if (sub_src != BLE_MESH_ADDR_UNASSIGNED && + !BLE_MESH_ADDR_IS_UNICAST(sub_src)) { + BT_WARN("Prohibited source address"); + return; + } + + if (BLE_MESH_ADDR_IS_VIRTUAL(sub_dst) || BLE_MESH_ADDR_IS_RFU(sub_dst) || + (BLE_MESH_ADDR_IS_UNICAST(sub_dst) && + sub_dst != bt_mesh_primary_addr())) { + BT_WARN("Prohibited destination address"); + return; + } + + if (sub_period > 0x11) { + BT_WARN("Prohibited subscription period 0x%02x", sub_period); + return; + } + + if (sub_src == BLE_MESH_ADDR_UNASSIGNED || + sub_dst == BLE_MESH_ADDR_UNASSIGNED || + sub_period == 0x00) { + /* Only an explicit address change to unassigned should + * trigger clearing of the values according to + * MESH/NODE/CFG/HBS/BV-02-C. + */ + if (cfg->hb_sub.src != sub_src || cfg->hb_sub.dst != sub_dst) { + cfg->hb_sub.src = BLE_MESH_ADDR_UNASSIGNED; + cfg->hb_sub.dst = BLE_MESH_ADDR_UNASSIGNED; + } + + period_ms = 0; + } else { + cfg->hb_sub.src = sub_src; + cfg->hb_sub.dst = sub_dst; + cfg->hb_sub.min_hops = BLE_MESH_TTL_MAX; + cfg->hb_sub.max_hops = 0U; + cfg->hb_sub.count = 0U; + period_ms = hb_pwr2(sub_period, 1) * 1000U; + } + + /* Let the transport layer know it needs to handle this address */ + bt_mesh_set_hb_sub_dst(cfg->hb_sub.dst); + + BT_DBG("period_ms %u", period_ms); + + if (period_ms) { + cfg->hb_sub.expiry = k_uptime_get() + period_ms; + } else { + cfg->hb_sub.expiry = 0; + } + + hb_sub_send_status(model, ctx, STATUS_SUCCESS); + + /* MESH/NODE/CFG/HBS/BV-01-C expects the MinHops to be 0x7f after + * disabling subscription, but 0x00 for subsequent Get requests. + */ + if (!period_ms) { + cfg->hb_sub.min_hops = 0U; + } +} + +const struct bt_mesh_model_op bt_mesh_cfg_srv_op[] = { + { OP_DEV_COMP_DATA_GET, 1, dev_comp_data_get }, + { OP_APP_KEY_ADD, 19, app_key_add }, + { OP_APP_KEY_UPDATE, 19, app_key_update }, + { OP_APP_KEY_DEL, 3, app_key_del }, + { OP_APP_KEY_GET, 2, app_key_get }, + { OP_BEACON_GET, 0, beacon_get }, + { OP_BEACON_SET, 1, beacon_set }, + { OP_DEFAULT_TTL_GET, 0, default_ttl_get }, + { OP_DEFAULT_TTL_SET, 1, default_ttl_set }, + { OP_GATT_PROXY_GET, 0, gatt_proxy_get }, + { OP_GATT_PROXY_SET, 1, gatt_proxy_set }, + { OP_NET_TRANSMIT_GET, 0, net_transmit_get }, + { OP_NET_TRANSMIT_SET, 1, net_transmit_set }, + { OP_RELAY_GET, 0, relay_get }, + { OP_RELAY_SET, 2, relay_set }, + { OP_MOD_PUB_GET, 4, mod_pub_get }, + { OP_MOD_PUB_SET, 11, mod_pub_set }, + { OP_MOD_PUB_VA_SET, 24, mod_pub_va_set }, + { OP_MOD_SUB_ADD, 6, mod_sub_add }, + { OP_MOD_SUB_VA_ADD, 20, mod_sub_va_add }, + { OP_MOD_SUB_DEL, 6, mod_sub_del }, + { OP_MOD_SUB_VA_DEL, 20, mod_sub_va_del }, + { OP_MOD_SUB_OVERWRITE, 6, mod_sub_overwrite }, + { OP_MOD_SUB_VA_OVERWRITE, 20, mod_sub_va_overwrite }, + { OP_MOD_SUB_DEL_ALL, 4, mod_sub_del_all }, + { OP_MOD_SUB_GET, 4, mod_sub_get }, + { OP_MOD_SUB_GET_VND, 6, mod_sub_get_vnd }, + { OP_NET_KEY_ADD, 18, net_key_add }, + { OP_NET_KEY_UPDATE, 18, net_key_update }, + { OP_NET_KEY_DEL, 2, net_key_del }, + { OP_NET_KEY_GET, 0, net_key_get }, + { OP_NODE_IDENTITY_GET, 2, node_identity_get }, + { OP_NODE_IDENTITY_SET, 3, node_identity_set }, + { OP_MOD_APP_BIND, 6, mod_app_bind }, + { OP_MOD_APP_UNBIND, 6, mod_app_unbind }, + { OP_SIG_MOD_APP_GET, 4, mod_app_get }, + { OP_VND_MOD_APP_GET, 6, mod_app_get }, + { OP_NODE_RESET, 0, node_reset }, + { OP_FRIEND_GET, 0, friend_get }, + { OP_FRIEND_SET, 1, friend_set }, + { OP_LPN_TIMEOUT_GET, 2, lpn_timeout_get }, + { OP_KRP_GET, 2, krp_get }, + { OP_KRP_SET, 3, krp_set }, + { OP_HEARTBEAT_PUB_GET, 0, heartbeat_pub_get }, + { OP_HEARTBEAT_PUB_SET, 9, heartbeat_pub_set }, + { OP_HEARTBEAT_SUB_GET, 0, heartbeat_sub_get }, + { OP_HEARTBEAT_SUB_SET, 5, heartbeat_sub_set }, + BLE_MESH_MODEL_OP_END, +}; + +static void hb_publish(struct k_work *work) +{ + struct bt_mesh_cfg_srv *cfg = CONTAINER_OF(work, + struct bt_mesh_cfg_srv, + hb_pub.timer.work); + struct bt_mesh_model *model = cfg->model; + struct bt_mesh_subnet *sub; + u16_t period_ms; + + BT_DBG("hb_pub.count: %u", cfg->hb_pub.count); + + sub = bt_mesh_subnet_get(cfg->hb_pub.net_idx); + if (!sub) { + BT_ERR("%s, No matching subnet for idx 0x%02x", + __func__, cfg->hb_pub.net_idx); + cfg->hb_pub.dst = BLE_MESH_ADDR_UNASSIGNED; + return; + } + + if (cfg->hb_pub.count == 0U) { + return; + } + + period_ms = hb_pwr2(cfg->hb_pub.period, 1) * 1000U; + if (period_ms && cfg->hb_pub.count > 1) { + k_delayed_work_submit(&cfg->hb_pub.timer, period_ms); + } + + hb_send(model); + + if (cfg->hb_pub.count != 0xffff) { + cfg->hb_pub.count--; + } +} + +static bool conf_is_valid(struct bt_mesh_cfg_srv *cfg) +{ + if (cfg->relay > 0x02) { + return false; + } + + if (cfg->beacon > 0x01) { + return false; + } + + if (cfg->default_ttl > BLE_MESH_TTL_MAX) { + return false; + } + + return true; +} + +int bt_mesh_cfg_srv_init(struct bt_mesh_model *model, bool primary) +{ + struct bt_mesh_cfg_srv *cfg = model->user_data; + + if (!cfg) { + BT_ERR("%s, No Configuration Server context provided", __func__); + return -EINVAL; + } + + if (!conf_is_valid(cfg)) { + BT_ERR("%s, Invalid values in configuration", __func__); + return -EINVAL; + } + + /* Configuration Model security is device-key based */ + model->keys[0] = BLE_MESH_KEY_DEV; + + if (!IS_ENABLED(CONFIG_BLE_MESH_RELAY)) { + cfg->relay = BLE_MESH_RELAY_NOT_SUPPORTED; + } + + if (!IS_ENABLED(CONFIG_BLE_MESH_FRIEND)) { + cfg->frnd = BLE_MESH_FRIEND_NOT_SUPPORTED; + } + + if (!IS_ENABLED(CONFIG_BLE_MESH_GATT_PROXY)) { + cfg->gatt_proxy = BLE_MESH_GATT_PROXY_NOT_SUPPORTED; + } + + k_delayed_work_init(&cfg->hb_pub.timer, hb_publish); + cfg->hb_pub.net_idx = BLE_MESH_KEY_UNUSED; + cfg->hb_sub.expiry = 0; + + cfg->model = model; + + conf = cfg; + + return 0; +} + +static void mod_reset(struct bt_mesh_model *mod, struct bt_mesh_elem *elem, + bool vnd, bool primary, void *user_data) +{ + size_t clear_count; + + /* Clear model state that isn't otherwise cleared. E.g. AppKey + * binding and model publication is cleared as a consequence + * of removing all app keys, however model subscription clearing + * must be taken care of here. + */ + + clear_count = mod_sub_list_clear(mod); + + if (IS_ENABLED(CONFIG_BLE_MESH_SETTINGS) && clear_count) { + bt_mesh_store_mod_sub(mod); + } +} + +void bt_mesh_cfg_reset(void) +{ + struct bt_mesh_cfg_srv *cfg = conf; + int i; + + BT_DBG("%s", __func__); + + if (!cfg) { + return; + } + + bt_mesh_set_hb_sub_dst(BLE_MESH_ADDR_UNASSIGNED); + + cfg->hb_sub.src = BLE_MESH_ADDR_UNASSIGNED; + cfg->hb_sub.dst = BLE_MESH_ADDR_UNASSIGNED; + cfg->hb_sub.expiry = 0; + + /* Delete all net keys, which also takes care of all app keys which + * are associated with each net key. + */ + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + struct bt_mesh_subnet *sub = &bt_mesh.sub[i]; + + if (sub->net_idx != BLE_MESH_KEY_UNUSED) { + bt_mesh_subnet_del(sub, true); + } + } + + bt_mesh_model_foreach(mod_reset, NULL); + + (void)memset(labels, 0, sizeof(labels)); +} + +void bt_mesh_heartbeat(u16_t src, u16_t dst, u8_t hops, u16_t feat) +{ + struct bt_mesh_cfg_srv *cfg = conf; + + if (!cfg) { + BT_WARN("No configuaration server context available"); + return; + } + + if (src != cfg->hb_sub.src || dst != cfg->hb_sub.dst) { + BT_WARN("No subscription for received heartbeat"); + return; + } + + if (k_uptime_get() > cfg->hb_sub.expiry) { + BT_WARN("Heartbeat subscription period expired"); + return; + } + + cfg->hb_sub.min_hops = MIN(cfg->hb_sub.min_hops, hops); + cfg->hb_sub.max_hops = MAX(cfg->hb_sub.max_hops, hops); + + if (cfg->hb_sub.count < 0xffff) { + cfg->hb_sub.count++; + } + + BT_DBG("src 0x%04x dst 0x%04x hops %u min %u max %u count %u", src, + dst, hops, cfg->hb_sub.min_hops, cfg->hb_sub.max_hops, + cfg->hb_sub.count); + + if (cfg->hb_sub.func) { + cfg->hb_sub.func(hops, feat); + } +} + +u8_t bt_mesh_net_transmit_get(void) +{ + if (conf) { + return conf->net_transmit; + } + + return 0; +} + +u8_t bt_mesh_relay_get(void) +{ + if (conf) { + return conf->relay; + } + + return BLE_MESH_RELAY_NOT_SUPPORTED; +} + +u8_t bt_mesh_friend_get(void) +{ + if (conf) { + BT_DBG("conf %p conf->frnd 0x%02x", conf, conf->frnd); + return conf->frnd; + } + + return BLE_MESH_FRIEND_NOT_SUPPORTED; +} + +u8_t bt_mesh_relay_retransmit_get(void) +{ + if (conf) { + return conf->relay_retransmit; + } + + return 0; +} + +u8_t bt_mesh_beacon_get(void) +{ + if (conf) { + return conf->beacon; + } + + return BLE_MESH_BEACON_DISABLED; +} + +u8_t bt_mesh_gatt_proxy_get(void) +{ + if (conf) { + return conf->gatt_proxy; + } + + return BLE_MESH_GATT_PROXY_NOT_SUPPORTED; +} + +u8_t bt_mesh_default_ttl_get(void) +{ + if (conf) { + return conf->default_ttl; + } + + return DEFAULT_TTL; +} + +u8_t *bt_mesh_label_uuid_get(u16_t addr) +{ + int i; + + BT_DBG("addr 0x%04x", addr); + + for (i = 0; i < ARRAY_SIZE(labels); i++) { + if (labels[i].addr == addr) { + BT_DBG("Found Label UUID for 0x%04x: %s", addr, + bt_hex(labels[i].uuid, 16)); + return labels[i].uuid; + } + } + + BT_WARN("No matching Label UUID for 0x%04x", addr); + + return NULL; +} + +struct bt_mesh_hb_pub *bt_mesh_hb_pub_get(void) +{ + if (!conf) { + return NULL; + } + + return &conf->hb_pub; +} + +void bt_mesh_hb_pub_disable(void) +{ + if (conf) { + hb_pub_disable(conf); + } +} + +struct bt_mesh_cfg_srv *bt_mesh_cfg_get(void) +{ + return conf; +} + +void bt_mesh_subnet_del(struct bt_mesh_subnet *sub, bool store) +{ + int i; + + BT_DBG("NetIdx 0x%03x store %u", sub->net_idx, store); + + if (conf && conf->hb_pub.net_idx == sub->net_idx) { + hb_pub_disable(conf); + + if (IS_ENABLED(CONFIG_BLE_MESH_SETTINGS) && store) { + bt_mesh_store_hb_pub(); + } + } + + /* Delete any app keys bound to this NetKey index */ + for (i = 0; i < ARRAY_SIZE(bt_mesh.app_keys); i++) { + struct bt_mesh_app_key *key = &bt_mesh.app_keys[i]; + + if (key->net_idx == sub->net_idx) { + bt_mesh_app_key_del(key, store); + } + } + + if (IS_ENABLED(CONFIG_BLE_MESH_FRIEND)) { + bt_mesh_friend_clear_net_idx(sub->net_idx); + } + + if (IS_ENABLED(CONFIG_BLE_MESH_SETTINGS) && store) { + bt_mesh_clear_subnet(sub); + } + + (void)memset(sub, 0, sizeof(*sub)); + sub->net_idx = BLE_MESH_KEY_UNUSED; +} diff --git a/components/bt/ble_mesh/mesh_core/crypto.c b/components/bt/ble_mesh/mesh_core/crypto.c new file mode 100644 index 0000000000..d2e6507882 --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/crypto.c @@ -0,0 +1,878 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include "sdkconfig.h" +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BLE_MESH_DEBUG_CRYPTO) + +#include "mesh_types.h" +#include "mesh_util.h" +#include "mesh_buf.h" +#include "mesh_trace.h" +#include "mesh_bearer_adapt.h" +#include "mesh_aes_encrypt.h" + +#include "mesh.h" +#include "crypto.h" + +#define NET_MIC_LEN(pdu) (((pdu)[1] & 0x80) ? 8 : 4) +#define APP_MIC_LEN(aszmic) ((aszmic) ? 8 : 4) + +int bt_mesh_aes_cmac(const u8_t key[16], struct bt_mesh_sg *sg, + size_t sg_len, u8_t mac[16]) +{ + struct tc_aes_key_sched_struct sched; + struct tc_cmac_struct state; + + if (tc_cmac_setup(&state, key, &sched) == TC_CRYPTO_FAIL) { + return -EIO; + } + + for (; sg_len; sg_len--, sg++) { + if (tc_cmac_update(&state, sg->data, + sg->len) == TC_CRYPTO_FAIL) { + return -EIO; + } + } + + if (tc_cmac_final(mac, &state) == TC_CRYPTO_FAIL) { + return -EIO; + } + + return 0; +} + +int bt_mesh_k1(const u8_t *ikm, size_t ikm_len, const u8_t salt[16], + const char *info, u8_t okm[16]) +{ + int err; + + err = bt_mesh_aes_cmac_one(salt, ikm, ikm_len, okm); + if (err < 0) { + return err; + } + + return bt_mesh_aes_cmac_one(okm, info, strlen(info), okm); +} + +int bt_mesh_k2(const u8_t n[16], const u8_t *p, size_t p_len, + u8_t net_id[1], u8_t enc_key[16], u8_t priv_key[16]) +{ + struct bt_mesh_sg sg[3]; + u8_t salt[16]; + u8_t out[16]; + u8_t t[16]; + u8_t pad; + int err; + + BT_DBG("n %s", bt_hex(n, 16)); + BT_DBG("p %s", bt_hex(p, p_len)); + + err = bt_mesh_s1("smk2", salt); + if (err) { + return err; + } + + err = bt_mesh_aes_cmac_one(salt, n, 16, t); + if (err) { + return err; + } + + pad = 0x01; + + sg[0].data = NULL; + sg[0].len = 0; + sg[1].data = p; + sg[1].len = p_len; + sg[2].data = &pad; + sg[2].len = sizeof(pad); + + err = bt_mesh_aes_cmac(t, sg, ARRAY_SIZE(sg), out); + if (err) { + return err; + } + + net_id[0] = out[15] & 0x7f; + + sg[0].data = out; + sg[0].len = sizeof(out); + pad = 0x02; + + err = bt_mesh_aes_cmac(t, sg, ARRAY_SIZE(sg), out); + if (err) { + return err; + } + + memcpy(enc_key, out, 16); + + pad = 0x03; + + err = bt_mesh_aes_cmac(t, sg, ARRAY_SIZE(sg), out); + if (err) { + return err; + } + + memcpy(priv_key, out, 16); + + BT_DBG("NID 0x%02x enc_key %s", net_id[0], bt_hex(enc_key, 16)); + BT_DBG("priv_key %s", bt_hex(priv_key, 16)); + + return 0; +} + +int bt_mesh_k3(const u8_t n[16], u8_t out[8]) +{ + u8_t id64[] = { 'i', 'd', '6', '4', 0x01 }; + u8_t tmp[16]; + u8_t t[16]; + int err; + + err = bt_mesh_s1("smk3", tmp); + if (err) { + return err; + } + + err = bt_mesh_aes_cmac_one(tmp, n, 16, t); + if (err) { + return err; + } + + err = bt_mesh_aes_cmac_one(t, id64, sizeof(id64), tmp); + if (err) { + return err; + } + + memcpy(out, tmp + 8, 8); + + return 0; +} + +int bt_mesh_k4(const u8_t n[16], u8_t out[1]) +{ + u8_t id6[] = { 'i', 'd', '6', 0x01 }; + u8_t tmp[16]; + u8_t t[16]; + int err; + + err = bt_mesh_s1("smk4", tmp); + if (err) { + return err; + } + + err = bt_mesh_aes_cmac_one(tmp, n, 16, t); + if (err) { + return err; + } + + err = bt_mesh_aes_cmac_one(t, id6, sizeof(id6), tmp); + if (err) { + return err; + } + + out[0] = tmp[15] & BIT_MASK(6); + + return 0; +} + +int bt_mesh_id128(const u8_t n[16], const char *s, u8_t out[16]) +{ + const char *id128 = "id128\x01"; + u8_t salt[16]; + int err; + + err = bt_mesh_s1(s, salt); + if (err) { + return err; + } + + return bt_mesh_k1(n, 16, salt, id128, out); +} + +static int bt_mesh_ccm_decrypt(const u8_t key[16], u8_t nonce[13], + const u8_t *enc_msg, size_t msg_len, + const u8_t *aad, size_t aad_len, + u8_t *out_msg, size_t mic_size) +{ + u8_t msg[16], pmsg[16], cmic[16], cmsg[16], Xn[16], mic[16]; + u16_t last_blk, blk_cnt; + size_t i, j; + int err; + + if (msg_len < 1 || aad_len >= 0xff00) { + return -EINVAL; + } + + /* C_mic = e(AppKey, 0x01 || nonce || 0x0000) */ + pmsg[0] = 0x01; + memcpy(pmsg + 1, nonce, 13); + sys_put_be16(0x0000, pmsg + 14); + + err = bt_mesh_encrypt_be(key, pmsg, cmic); + if (err) { + return err; + } + + /* X_0 = e(AppKey, 0x09 || nonce || length) */ + if (mic_size == sizeof(u64_t)) { + pmsg[0] = 0x19 | (aad_len ? 0x40 : 0x00); + } else { + pmsg[0] = 0x09 | (aad_len ? 0x40 : 0x00); + } + + memcpy(pmsg + 1, nonce, 13); + sys_put_be16(msg_len, pmsg + 14); + + err = bt_mesh_encrypt_be(key, pmsg, Xn); + if (err) { + return err; + } + + /* If AAD is being used to authenticate, include it here */ + if (aad_len) { + sys_put_be16(aad_len, pmsg); + + for (i = 0; i < sizeof(u16_t); i++) { + pmsg[i] = Xn[i] ^ pmsg[i]; + } + + j = 0; + aad_len += sizeof(u16_t); + while (aad_len > 16) { + do { + pmsg[i] = Xn[i] ^ aad[j]; + i++, j++; + } while (i < 16); + + aad_len -= 16; + i = 0; + + err = bt_mesh_encrypt_be(key, pmsg, Xn); + if (err) { + return err; + } + } + + for (i = 0; i < aad_len; i++, j++) { + pmsg[i] = Xn[i] ^ aad[j]; + } + + for (i = aad_len; i < 16; i++) { + pmsg[i] = Xn[i]; + } + + err = bt_mesh_encrypt_be(key, pmsg, Xn); + if (err) { + return err; + } + } + + last_blk = msg_len % 16; + blk_cnt = (msg_len + 15) / 16; + if (!last_blk) { + last_blk = 16U; + } + + for (j = 0; j < blk_cnt; j++) { + if (j + 1 == blk_cnt) { + /* C_1 = e(AppKey, 0x01 || nonce || 0x0001) */ + pmsg[0] = 0x01; + memcpy(pmsg + 1, nonce, 13); + sys_put_be16(j + 1, pmsg + 14); + + err = bt_mesh_encrypt_be(key, pmsg, cmsg); + if (err) { + return err; + } + + /* Encrypted = Payload[0-15] ^ C_1 */ + for (i = 0; i < last_blk; i++) { + msg[i] = enc_msg[(j * 16) + i] ^ cmsg[i]; + } + + memcpy(out_msg + (j * 16), msg, last_blk); + + /* X_1 = e(AppKey, X_0 ^ Payload[0-15]) */ + for (i = 0; i < last_blk; i++) { + pmsg[i] = Xn[i] ^ msg[i]; + } + + for (i = last_blk; i < 16; i++) { + pmsg[i] = Xn[i] ^ 0x00; + } + + err = bt_mesh_encrypt_be(key, pmsg, Xn); + if (err) { + return err; + } + + /* MIC = C_mic ^ X_1 */ + for (i = 0; i < sizeof(mic); i++) { + mic[i] = cmic[i] ^ Xn[i]; + } + } else { + /* C_1 = e(AppKey, 0x01 || nonce || 0x0001) */ + pmsg[0] = 0x01; + memcpy(pmsg + 1, nonce, 13); + sys_put_be16(j + 1, pmsg + 14); + + err = bt_mesh_encrypt_be(key, pmsg, cmsg); + if (err) { + return err; + } + + /* Encrypted = Payload[0-15] ^ C_1 */ + for (i = 0; i < 16; i++) { + msg[i] = enc_msg[(j * 16) + i] ^ cmsg[i]; + } + + memcpy(out_msg + (j * 16), msg, 16); + + /* X_1 = e(AppKey, X_0 ^ Payload[0-15]) */ + for (i = 0; i < 16; i++) { + pmsg[i] = Xn[i] ^ msg[i]; + } + + err = bt_mesh_encrypt_be(key, pmsg, Xn); + if (err) { + return err; + } + } + } + + if (memcmp(mic, enc_msg + msg_len, mic_size)) { + return -EBADMSG; + } + + return 0; +} + +static int bt_mesh_ccm_encrypt(const u8_t key[16], u8_t nonce[13], + const u8_t *msg, size_t msg_len, + const u8_t *aad, size_t aad_len, + u8_t *out_msg, size_t mic_size) +{ + u8_t pmsg[16], cmic[16], cmsg[16], mic[16], Xn[16]; + u16_t blk_cnt, last_blk; + size_t i, j; + int err; + + BT_DBG("key %s", bt_hex(key, 16)); + BT_DBG("nonce %s", bt_hex(nonce, 13)); + BT_DBG("msg (len %u) %s", msg_len, bt_hex(msg, msg_len)); + BT_DBG("aad_len %u mic_size %u", aad_len, mic_size); + + /* Unsupported AAD size */ + if (aad_len >= 0xff00) { + return -EINVAL; + } + + /* C_mic = e(AppKey, 0x01 || nonce || 0x0000) */ + pmsg[0] = 0x01; + memcpy(pmsg + 1, nonce, 13); + sys_put_be16(0x0000, pmsg + 14); + + err = bt_mesh_encrypt_be(key, pmsg, cmic); + if (err) { + return err; + } + + /* X_0 = e(AppKey, 0x09 || nonce || length) */ + if (mic_size == sizeof(u64_t)) { + pmsg[0] = 0x19 | (aad_len ? 0x40 : 0x00); + } else { + pmsg[0] = 0x09 | (aad_len ? 0x40 : 0x00); + } + + memcpy(pmsg + 1, nonce, 13); + sys_put_be16(msg_len, pmsg + 14); + + err = bt_mesh_encrypt_be(key, pmsg, Xn); + if (err) { + return err; + } + + /* If AAD is being used to authenticate, include it here */ + if (aad_len) { + sys_put_be16(aad_len, pmsg); + + for (i = 0; i < sizeof(u16_t); i++) { + pmsg[i] = Xn[i] ^ pmsg[i]; + } + + j = 0; + aad_len += sizeof(u16_t); + while (aad_len > 16) { + do { + pmsg[i] = Xn[i] ^ aad[j]; + i++, j++; + } while (i < 16); + + aad_len -= 16; + i = 0; + + err = bt_mesh_encrypt_be(key, pmsg, Xn); + if (err) { + return err; + } + } + + for (i = 0; i < aad_len; i++, j++) { + pmsg[i] = Xn[i] ^ aad[j]; + } + + for (i = aad_len; i < 16; i++) { + pmsg[i] = Xn[i]; + } + + err = bt_mesh_encrypt_be(key, pmsg, Xn); + if (err) { + return err; + } + } + + last_blk = msg_len % 16; + blk_cnt = (msg_len + 15) / 16; + if (!last_blk) { + last_blk = 16U; + } + + for (j = 0; j < blk_cnt; j++) { + if (j + 1 == blk_cnt) { + /* X_1 = e(AppKey, X_0 ^ Payload[0-15]) */ + for (i = 0; i < last_blk; i++) { + pmsg[i] = Xn[i] ^ msg[(j * 16) + i]; + } + for (i = last_blk; i < 16; i++) { + pmsg[i] = Xn[i] ^ 0x00; + } + + err = bt_mesh_encrypt_be(key, pmsg, Xn); + if (err) { + return err; + } + + /* MIC = C_mic ^ X_1 */ + for (i = 0; i < sizeof(mic); i++) { + mic[i] = cmic[i] ^ Xn[i]; + } + + /* C_1 = e(AppKey, 0x01 || nonce || 0x0001) */ + pmsg[0] = 0x01; + memcpy(pmsg + 1, nonce, 13); + sys_put_be16(j + 1, pmsg + 14); + + err = bt_mesh_encrypt_be(key, pmsg, cmsg); + if (err) { + return err; + } + + /* Encrypted = Payload[0-15] ^ C_1 */ + for (i = 0; i < last_blk; i++) { + out_msg[(j * 16) + i] = + msg[(j * 16) + i] ^ cmsg[i]; + } + } else { + /* X_1 = e(AppKey, X_0 ^ Payload[0-15]) */ + for (i = 0; i < 16; i++) { + pmsg[i] = Xn[i] ^ msg[(j * 16) + i]; + } + + err = bt_mesh_encrypt_be(key, pmsg, Xn); + if (err) { + return err; + } + + /* C_1 = e(AppKey, 0x01 || nonce || 0x0001) */ + pmsg[0] = 0x01; + memcpy(pmsg + 1, nonce, 13); + sys_put_be16(j + 1, pmsg + 14); + + err = bt_mesh_encrypt_be(key, pmsg, cmsg); + if (err) { + return err; + } + + /* Encrypted = Payload[0-15] ^ C_N */ + for (i = 0; i < 16; i++) { + out_msg[(j * 16) + i] = + msg[(j * 16) + i] ^ cmsg[i]; + } + + } + } + + memcpy(out_msg + msg_len, mic, mic_size); + + return 0; +} + +#if defined(CONFIG_BLE_MESH_PROXY) +static void create_proxy_nonce(u8_t nonce[13], const u8_t *pdu, + u32_t iv_index) +{ + /* Nonce Type */ + nonce[0] = 0x03; + + /* Pad */ + nonce[1] = 0x00; + + /* Sequence Number */ + nonce[2] = pdu[2]; + nonce[3] = pdu[3]; + nonce[4] = pdu[4]; + + /* Source Address */ + nonce[5] = pdu[5]; + nonce[6] = pdu[6]; + + /* Pad */ + nonce[7] = 0U; + nonce[8] = 0U; + + /* IV Index */ + sys_put_be32(iv_index, &nonce[9]); +} +#endif /* PROXY */ + +static void create_net_nonce(u8_t nonce[13], const u8_t *pdu, + u32_t iv_index) +{ + /* Nonce Type */ + nonce[0] = 0x00; + + /* FRND + TTL */ + nonce[1] = pdu[1]; + + /* Sequence Number */ + nonce[2] = pdu[2]; + nonce[3] = pdu[3]; + nonce[4] = pdu[4]; + + /* Source Address */ + nonce[5] = pdu[5]; + nonce[6] = pdu[6]; + + /* Pad */ + nonce[7] = 0U; + nonce[8] = 0U; + + /* IV Index */ + sys_put_be32(iv_index, &nonce[9]); +} + +int bt_mesh_net_obfuscate(u8_t *pdu, u32_t iv_index, + const u8_t privacy_key[16]) +{ + u8_t priv_rand[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, }; + u8_t tmp[16]; + int err, i; + + BT_DBG("IVIndex %u, PrivacyKey %s", iv_index, bt_hex(privacy_key, 16)); + + sys_put_be32(iv_index, &priv_rand[5]); + memcpy(&priv_rand[9], &pdu[7], 7); + + BT_DBG("PrivacyRandom %s", bt_hex(priv_rand, 16)); + + err = bt_mesh_encrypt_be(privacy_key, priv_rand, tmp); + if (err) { + return err; + } + + for (i = 0; i < 6; i++) { + pdu[1 + i] ^= tmp[i]; + } + + return 0; +} + +int bt_mesh_net_encrypt(const u8_t key[16], struct net_buf_simple *buf, + u32_t iv_index, bool proxy) +{ + u8_t mic_len = NET_MIC_LEN(buf->data); + u8_t nonce[13]; + int err; + + BT_DBG("IVIndex %u EncKey %s mic_len %u", iv_index, bt_hex(key, 16), + mic_len); + BT_DBG("PDU (len %u) %s", buf->len, bt_hex(buf->data, buf->len)); + +#if defined(CONFIG_BLE_MESH_PROXY) + if (proxy) { + create_proxy_nonce(nonce, buf->data, iv_index); + } else { + create_net_nonce(nonce, buf->data, iv_index); + } +#else + create_net_nonce(nonce, buf->data, iv_index); +#endif + + BT_DBG("Nonce %s", bt_hex(nonce, 13)); + + err = bt_mesh_ccm_encrypt(key, nonce, &buf->data[7], buf->len - 7, + NULL, 0, &buf->data[7], mic_len); + if (!err) { + net_buf_simple_add(buf, mic_len); + } + + return err; +} + +int bt_mesh_net_decrypt(const u8_t key[16], struct net_buf_simple *buf, + u32_t iv_index, bool proxy) +{ + u8_t mic_len = NET_MIC_LEN(buf->data); + u8_t nonce[13]; + + BT_DBG("PDU (%u bytes) %s", buf->len, bt_hex(buf->data, buf->len)); + BT_DBG("iv_index %u, key %s mic_len %u", iv_index, bt_hex(key, 16), + mic_len); + +#if defined(CONFIG_BLE_MESH_PROXY) + if (proxy) { + create_proxy_nonce(nonce, buf->data, iv_index); + } else { + create_net_nonce(nonce, buf->data, iv_index); + } +#else + create_net_nonce(nonce, buf->data, iv_index); +#endif + + BT_DBG("Nonce %s", bt_hex(nonce, 13)); + + buf->len -= mic_len; + + return bt_mesh_ccm_decrypt(key, nonce, &buf->data[7], buf->len - 7, + NULL, 0, &buf->data[7], mic_len); +} + +static void create_app_nonce(u8_t nonce[13], bool dev_key, u8_t aszmic, + u16_t src, u16_t dst, u32_t seq_num, + u32_t iv_index) +{ + if (dev_key) { + nonce[0] = 0x02; + } else { + nonce[0] = 0x01; + } + + sys_put_be32((seq_num | ((u32_t)aszmic << 31)), &nonce[1]); + + sys_put_be16(src, &nonce[5]); + sys_put_be16(dst, &nonce[7]); + + sys_put_be32(iv_index, &nonce[9]); +} + +int bt_mesh_app_encrypt(const u8_t key[16], bool dev_key, u8_t aszmic, + struct net_buf_simple *buf, const u8_t *ad, + u16_t src, u16_t dst, u32_t seq_num, u32_t iv_index) +{ + u8_t nonce[13]; + int err; + + BT_DBG("AppKey %s", bt_hex(key, 16)); + BT_DBG("dev_key %u src 0x%04x dst 0x%04x", dev_key, src, dst); + BT_DBG("seq_num 0x%08x iv_index 0x%08x", seq_num, iv_index); + BT_DBG("Clear: %s", bt_hex(buf->data, buf->len)); + + create_app_nonce(nonce, dev_key, aszmic, src, dst, seq_num, iv_index); + + BT_DBG("Nonce %s", bt_hex(nonce, 13)); + + err = bt_mesh_ccm_encrypt(key, nonce, buf->data, buf->len, ad, + ad ? 16 : 0, buf->data, APP_MIC_LEN(aszmic)); + if (!err) { + net_buf_simple_add(buf, APP_MIC_LEN(aszmic)); + BT_DBG("Encr: %s", bt_hex(buf->data, buf->len)); + } + + return err; +} + +int bt_mesh_app_decrypt(const u8_t key[16], bool dev_key, u8_t aszmic, + struct net_buf_simple *buf, struct net_buf_simple *out, + const u8_t *ad, u16_t src, u16_t dst, u32_t seq_num, + u32_t iv_index) +{ + u8_t nonce[13]; + int err; + + BT_DBG("EncData (len %u) %s", buf->len, bt_hex(buf->data, buf->len)); + + create_app_nonce(nonce, dev_key, aszmic, src, dst, seq_num, iv_index); + + BT_DBG("AppKey %s", bt_hex(key, 16)); + BT_DBG("Nonce %s", bt_hex(nonce, 13)); + + err = bt_mesh_ccm_decrypt(key, nonce, buf->data, buf->len, ad, + ad ? 16 : 0, out->data, APP_MIC_LEN(aszmic)); + if (!err) { + net_buf_simple_add(out, buf->len); + } + + return err; +} + +/* reversed, 8-bit, poly=0x07 */ +static const u8_t crc_table[256] = { + 0x00, 0x91, 0xe3, 0x72, 0x07, 0x96, 0xe4, 0x75, + 0x0e, 0x9f, 0xed, 0x7c, 0x09, 0x98, 0xea, 0x7b, + 0x1c, 0x8d, 0xff, 0x6e, 0x1b, 0x8a, 0xf8, 0x69, + 0x12, 0x83, 0xf1, 0x60, 0x15, 0x84, 0xf6, 0x67, + + 0x38, 0xa9, 0xdb, 0x4a, 0x3f, 0xae, 0xdc, 0x4d, + 0x36, 0xa7, 0xd5, 0x44, 0x31, 0xa0, 0xd2, 0x43, + 0x24, 0xb5, 0xc7, 0x56, 0x23, 0xb2, 0xc0, 0x51, + 0x2a, 0xbb, 0xc9, 0x58, 0x2d, 0xbc, 0xce, 0x5f, + + 0x70, 0xe1, 0x93, 0x02, 0x77, 0xe6, 0x94, 0x05, + 0x7e, 0xef, 0x9d, 0x0c, 0x79, 0xe8, 0x9a, 0x0b, + 0x6c, 0xfd, 0x8f, 0x1e, 0x6b, 0xfa, 0x88, 0x19, + 0x62, 0xf3, 0x81, 0x10, 0x65, 0xf4, 0x86, 0x17, + + 0x48, 0xd9, 0xab, 0x3a, 0x4f, 0xde, 0xac, 0x3d, + 0x46, 0xd7, 0xa5, 0x34, 0x41, 0xd0, 0xa2, 0x33, + 0x54, 0xc5, 0xb7, 0x26, 0x53, 0xc2, 0xb0, 0x21, + 0x5a, 0xcb, 0xb9, 0x28, 0x5d, 0xcc, 0xbe, 0x2f, + + 0xe0, 0x71, 0x03, 0x92, 0xe7, 0x76, 0x04, 0x95, + 0xee, 0x7f, 0x0d, 0x9c, 0xe9, 0x78, 0x0a, 0x9b, + 0xfc, 0x6d, 0x1f, 0x8e, 0xfb, 0x6a, 0x18, 0x89, + 0xf2, 0x63, 0x11, 0x80, 0xf5, 0x64, 0x16, 0x87, + + 0xd8, 0x49, 0x3b, 0xaa, 0xdf, 0x4e, 0x3c, 0xad, + 0xd6, 0x47, 0x35, 0xa4, 0xd1, 0x40, 0x32, 0xa3, + 0xc4, 0x55, 0x27, 0xb6, 0xc3, 0x52, 0x20, 0xb1, + 0xca, 0x5b, 0x29, 0xb8, 0xcd, 0x5c, 0x2e, 0xbf, + + 0x90, 0x01, 0x73, 0xe2, 0x97, 0x06, 0x74, 0xe5, + 0x9e, 0x0f, 0x7d, 0xec, 0x99, 0x08, 0x7a, 0xeb, + 0x8c, 0x1d, 0x6f, 0xfe, 0x8b, 0x1a, 0x68, 0xf9, + 0x82, 0x13, 0x61, 0xf0, 0x85, 0x14, 0x66, 0xf7, + + 0xa8, 0x39, 0x4b, 0xda, 0xaf, 0x3e, 0x4c, 0xdd, + 0xa6, 0x37, 0x45, 0xd4, 0xa1, 0x30, 0x42, 0xd3, + 0xb4, 0x25, 0x57, 0xc6, 0xb3, 0x22, 0x50, 0xc1, + 0xba, 0x2b, 0x59, 0xc8, 0xbd, 0x2c, 0x5e, 0xcf +}; + +u8_t bt_mesh_fcs_calc(const u8_t *data, u8_t data_len) +{ + u8_t fcs = 0xff; + + while (data_len--) { + fcs = crc_table[fcs ^ *data++]; + } + + BT_DBG("fcs 0x%02x", 0xff - fcs); + + return 0xff - fcs; +} + +bool bt_mesh_fcs_check(struct net_buf_simple *buf, u8_t received_fcs) +{ + const u8_t *data = buf->data; + u16_t data_len = buf->len; + u8_t fcs = 0xff; + + while (data_len--) { + fcs = crc_table[fcs ^ *data++]; + } + + return crc_table[fcs ^ received_fcs] == 0xcf; +} + +int bt_mesh_virtual_addr(const u8_t virtual_label[16], u16_t *addr) +{ + u8_t salt[16]; + u8_t tmp[16]; + int err; + + err = bt_mesh_s1("vtad", salt); + if (err) { + return err; + } + + err = bt_mesh_aes_cmac_one(salt, virtual_label, 16, tmp); + if (err) { + return err; + } + + *addr = (sys_get_be16(&tmp[14]) & 0x3fff) | 0x8000; + + return 0; +} + +int bt_mesh_prov_conf_salt(const u8_t conf_inputs[145], u8_t salt[16]) +{ + const u8_t conf_salt_key[16] = { 0 }; + + return bt_mesh_aes_cmac_one(conf_salt_key, conf_inputs, 145, salt); +} + +int bt_mesh_prov_conf_key(const u8_t dhkey[32], const u8_t conf_salt[16], + u8_t conf_key[16]) +{ + return bt_mesh_k1(dhkey, 32, conf_salt, "prck", conf_key); +} + +int bt_mesh_prov_conf(const u8_t conf_key[16], const u8_t rand[16], + const u8_t auth[16], u8_t conf[16]) +{ + struct bt_mesh_sg sg[] = { { rand, 16 }, { auth, 16 } }; + + BT_DBG("ConfirmationKey %s", bt_hex(conf_key, 16)); + BT_DBG("RandomDevice %s", bt_hex(rand, 16)); + BT_DBG("AuthValue %s", bt_hex(auth, 16)); + + return bt_mesh_aes_cmac(conf_key, sg, ARRAY_SIZE(sg), conf); +} + +int bt_mesh_prov_decrypt(const u8_t key[16], u8_t nonce[13], + const u8_t data[25 + 8], u8_t out[25]) +{ + return bt_mesh_ccm_decrypt(key, nonce, data, 25, NULL, 0, out, 8); +} + +#if CONFIG_BLE_MESH_PROVISIONER +int bt_mesh_prov_encrypt(const u8_t key[16], u8_t nonce[13], + const u8_t data[25], u8_t out[33]) +{ + return bt_mesh_ccm_encrypt(key, nonce, data, 25, NULL, 0, out, 8); +} +#endif + +int bt_mesh_beacon_auth(const u8_t beacon_key[16], u8_t flags, + const u8_t net_id[8], u32_t iv_index, + u8_t auth[8]) +{ + u8_t msg[13], tmp[16]; + int err; + + BT_DBG("BeaconKey %s", bt_hex(beacon_key, 16)); + BT_DBG("NetId %s", bt_hex(net_id, 8)); + BT_DBG("IV Index 0x%08x", iv_index); + + msg[0] = flags; + memcpy(&msg[1], net_id, 8); + sys_put_be32(iv_index, &msg[9]); + + BT_DBG("BeaconMsg %s", bt_hex(msg, sizeof(msg))); + + err = bt_mesh_aes_cmac_one(beacon_key, msg, sizeof(msg), tmp); + if (!err) { + memcpy(auth, tmp, 8); + } + + return err; +} diff --git a/components/bt/ble_mesh/mesh_core/crypto.h b/components/bt/ble_mesh/mesh_core/crypto.h new file mode 100644 index 0000000000..d8e9b1452e --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/crypto.h @@ -0,0 +1,166 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _CRYPTO_H_ +#define _CRYPTO_H_ + +#include "mesh_types.h" +#include + +struct bt_mesh_sg { + const void *data; + size_t len; +}; + +int bt_mesh_aes_cmac(const u8_t key[16], struct bt_mesh_sg *sg, + size_t sg_len, u8_t mac[16]); + +static inline int bt_mesh_aes_cmac_one(const u8_t key[16], const void *m, + size_t len, u8_t mac[16]) +{ + struct bt_mesh_sg sg = { m, len }; + + return bt_mesh_aes_cmac(key, &sg, 1, mac); +} + +static inline bool bt_mesh_s1(const char *m, u8_t salt[16]) +{ + const u8_t zero[16] = { 0 }; + + return bt_mesh_aes_cmac_one(zero, m, strlen(m), salt); +} + +int bt_mesh_k1(const u8_t *ikm, size_t ikm_len, const u8_t salt[16], + const char *info, u8_t okm[16]); + +#define bt_mesh_k1_str(ikm, ikm_len, salt_str, info, okm) \ +({ \ + const u8_t salt[16] = salt_str; \ + bt_mesh_k1(ikm, ikm_len, salt, info, okm); \ +}) + +int bt_mesh_k2(const u8_t n[16], const u8_t *p, size_t p_len, + u8_t net_id[1], u8_t enc_key[16], u8_t priv_key[16]); + +int bt_mesh_k3(const u8_t n[16], u8_t out[8]); + +int bt_mesh_k4(const u8_t n[16], u8_t out[1]); + +int bt_mesh_id128(const u8_t n[16], const char *s, u8_t out[16]); + +static inline int bt_mesh_id_resolving_key(const u8_t net_key[16], + u8_t resolving_key[16]) +{ + return bt_mesh_k1_str(net_key, 16, "smbt", "smbi", resolving_key); +} + +static inline int bt_mesh_identity_key(const u8_t net_key[16], + u8_t identity_key[16]) +{ + return bt_mesh_id128(net_key, "nkik", identity_key); +} + +static inline int bt_mesh_beacon_key(const u8_t net_key[16], + u8_t beacon_key[16]) +{ + return bt_mesh_id128(net_key, "nkbk", beacon_key); +} + +int bt_mesh_beacon_auth(const u8_t beacon_key[16], u8_t flags, + const u8_t net_id[16], u32_t iv_index, + u8_t auth[8]); + +static inline int bt_mesh_app_id(const u8_t app_key[16], u8_t app_id[1]) +{ + return bt_mesh_k4(app_key, app_id); +} + +static inline int bt_mesh_session_key(const u8_t dhkey[32], + const u8_t prov_salt[16], + u8_t session_key[16]) +{ + return bt_mesh_k1(dhkey, 32, prov_salt, "prsk", session_key); +} + +static inline int bt_mesh_prov_nonce(const u8_t dhkey[32], + const u8_t prov_salt[16], + u8_t nonce[13]) +{ + u8_t tmp[16]; + int err; + + err = bt_mesh_k1(dhkey, 32, prov_salt, "prsn", tmp); + if (!err) { + memcpy(nonce, tmp + 3, 13); + } + + return err; +} + +static inline int bt_mesh_dev_key(const u8_t dhkey[32], + const u8_t prov_salt[16], + u8_t dev_key[16]) +{ + return bt_mesh_k1(dhkey, 32, prov_salt, "prdk", dev_key); +} + +static inline int bt_mesh_prov_salt(const u8_t conf_salt[16], + const u8_t prov_rand[16], + const u8_t dev_rand[16], + u8_t prov_salt[16]) +{ + const u8_t prov_salt_key[16] = { 0 }; + struct bt_mesh_sg sg[] = { + { conf_salt, 16 }, + { prov_rand, 16 }, + { dev_rand, 16 }, + }; + + return bt_mesh_aes_cmac(prov_salt_key, sg, ARRAY_SIZE(sg), prov_salt); +} + +int bt_mesh_net_obfuscate(u8_t *pdu, u32_t iv_index, + const u8_t privacy_key[16]); + +int bt_mesh_net_encrypt(const u8_t key[16], struct net_buf_simple *buf, + u32_t iv_index, bool proxy); + +int bt_mesh_net_decrypt(const u8_t key[16], struct net_buf_simple *buf, + u32_t iv_index, bool proxy); + +int bt_mesh_app_encrypt(const u8_t key[16], bool dev_key, u8_t aszmic, + struct net_buf_simple *buf, const u8_t *ad, + u16_t src, u16_t dst, u32_t seq_num, u32_t iv_index); + +int bt_mesh_app_decrypt(const u8_t key[16], bool dev_key, u8_t aszmic, + struct net_buf_simple *buf, struct net_buf_simple *out, + const u8_t *ad, u16_t src, u16_t dst, u32_t seq_num, + u32_t iv_index); + +u8_t bt_mesh_fcs_calc(const u8_t *data, u8_t data_len); + +bool bt_mesh_fcs_check(struct net_buf_simple *buf, u8_t received_fcs); + +int bt_mesh_virtual_addr(const u8_t virtual_label[16], u16_t *addr); + +int bt_mesh_prov_conf_salt(const u8_t conf_inputs[145], u8_t salt[16]); + +int bt_mesh_prov_conf_key(const u8_t dhkey[32], const u8_t conf_salt[16], + u8_t conf_key[16]); + +int bt_mesh_prov_conf(const u8_t conf_key[16], const u8_t rand[16], + const u8_t auth[16], u8_t conf[16]); + +int bt_mesh_prov_decrypt(const u8_t key[16], u8_t nonce[13], + const u8_t data[25 + 8], u8_t out[25]); + +int bt_mesh_prov_encrypt(const u8_t key[16], u8_t nonce[13], + const u8_t data[25], u8_t out[33]); + +#endif /* _CRYPTO_H_ */ diff --git a/components/bt/ble_mesh/mesh_core/foundation.h b/components/bt/ble_mesh/mesh_core/foundation.h new file mode 100644 index 0000000000..d1ccd31ae4 --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/foundation.h @@ -0,0 +1,166 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef _FOUNDATION_H_ +#define _FOUNDATION_H_ + +#include "mesh_access.h" +#include "net.h" + +#define OP_APP_KEY_ADD BLE_MESH_MODEL_OP_1(0x00) +#define OP_APP_KEY_UPDATE BLE_MESH_MODEL_OP_1(0x01) +#define OP_DEV_COMP_DATA_STATUS BLE_MESH_MODEL_OP_1(0x02) +#define OP_MOD_PUB_SET BLE_MESH_MODEL_OP_1(0x03) +#define OP_HEALTH_CURRENT_STATUS BLE_MESH_MODEL_OP_1(0x04) +#define OP_HEALTH_FAULT_STATUS BLE_MESH_MODEL_OP_1(0x05) +#define OP_HEARTBEAT_PUB_STATUS BLE_MESH_MODEL_OP_1(0x06) +#define OP_APP_KEY_DEL BLE_MESH_MODEL_OP_2(0x80, 0x00) +#define OP_APP_KEY_GET BLE_MESH_MODEL_OP_2(0x80, 0x01) +#define OP_APP_KEY_LIST BLE_MESH_MODEL_OP_2(0x80, 0x02) +#define OP_APP_KEY_STATUS BLE_MESH_MODEL_OP_2(0x80, 0x03) +#define OP_ATTENTION_GET BLE_MESH_MODEL_OP_2(0x80, 0x04) +#define OP_ATTENTION_SET BLE_MESH_MODEL_OP_2(0x80, 0x05) +#define OP_ATTENTION_SET_UNREL BLE_MESH_MODEL_OP_2(0x80, 0x06) +#define OP_ATTENTION_STATUS BLE_MESH_MODEL_OP_2(0x80, 0x07) +#define OP_DEV_COMP_DATA_GET BLE_MESH_MODEL_OP_2(0x80, 0x08) +#define OP_BEACON_GET BLE_MESH_MODEL_OP_2(0x80, 0x09) +#define OP_BEACON_SET BLE_MESH_MODEL_OP_2(0x80, 0x0a) +#define OP_BEACON_STATUS BLE_MESH_MODEL_OP_2(0x80, 0x0b) +#define OP_DEFAULT_TTL_GET BLE_MESH_MODEL_OP_2(0x80, 0x0c) +#define OP_DEFAULT_TTL_SET BLE_MESH_MODEL_OP_2(0x80, 0x0d) +#define OP_DEFAULT_TTL_STATUS BLE_MESH_MODEL_OP_2(0x80, 0x0e) +#define OP_FRIEND_GET BLE_MESH_MODEL_OP_2(0x80, 0x0f) +#define OP_FRIEND_SET BLE_MESH_MODEL_OP_2(0x80, 0x10) +#define OP_FRIEND_STATUS BLE_MESH_MODEL_OP_2(0x80, 0x11) +#define OP_GATT_PROXY_GET BLE_MESH_MODEL_OP_2(0x80, 0x12) +#define OP_GATT_PROXY_SET BLE_MESH_MODEL_OP_2(0x80, 0x13) +#define OP_GATT_PROXY_STATUS BLE_MESH_MODEL_OP_2(0x80, 0x14) +#define OP_KRP_GET BLE_MESH_MODEL_OP_2(0x80, 0x15) +#define OP_KRP_SET BLE_MESH_MODEL_OP_2(0x80, 0x16) +#define OP_KRP_STATUS BLE_MESH_MODEL_OP_2(0x80, 0x17) +#define OP_MOD_PUB_GET BLE_MESH_MODEL_OP_2(0x80, 0x18) +#define OP_MOD_PUB_STATUS BLE_MESH_MODEL_OP_2(0x80, 0x19) +#define OP_MOD_PUB_VA_SET BLE_MESH_MODEL_OP_2(0x80, 0x1a) +#define OP_MOD_SUB_ADD BLE_MESH_MODEL_OP_2(0x80, 0x1b) +#define OP_MOD_SUB_DEL BLE_MESH_MODEL_OP_2(0x80, 0x1c) +#define OP_MOD_SUB_DEL_ALL BLE_MESH_MODEL_OP_2(0x80, 0x1d) +#define OP_MOD_SUB_OVERWRITE BLE_MESH_MODEL_OP_2(0x80, 0x1e) +#define OP_MOD_SUB_STATUS BLE_MESH_MODEL_OP_2(0x80, 0x1f) +#define OP_MOD_SUB_VA_ADD BLE_MESH_MODEL_OP_2(0x80, 0x20) +#define OP_MOD_SUB_VA_DEL BLE_MESH_MODEL_OP_2(0x80, 0x21) +#define OP_MOD_SUB_VA_OVERWRITE BLE_MESH_MODEL_OP_2(0x80, 0x22) +#define OP_NET_TRANSMIT_GET BLE_MESH_MODEL_OP_2(0x80, 0x23) +#define OP_NET_TRANSMIT_SET BLE_MESH_MODEL_OP_2(0x80, 0x24) +#define OP_NET_TRANSMIT_STATUS BLE_MESH_MODEL_OP_2(0x80, 0x25) +#define OP_RELAY_GET BLE_MESH_MODEL_OP_2(0x80, 0x26) +#define OP_RELAY_SET BLE_MESH_MODEL_OP_2(0x80, 0x27) +#define OP_RELAY_STATUS BLE_MESH_MODEL_OP_2(0x80, 0x28) +#define OP_MOD_SUB_GET BLE_MESH_MODEL_OP_2(0x80, 0x29) +#define OP_MOD_SUB_LIST BLE_MESH_MODEL_OP_2(0x80, 0x2a) +#define OP_MOD_SUB_GET_VND BLE_MESH_MODEL_OP_2(0x80, 0x2b) +#define OP_MOD_SUB_LIST_VND BLE_MESH_MODEL_OP_2(0x80, 0x2c) +#define OP_LPN_TIMEOUT_GET BLE_MESH_MODEL_OP_2(0x80, 0x2d) +#define OP_LPN_TIMEOUT_STATUS BLE_MESH_MODEL_OP_2(0x80, 0x2e) +#define OP_HEALTH_FAULT_CLEAR BLE_MESH_MODEL_OP_2(0x80, 0x2f) +#define OP_HEALTH_FAULT_CLEAR_UNREL BLE_MESH_MODEL_OP_2(0x80, 0x30) +#define OP_HEALTH_FAULT_GET BLE_MESH_MODEL_OP_2(0x80, 0x31) +#define OP_HEALTH_FAULT_TEST BLE_MESH_MODEL_OP_2(0x80, 0x32) +#define OP_HEALTH_FAULT_TEST_UNREL BLE_MESH_MODEL_OP_2(0x80, 0x33) +#define OP_HEALTH_PERIOD_GET BLE_MESH_MODEL_OP_2(0x80, 0x34) +#define OP_HEALTH_PERIOD_SET BLE_MESH_MODEL_OP_2(0x80, 0x35) +#define OP_HEALTH_PERIOD_SET_UNREL BLE_MESH_MODEL_OP_2(0x80, 0x36) +#define OP_HEALTH_PERIOD_STATUS BLE_MESH_MODEL_OP_2(0x80, 0x37) +#define OP_HEARTBEAT_PUB_GET BLE_MESH_MODEL_OP_2(0x80, 0x38) +#define OP_HEARTBEAT_PUB_SET BLE_MESH_MODEL_OP_2(0x80, 0x39) +#define OP_HEARTBEAT_SUB_GET BLE_MESH_MODEL_OP_2(0x80, 0x3a) +#define OP_HEARTBEAT_SUB_SET BLE_MESH_MODEL_OP_2(0x80, 0x3b) +#define OP_HEARTBEAT_SUB_STATUS BLE_MESH_MODEL_OP_2(0x80, 0x3c) +#define OP_MOD_APP_BIND BLE_MESH_MODEL_OP_2(0x80, 0x3d) +#define OP_MOD_APP_STATUS BLE_MESH_MODEL_OP_2(0x80, 0x3e) +#define OP_MOD_APP_UNBIND BLE_MESH_MODEL_OP_2(0x80, 0x3f) +#define OP_NET_KEY_ADD BLE_MESH_MODEL_OP_2(0x80, 0x40) +#define OP_NET_KEY_DEL BLE_MESH_MODEL_OP_2(0x80, 0x41) +#define OP_NET_KEY_GET BLE_MESH_MODEL_OP_2(0x80, 0x42) +#define OP_NET_KEY_LIST BLE_MESH_MODEL_OP_2(0x80, 0x43) +#define OP_NET_KEY_STATUS BLE_MESH_MODEL_OP_2(0x80, 0x44) +#define OP_NET_KEY_UPDATE BLE_MESH_MODEL_OP_2(0x80, 0x45) +#define OP_NODE_IDENTITY_GET BLE_MESH_MODEL_OP_2(0x80, 0x46) +#define OP_NODE_IDENTITY_SET BLE_MESH_MODEL_OP_2(0x80, 0x47) +#define OP_NODE_IDENTITY_STATUS BLE_MESH_MODEL_OP_2(0x80, 0x48) +#define OP_NODE_RESET BLE_MESH_MODEL_OP_2(0x80, 0x49) +#define OP_NODE_RESET_STATUS BLE_MESH_MODEL_OP_2(0x80, 0x4a) +#define OP_SIG_MOD_APP_GET BLE_MESH_MODEL_OP_2(0x80, 0x4b) +#define OP_SIG_MOD_APP_LIST BLE_MESH_MODEL_OP_2(0x80, 0x4c) +#define OP_VND_MOD_APP_GET BLE_MESH_MODEL_OP_2(0x80, 0x4d) +#define OP_VND_MOD_APP_LIST BLE_MESH_MODEL_OP_2(0x80, 0x4e) + +#define STATUS_SUCCESS 0x00 +#define STATUS_INVALID_ADDRESS 0x01 +#define STATUS_INVALID_MODEL 0x02 +#define STATUS_INVALID_APPKEY 0x03 +#define STATUS_INVALID_NETKEY 0x04 +#define STATUS_INSUFF_RESOURCES 0x05 +#define STATUS_IDX_ALREADY_STORED 0x06 +#define STATUS_NVAL_PUB_PARAM 0x07 +#define STATUS_NOT_SUB_MOD 0x08 +#define STATUS_STORAGE_FAIL 0x09 +#define STATUS_FEAT_NOT_SUPP 0x0a +#define STATUS_CANNOT_UPDATE 0x0b +#define STATUS_CANNOT_REMOVE 0x0c +#define STATUS_CANNOT_BIND 0x0d +#define STATUS_TEMP_STATE_CHG_FAIL 0x0e +#define STATUS_CANNOT_SET 0x0f +#define STATUS_UNSPECIFIED 0x10 +#define STATUS_INVALID_BINDING 0x11 + +int bt_mesh_cfg_srv_init(struct bt_mesh_model *model, bool primary); +int bt_mesh_health_srv_init(struct bt_mesh_model *model, bool primary); + +int bt_mesh_cfg_cli_init(struct bt_mesh_model *model, bool primary); +int bt_mesh_health_cli_init(struct bt_mesh_model *model, bool primary); + +void bt_mesh_cfg_reset(void); + +void bt_mesh_heartbeat(u16_t src, u16_t dst, u8_t hops, u16_t feat); + +void bt_mesh_attention(struct bt_mesh_model *model, u8_t time); + +u8_t *bt_mesh_label_uuid_get(u16_t addr); + +struct bt_mesh_hb_pub *bt_mesh_hb_pub_get(void); +void bt_mesh_hb_pub_disable(void); +struct bt_mesh_cfg_srv *bt_mesh_cfg_get(void); + +u8_t bt_mesh_net_transmit_get(void); +u8_t bt_mesh_relay_get(void); +u8_t bt_mesh_friend_get(void); +u8_t bt_mesh_relay_retransmit_get(void); +u8_t bt_mesh_beacon_get(void); +u8_t bt_mesh_gatt_proxy_get(void); +u8_t bt_mesh_default_ttl_get(void); + +void bt_mesh_subnet_del(struct bt_mesh_subnet *sub, bool store); + +struct bt_mesh_app_key *bt_mesh_app_key_alloc(u16_t app_idx); +void bt_mesh_app_key_del(struct bt_mesh_app_key *key, bool store); + +static inline void key_idx_pack(struct net_buf_simple *buf, + u16_t idx1, u16_t idx2) +{ + net_buf_simple_add_le16(buf, idx1 | ((idx2 & 0x00f) << 12)); + net_buf_simple_add_u8(buf, idx2 >> 4); +} + +static inline void key_idx_unpack(struct net_buf_simple *buf, + u16_t *idx1, u16_t *idx2) +{ + *idx1 = sys_get_le16(&buf->data[0]) & 0xfff; + *idx2 = sys_get_le16(&buf->data[1]) >> 4; + net_buf_simple_pull(buf, 3); +} + +#endif /* _FOUNDATION_H_ */ diff --git a/components/bt/ble_mesh/mesh_core/friend.c b/components/bt/ble_mesh/mesh_core/friend.c new file mode 100644 index 0000000000..b47e86fe81 --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/friend.c @@ -0,0 +1,1326 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include "sdkconfig.h" +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BLE_MESH_DEBUG_FRIEND) + +#include "mesh_buf.h" +#include "mesh_util.h" +#include "mesh_main.h" +#include "mesh_trace.h" + +#include "crypto.h" +#include "adv.h" +#include "mesh.h" +#include "net.h" +#include "transport.h" +#include "access.h" +#include "foundation.h" +#include "friend.h" + +#ifdef CONFIG_BLE_MESH_FRIEND + +#define FRIEND_BUF_SIZE (BLE_MESH_ADV_DATA_SIZE - BLE_MESH_NET_HDR_LEN) + +/* We reserve one extra buffer for each friendship, since we need to be able + * to resend the last sent PDU, which sits separately outside of the queue. + */ +#define FRIEND_BUF_COUNT ((CONFIG_BLE_MESH_FRIEND_QUEUE_SIZE + 1) * \ + CONFIG_BLE_MESH_FRIEND_LPN_COUNT) + +#define FRIEND_ADV(buf) CONTAINER_OF(BLE_MESH_ADV(buf), \ + struct friend_adv, adv) + +/* PDUs from Friend to the LPN should only be transmitted once with the + * smallest possible interval (20ms). + */ +#define FRIEND_XMIT BLE_MESH_TRANSMIT(0, 20) + +struct friend_pdu_info { + u16_t src; + u16_t dst; + + u8_t seq[3]; + + u8_t ttl: 7, + ctl: 1; + + u32_t iv_index; +}; + +NET_BUF_POOL_FIXED_DEFINE(friend_buf_pool, FRIEND_BUF_COUNT, + BLE_MESH_ADV_DATA_SIZE, NULL); + +static struct friend_adv { + struct bt_mesh_adv adv; + u64_t seq_auth; +} adv_pool[FRIEND_BUF_COUNT]; + +static struct bt_mesh_adv *adv_alloc(int id) +{ + return &adv_pool[id].adv; +} + +static void discard_buffer(void) +{ + struct bt_mesh_friend *frnd = &bt_mesh.frnd[0]; + struct net_buf *buf; + int i; + + /* Find the Friend context with the most queued buffers */ + for (i = 1; i < ARRAY_SIZE(bt_mesh.frnd); i++) { + if (bt_mesh.frnd[i].queue_size > frnd->queue_size) { + frnd = &bt_mesh.frnd[i]; + } + } + + buf = net_buf_slist_get(&frnd->queue); + __ASSERT_NO_MSG(buf != NULL); + BT_WARN("Discarding buffer %p for LPN 0x%04x", buf, frnd->lpn); + net_buf_unref(buf); +} + +static struct net_buf *friend_buf_alloc(u16_t src) +{ + struct net_buf *buf; + + BT_DBG("src 0x%04x", src); + + do { + buf = bt_mesh_adv_create_from_pool(&friend_buf_pool, adv_alloc, + BLE_MESH_ADV_DATA, + FRIEND_XMIT, K_NO_WAIT); + if (!buf) { + discard_buffer(); + } + } while (!buf); + + BLE_MESH_ADV(buf)->addr = src; + FRIEND_ADV(buf)->seq_auth = TRANS_SEQ_AUTH_NVAL; + + BT_DBG("allocated buf %p", buf); + + return buf; +} + +static bool is_lpn_unicast(struct bt_mesh_friend *frnd, u16_t addr) +{ + if (frnd->lpn == BLE_MESH_ADDR_UNASSIGNED) { + return false; + } + + return (addr >= frnd->lpn && addr < (frnd->lpn + frnd->num_elem)); +} + +struct bt_mesh_friend *bt_mesh_friend_find(u16_t net_idx, u16_t lpn_addr, + bool valid, bool established) +{ + int i; + + BT_DBG("net_idx 0x%04x lpn_addr 0x%04x", net_idx, lpn_addr); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) { + struct bt_mesh_friend *frnd = &bt_mesh.frnd[i]; + + if (valid && !frnd->valid) { + continue; + } + + if (established && !frnd->established) { + continue; + } + + if (net_idx != BLE_MESH_KEY_ANY && frnd->net_idx != net_idx) { + continue; + } + + if (is_lpn_unicast(frnd, lpn_addr)) { + return frnd; + } + } + + return NULL; +} + +/* Intentionally start a little bit late into the ReceiveWindow when + * it's large enough. This may improve reliability with some platforms, + * like the PTS, where the receiver might not have sufficiently compensated + * for internal latencies required to start scanning. + */ +static s32_t recv_delay(struct bt_mesh_friend *frnd) +{ +#if CONFIG_BLE_MESH_FRIEND_RECV_WIN > 50 + return (s32_t)frnd->recv_delay + (CONFIG_BLE_MESH_FRIEND_RECV_WIN / 5); +#else + return frnd->recv_delay; +#endif +} + +static void friend_clear(struct bt_mesh_friend *frnd) +{ + int i; + + BT_DBG("LPN 0x%04x", frnd->lpn); + + k_delayed_work_cancel(&frnd->timer); + + friend_cred_del(frnd->net_idx, frnd->lpn); + + if (frnd->last) { + /* Cancel the sending if necessary */ + if (frnd->pending_buf) { + BLE_MESH_ADV(frnd->last)->busy = 0U; + } + + net_buf_unref(frnd->last); + frnd->last = NULL; + } + + while (!sys_slist_is_empty(&frnd->queue)) { + net_buf_unref(net_buf_slist_get(&frnd->queue)); + } + + for (i = 0; i < ARRAY_SIZE(frnd->seg); i++) { + struct bt_mesh_friend_seg *seg = &frnd->seg[i]; + + while (!sys_slist_is_empty(&seg->queue)) { + net_buf_unref(net_buf_slist_get(&seg->queue)); + } + } + + frnd->valid = 0U; + frnd->established = 0U; + frnd->pending_buf = 0U; + frnd->fsn = 0U; + frnd->queue_size = 0U; + frnd->pending_req = 0U; + (void)memset(frnd->sub_list, 0, sizeof(frnd->sub_list)); +} + +void bt_mesh_friend_clear_net_idx(u16_t net_idx) +{ + int i; + + BT_DBG("net_idx 0x%04x", net_idx); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) { + struct bt_mesh_friend *frnd = &bt_mesh.frnd[i]; + + if (frnd->net_idx == BLE_MESH_KEY_UNUSED) { + continue; + } + + if (net_idx == BLE_MESH_KEY_ANY || frnd->net_idx == net_idx) { + friend_clear(frnd); + } + } +} + +void bt_mesh_friend_sec_update(u16_t net_idx) +{ + int i; + + BT_DBG("net_idx 0x%04x", net_idx); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) { + struct bt_mesh_friend *frnd = &bt_mesh.frnd[i]; + + if (frnd->net_idx == BLE_MESH_KEY_UNUSED) { + continue; + } + + if (net_idx == BLE_MESH_KEY_ANY || frnd->net_idx == net_idx) { + frnd->sec_update = 1U; + } + } +} + +int bt_mesh_friend_clear(struct bt_mesh_net_rx *rx, struct net_buf_simple *buf) +{ + struct bt_mesh_ctl_friend_clear *msg = (void *)buf->data; + struct bt_mesh_friend *frnd; + u16_t lpn_addr, lpn_counter; + struct bt_mesh_net_tx tx = { + .sub = rx->sub, + .ctx = &rx->ctx, + .src = bt_mesh_primary_addr(), + .xmit = bt_mesh_net_transmit_get(), + }; + struct bt_mesh_ctl_friend_clear_confirm cfm; + + if (buf->len < sizeof(*msg)) { + BT_WARN("%s, Too short Friend Clear", __func__); + return -EINVAL; + } + + lpn_addr = sys_be16_to_cpu(msg->lpn_addr); + lpn_counter = sys_be16_to_cpu(msg->lpn_counter); + + BT_DBG("LPN addr 0x%04x counter 0x%04x", lpn_addr, lpn_counter); + + frnd = bt_mesh_friend_find(rx->sub->net_idx, lpn_addr, false, false); + if (!frnd) { + BT_WARN("%s, No matching LPN addr 0x%04x", __func__, lpn_addr); + return 0; + } + + /* A Friend Clear message is considered valid if the result of the + * subtraction of the value of the LPNCounter field of the Friend + * Request message (the one that initiated the friendship) from the + * value of the LPNCounter field of the Friend Clear message, modulo + * 65536, is in the range 0 to 255 inclusive. + */ + if (lpn_counter - frnd->lpn_counter > 255) { + BT_WARN("%s, LPN Counter out of range (old %u new %u)", + __func__, frnd->lpn_counter, lpn_counter); + return 0; + } + + tx.ctx->send_ttl = BLE_MESH_TTL_MAX; + + cfm.lpn_addr = msg->lpn_addr; + cfm.lpn_counter = msg->lpn_counter; + + bt_mesh_ctl_send(&tx, TRANS_CTL_OP_FRIEND_CLEAR_CFM, &cfm, + sizeof(cfm), NULL, NULL, NULL); + + friend_clear(frnd); + + return 0; +} + +static void friend_sub_add(struct bt_mesh_friend *frnd, u16_t addr) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(frnd->sub_list); i++) { + if (frnd->sub_list[i] == BLE_MESH_ADDR_UNASSIGNED) { + frnd->sub_list[i] = addr; + return; + } + } + + BT_WARN("%s, No space in friend subscription list", __func__); +} + +static void friend_sub_rem(struct bt_mesh_friend *frnd, u16_t addr) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(frnd->sub_list); i++) { + if (frnd->sub_list[i] == addr) { + frnd->sub_list[i] = BLE_MESH_ADDR_UNASSIGNED; + return; + } + } +} + +static struct net_buf *create_friend_pdu(struct bt_mesh_friend *frnd, + struct friend_pdu_info *info, + struct net_buf_simple *sdu) +{ + struct bt_mesh_subnet *sub; + const u8_t *enc, *priv; + struct net_buf *buf; + u8_t nid; + + sub = bt_mesh_subnet_get(frnd->net_idx); + __ASSERT_NO_MSG(sub != NULL); + + buf = friend_buf_alloc(info->src); + + /* Friend Offer needs master security credentials */ + if (info->ctl && TRANS_CTL_OP(sdu->data) == TRANS_CTL_OP_FRIEND_OFFER) { + enc = sub->keys[sub->kr_flag].enc; + priv = sub->keys[sub->kr_flag].privacy; + nid = sub->keys[sub->kr_flag].nid; + } else { + if (friend_cred_get(sub, frnd->lpn, &nid, &enc, &priv)) { + BT_ERR("%s, friend_cred_get failed", __func__); + goto failed; + } + } + + net_buf_add_u8(buf, (nid | (info->iv_index & 1) << 7)); + + if (info->ctl) { + net_buf_add_u8(buf, info->ttl | 0x80); + } else { + net_buf_add_u8(buf, info->ttl); + } + + net_buf_add_mem(buf, info->seq, sizeof(info->seq)); + + net_buf_add_be16(buf, info->src); + net_buf_add_be16(buf, info->dst); + + net_buf_add_mem(buf, sdu->data, sdu->len); + + /* We re-encrypt and obfuscate using the received IVI rather than + * the normal TX IVI (which may be different) since the transport + * layer nonce includes the IVI. + */ + if (bt_mesh_net_encrypt(enc, &buf->b, info->iv_index, false)) { + BT_ERR("%s, Re-encrypting failed", __func__); + goto failed; + } + + if (bt_mesh_net_obfuscate(buf->data, info->iv_index, priv)) { + BT_ERR("%s, Re-obfuscating failed", __func__); + goto failed; + } + + return buf; + +failed: + net_buf_unref(buf); + return NULL; +} + +static struct net_buf *encode_friend_ctl(struct bt_mesh_friend *frnd, + u8_t ctl_op, + struct net_buf_simple *sdu) +{ + struct friend_pdu_info info; + u32_t seq; + + BT_DBG("LPN 0x%04x", frnd->lpn); + + net_buf_simple_push_u8(sdu, TRANS_CTL_HDR(ctl_op, 0)); + + info.src = bt_mesh_primary_addr(); + info.dst = frnd->lpn; + + info.ctl = 1U; + info.ttl = 0U; + + seq = bt_mesh_next_seq(); + info.seq[0] = seq >> 16; + info.seq[1] = seq >> 8; + info.seq[2] = seq; + + info.iv_index = BLE_MESH_NET_IVI_TX; + + return create_friend_pdu(frnd, &info, sdu); +} + +static struct net_buf *encode_update(struct bt_mesh_friend *frnd, u8_t md) +{ + struct bt_mesh_ctl_friend_update *upd; + NET_BUF_SIMPLE_DEFINE(sdu, 1 + sizeof(*upd)); + struct bt_mesh_subnet *sub = bt_mesh_subnet_get(frnd->net_idx); + + __ASSERT_NO_MSG(sub != NULL); + + BT_DBG("lpn 0x%04x md 0x%02x", frnd->lpn, md); + + net_buf_simple_reserve(&sdu, 1); + + upd = net_buf_simple_add(&sdu, sizeof(*upd)); + upd->flags = bt_mesh_net_flags(sub); + upd->iv_index = sys_cpu_to_be32(bt_mesh.iv_index); + upd->md = md; + + return encode_friend_ctl(frnd, TRANS_CTL_OP_FRIEND_UPDATE, &sdu); +} + +static void enqueue_sub_cfm(struct bt_mesh_friend *frnd, u8_t xact) +{ + struct bt_mesh_ctl_friend_sub_confirm *cfm; + NET_BUF_SIMPLE_DEFINE(sdu, 1 + sizeof(*cfm)); + struct net_buf *buf; + + BT_DBG("lpn 0x%04x xact 0x%02x", frnd->lpn, xact); + + net_buf_simple_reserve(&sdu, 1); + + cfm = net_buf_simple_add(&sdu, sizeof(*cfm)); + cfm->xact = xact; + + buf = encode_friend_ctl(frnd, TRANS_CTL_OP_FRIEND_SUB_CFM, &sdu); + if (!buf) { + BT_ERR("%s, Unable to encode Subscription List Confirmation", __func__); + return; + } + + if (frnd->last) { + BT_DBG("Discarding last PDU"); + net_buf_unref(frnd->last); + } + + frnd->last = buf; + frnd->send_last = 1U; +} + +static void friend_recv_delay(struct bt_mesh_friend *frnd) +{ + frnd->pending_req = 1U; + k_delayed_work_submit(&frnd->timer, recv_delay(frnd)); + BT_DBG("Waiting RecvDelay of %d ms", recv_delay(frnd)); +} + +int bt_mesh_friend_sub_add(struct bt_mesh_net_rx *rx, + struct net_buf_simple *buf) +{ + struct bt_mesh_friend *frnd; + u8_t xact; + + if (buf->len < BLE_MESH_FRIEND_SUB_MIN_LEN) { + BT_WARN("%s, Too short Friend Subscription Add", __func__); + return -EINVAL; + } + + frnd = bt_mesh_friend_find(rx->sub->net_idx, rx->ctx.addr, true, true); + if (!frnd) { + BT_WARN("%s, No matching LPN addr 0x%04x", __func__, rx->ctx.addr); + return 0; + } + + if (frnd->pending_buf) { + BT_WARN("%s, Previous buffer not yet sent!", __func__); + return 0; + } + + friend_recv_delay(frnd); + + xact = net_buf_simple_pull_u8(buf); + + while (buf->len >= 2U) { + friend_sub_add(frnd, net_buf_simple_pull_be16(buf)); + } + + enqueue_sub_cfm(frnd, xact); + + return 0; +} + +int bt_mesh_friend_sub_rem(struct bt_mesh_net_rx *rx, + struct net_buf_simple *buf) +{ + struct bt_mesh_friend *frnd; + u8_t xact; + + if (buf->len < BLE_MESH_FRIEND_SUB_MIN_LEN) { + BT_WARN("%s, Too short Friend Subscription Remove", __func__); + return -EINVAL; + } + + frnd = bt_mesh_friend_find(rx->sub->net_idx, rx->ctx.addr, true, true); + if (!frnd) { + BT_WARN("%s, No matching LPN addr 0x%04x", __func__, rx->ctx.addr); + return 0; + } + + if (frnd->pending_buf) { + BT_WARN("%s, Previous buffer not yet sent!", __func__); + return 0; + } + + friend_recv_delay(frnd); + + xact = net_buf_simple_pull_u8(buf); + + while (buf->len >= 2U) { + friend_sub_rem(frnd, net_buf_simple_pull_be16(buf)); + } + + enqueue_sub_cfm(frnd, xact); + + return 0; +} + +static void enqueue_buf(struct bt_mesh_friend *frnd, struct net_buf *buf) +{ + net_buf_slist_put(&frnd->queue, buf); + frnd->queue_size++; +} + +static void enqueue_update(struct bt_mesh_friend *frnd, u8_t md) +{ + struct net_buf *buf; + + buf = encode_update(frnd, md); + if (!buf) { + BT_ERR("%s, Unable to encode Friend Update", __func__); + return; + } + + frnd->sec_update = 0U; + enqueue_buf(frnd, buf); +} + +int bt_mesh_friend_poll(struct bt_mesh_net_rx *rx, struct net_buf_simple *buf) +{ + struct bt_mesh_ctl_friend_poll *msg = (void *)buf->data; + struct bt_mesh_friend *frnd; + + if (buf->len < sizeof(*msg)) { + BT_WARN("%s, Too short Friend Poll", __func__); + return -EINVAL; + } + + frnd = bt_mesh_friend_find(rx->sub->net_idx, rx->ctx.addr, true, false); + if (!frnd) { + BT_WARN("%s, No matching LPN addr 0x%04x", __func__, rx->ctx.addr); + return 0; + } + + if (msg->fsn & ~1) { + BT_WARN("%s, Prohibited (non-zero) padding bits", __func__); + return -EINVAL; + } + + if (frnd->pending_buf) { + BT_WARN("%s, Previous buffer not yet sent!", __func__); + return 0; + } + + BT_DBG("msg->fsn %u frnd->fsn %u", (msg->fsn & 1), frnd->fsn); + + friend_recv_delay(frnd); + + if (!frnd->established) { + BT_DBG("Friendship established with 0x%04x", frnd->lpn); + frnd->established = 1U; + } + + if (msg->fsn == frnd->fsn && frnd->last) { + BT_DBG("Re-sending last PDU"); + frnd->send_last = 1U; + } else { + if (frnd->last) { + net_buf_unref(frnd->last); + frnd->last = NULL; + } + + frnd->fsn = msg->fsn; + + if (sys_slist_is_empty(&frnd->queue)) { + enqueue_update(frnd, 0); + BT_DBG("Enqueued Friend Update to empty queue"); + } + } + + return 0; +} + +static struct bt_mesh_friend *find_clear(u16_t prev_friend) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) { + struct bt_mesh_friend *frnd = &bt_mesh.frnd[i]; + + if (frnd->clear.frnd == prev_friend) { + return frnd; + } + } + + return NULL; +} + +static void friend_clear_sent(int err, void *user_data) +{ + struct bt_mesh_friend *frnd = user_data; + + k_delayed_work_submit(&frnd->clear.timer, + K_SECONDS(frnd->clear.repeat_sec)); + frnd->clear.repeat_sec *= 2U; +} + +static const struct bt_mesh_send_cb clear_sent_cb = { + .end = friend_clear_sent, +}; + +static void send_friend_clear(struct bt_mesh_friend *frnd) +{ + struct bt_mesh_msg_ctx ctx = { + .net_idx = frnd->net_idx, + .app_idx = BLE_MESH_KEY_UNUSED, + .addr = frnd->clear.frnd, + .send_ttl = BLE_MESH_TTL_MAX, + }; + struct bt_mesh_net_tx tx = { + .sub = &bt_mesh.sub[0], + .ctx = &ctx, + .src = bt_mesh_primary_addr(), + .xmit = bt_mesh_net_transmit_get(), + }; + struct bt_mesh_ctl_friend_clear req = { + .lpn_addr = sys_cpu_to_be16(frnd->lpn), + .lpn_counter = sys_cpu_to_be16(frnd->lpn_counter), + }; + + BT_DBG("%s", __func__); + + bt_mesh_ctl_send(&tx, TRANS_CTL_OP_FRIEND_CLEAR, &req, + sizeof(req), NULL, &clear_sent_cb, frnd); +} + +static void clear_timeout(struct k_work *work) +{ + struct bt_mesh_friend *frnd = CONTAINER_OF(work, struct bt_mesh_friend, + clear.timer.work); + u32_t duration; + + BT_DBG("LPN 0x%04x (old) Friend 0x%04x", frnd->lpn, frnd->clear.frnd); + + duration = k_uptime_get_32() - frnd->clear.start; + if (duration > 2 * frnd->poll_to) { + BT_DBG("Clear Procedure timer expired"); + frnd->clear.frnd = BLE_MESH_ADDR_UNASSIGNED; + return; + } + + send_friend_clear(frnd); +} + +static void clear_procedure_start(struct bt_mesh_friend *frnd) +{ + BT_DBG("LPN 0x%04x (old) Friend 0x%04x", frnd->lpn, frnd->clear.frnd); + + frnd->clear.start = k_uptime_get_32() + (2 * frnd->poll_to); + frnd->clear.repeat_sec = 1U; + + send_friend_clear(frnd); +} + +int bt_mesh_friend_clear_cfm(struct bt_mesh_net_rx *rx, + struct net_buf_simple *buf) +{ + struct bt_mesh_ctl_friend_clear_confirm *msg = (void *)buf->data; + struct bt_mesh_friend *frnd; + u16_t lpn_addr, lpn_counter; + + BT_DBG("%s", __func__); + + if (buf->len < sizeof(*msg)) { + BT_WARN("%s, Too short Friend Clear Confirm", __func__); + return -EINVAL; + } + + frnd = find_clear(rx->ctx.addr); + if (!frnd) { + BT_WARN("%s, No pending clear procedure for 0x%02x", __func__, rx->ctx.addr); + return 0; + } + + lpn_addr = sys_be16_to_cpu(msg->lpn_addr); + if (lpn_addr != frnd->lpn) { + BT_WARN("%s, LPN address mismatch (0x%04x != 0x%04x)", + __func__, lpn_addr, frnd->lpn); + return 0; + } + + lpn_counter = sys_be16_to_cpu(msg->lpn_counter); + if (lpn_counter != frnd->lpn_counter) { + BT_WARN("%s, LPN counter mismatch (0x%04x != 0x%04x)", + __func__, lpn_counter, frnd->lpn_counter); + return 0; + } + + k_delayed_work_cancel(&frnd->clear.timer); + frnd->clear.frnd = BLE_MESH_ADDR_UNASSIGNED; + + return 0; +} + +static void enqueue_offer(struct bt_mesh_friend *frnd, s8_t rssi) +{ + struct bt_mesh_ctl_friend_offer *off; + NET_BUF_SIMPLE_DEFINE(sdu, 1 + sizeof(*off)); + struct net_buf *buf; + + BT_DBG("%s", __func__); + + net_buf_simple_reserve(&sdu, 1); + + off = net_buf_simple_add(&sdu, sizeof(*off)); + + off->recv_win = CONFIG_BLE_MESH_FRIEND_RECV_WIN, + off->queue_size = CONFIG_BLE_MESH_FRIEND_QUEUE_SIZE, + off->sub_list_size = ARRAY_SIZE(frnd->sub_list), + off->rssi = rssi, + off->frnd_counter = sys_cpu_to_be16(frnd->counter); + + buf = encode_friend_ctl(frnd, TRANS_CTL_OP_FRIEND_OFFER, &sdu); + if (!buf) { + BT_ERR("%s, Unable to encode Friend Offer", __func__); + return; + } + + frnd->counter++; + + if (frnd->last) { + net_buf_unref(frnd->last); + } + + frnd->last = buf; + frnd->send_last = 1U; +} + +#define RECV_WIN CONFIG_BLE_MESH_FRIEND_RECV_WIN +#define RSSI_FACT(crit) (((crit) >> 5) & (u8_t)BIT_MASK(2)) +#define RECV_WIN_FACT(crit) (((crit) >> 3) & (u8_t)BIT_MASK(2)) +#define MIN_QUEUE_SIZE_LOG(crit) ((crit) & (u8_t)BIT_MASK(3)) +#define MIN_QUEUE_SIZE(crit) ((u32_t)BIT(MIN_QUEUE_SIZE_LOG(crit))) + +static s32_t offer_delay(struct bt_mesh_friend *frnd, s8_t rssi, u8_t crit) +{ + /* Scaling factors. The actual values are 1, 1.5, 2 & 2.5, but we + * want to avoid floating-point arithmetic. + */ + static const u8_t fact[] = { 10, 15, 20, 25 }; + s32_t delay; + + BT_DBG("ReceiveWindowFactor %u ReceiveWindow %u RSSIFactor %u RSSI %d", + fact[RECV_WIN_FACT(crit)], RECV_WIN, + fact[RSSI_FACT(crit)], rssi); + + /* Delay = ReceiveWindowFactor * ReceiveWindow - RSSIFactor * RSSI */ + delay = (s32_t)fact[RECV_WIN_FACT(crit)] * RECV_WIN; + delay -= (s32_t)fact[RSSI_FACT(crit)] * rssi; + delay /= 10; + + BT_DBG("Local Delay calculated as %d ms", delay); + + if (delay < 100) { + return K_MSEC(100); + } + + return K_MSEC(delay); +} + +int bt_mesh_friend_req(struct bt_mesh_net_rx *rx, struct net_buf_simple *buf) +{ + struct bt_mesh_ctl_friend_req *msg = (void *)buf->data; + struct bt_mesh_friend *frnd = NULL; + u32_t poll_to; + int i; + + if (buf->len < sizeof(*msg)) { + BT_WARN("%s, Too short Friend Request", __func__); + return -EINVAL; + } + + if (msg->recv_delay <= 0x09) { + BT_WARN("%s, Prohibited ReceiveDelay (0x%02x)", __func__, msg->recv_delay); + return -EINVAL; + } + + poll_to = (((u32_t)msg->poll_to[0] << 16) | + ((u32_t)msg->poll_to[1] << 8) | + ((u32_t)msg->poll_to[2])); + + if (poll_to <= 0x000009 || poll_to >= 0x34bc00) { + BT_WARN("%s, Prohibited PollTimeout (0x%06x)", __func__, poll_to); + return -EINVAL; + } + + if (msg->num_elem == 0x00) { + BT_WARN("%s, Prohibited NumElements value (0x00)", __func__); + return -EINVAL; + } + + if (!BLE_MESH_ADDR_IS_UNICAST(rx->ctx.addr + msg->num_elem - 1)) { + BT_WARN("%s, LPN elements stretch outside of unicast range", __func__); + return -EINVAL; + } + + if (!MIN_QUEUE_SIZE_LOG(msg->criteria)) { + BT_WARN("%s, Prohibited Minimum Queue Size in Friend Request", __func__); + return -EINVAL; + } + + if (CONFIG_BLE_MESH_FRIEND_QUEUE_SIZE < MIN_QUEUE_SIZE(msg->criteria)) { + BT_WARN("%s, We have a too small Friend Queue size (%u < %u)", + __func__, CONFIG_BLE_MESH_FRIEND_QUEUE_SIZE, + MIN_QUEUE_SIZE(msg->criteria)); + return 0; + } + + frnd = bt_mesh_friend_find(rx->sub->net_idx, rx->ctx.addr, true, false); + if (frnd) { + BT_WARN("%s, Existing LPN re-requesting Friendship", __func__); + friend_clear(frnd); + goto init_friend; + } + + for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) { + if (!bt_mesh.frnd[i].valid) { + frnd = &bt_mesh.frnd[i]; + frnd->valid = 1U; + break; + } + } + + if (!frnd) { + BT_WARN("%s, No free Friend contexts for new LPN", __func__); + return -ENOMEM; + } + +init_friend: + frnd->lpn = rx->ctx.addr; + frnd->num_elem = msg->num_elem; + frnd->net_idx = rx->sub->net_idx; + frnd->recv_delay = msg->recv_delay; + frnd->poll_to = poll_to * 100U; + frnd->lpn_counter = sys_be16_to_cpu(msg->lpn_counter); + frnd->clear.frnd = sys_be16_to_cpu(msg->prev_addr); + + BT_DBG("LPN 0x%04x rssi %d recv_delay %u poll_to %ums", + frnd->lpn, rx->rssi, frnd->recv_delay, frnd->poll_to); + + if (BLE_MESH_ADDR_IS_UNICAST(frnd->clear.frnd) && + !bt_mesh_elem_find(frnd->clear.frnd)) { + clear_procedure_start(frnd); + } + + k_delayed_work_submit(&frnd->timer, + offer_delay(frnd, rx->rssi, msg->criteria)); + + friend_cred_create(rx->sub, frnd->lpn, frnd->lpn_counter, + frnd->counter); + + enqueue_offer(frnd, rx->rssi); + + return 0; +} + +static struct bt_mesh_friend_seg *get_seg(struct bt_mesh_friend *frnd, + u16_t src, u64_t *seq_auth) +{ + struct bt_mesh_friend_seg *unassigned = NULL; + int i; + + for (i = 0; i < ARRAY_SIZE(frnd->seg); i++) { + struct bt_mesh_friend_seg *seg = &frnd->seg[i]; + struct net_buf *buf = (void *)sys_slist_peek_head(&seg->queue); + + if (buf && BLE_MESH_ADV(buf)->addr == src && + FRIEND_ADV(buf)->seq_auth == *seq_auth) { + return seg; + } + + if (!unassigned && !buf) { + unassigned = seg; + } + } + + return unassigned; +} + +static void enqueue_friend_pdu(struct bt_mesh_friend *frnd, + enum bt_mesh_friend_pdu_type type, + struct net_buf *buf) +{ + struct bt_mesh_friend_seg *seg; + struct friend_adv *adv; + + BT_DBG("type %u", type); + + if (type == BLE_MESH_FRIEND_PDU_SINGLE) { + if (frnd->sec_update) { + enqueue_update(frnd, 1); + } + + enqueue_buf(frnd, buf); + return; + } + + adv = FRIEND_ADV(buf); + seg = get_seg(frnd, BLE_MESH_ADV(buf)->addr, &adv->seq_auth); + if (!seg) { + BT_ERR("%s, No free friend segment RX contexts for 0x%04x", + __func__, BLE_MESH_ADV(buf)->addr); + net_buf_unref(buf); + return; + } + + net_buf_slist_put(&seg->queue, buf); + + if (type == BLE_MESH_FRIEND_PDU_COMPLETE) { + if (frnd->sec_update) { + enqueue_update(frnd, 1); + } + + /* Only acks should have a valid SeqAuth in the Friend queue + * (otherwise we can't easily detect them there), so clear + * the SeqAuth information from the segments before merging. + */ + SYS_SLIST_FOR_EACH_CONTAINER(&seg->queue, buf, node) { + FRIEND_ADV(buf)->seq_auth = TRANS_SEQ_AUTH_NVAL; + frnd->queue_size++; + } + + sys_slist_merge_slist(&frnd->queue, &seg->queue); + } +} + +static void buf_send_start(u16_t duration, int err, void *user_data) +{ + struct bt_mesh_friend *frnd = user_data; + + BT_DBG("err %d", err); + + frnd->pending_buf = 0U; + + /* Friend Offer doesn't follow the re-sending semantics */ + if (!frnd->established) { + net_buf_unref(frnd->last); + frnd->last = NULL; + } +} + +static void buf_send_end(int err, void *user_data) +{ + struct bt_mesh_friend *frnd = user_data; + + BT_DBG("err %d", err); + + if (frnd->pending_req) { + BT_WARN("Another request before previous completed sending"); + return; + } + + if (frnd->established) { + k_delayed_work_submit(&frnd->timer, frnd->poll_to); + BT_DBG("Waiting %u ms for next poll", frnd->poll_to); + } else { + /* Friend offer timeout is 1 second */ + k_delayed_work_submit(&frnd->timer, K_SECONDS(1)); + BT_DBG("Waiting for first poll"); + } +} + +static void friend_timeout(struct k_work *work) +{ + struct bt_mesh_friend *frnd = CONTAINER_OF(work, struct bt_mesh_friend, + timer.work); + static const struct bt_mesh_send_cb buf_sent_cb = { + .start = buf_send_start, + .end = buf_send_end, + }; + + __ASSERT_NO_MSG(frnd->pending_buf == 0U); + + BT_DBG("lpn 0x%04x send_last %u last %p", frnd->lpn, + frnd->send_last, frnd->last); + + if (frnd->send_last && frnd->last) { + BT_DBG("Sending frnd->last %p", frnd->last); + frnd->send_last = 0U; + goto send_last; + } + + if (frnd->established && !frnd->pending_req) { + BT_WARN("%s, Friendship lost with 0x%04x", __func__, frnd->lpn); + friend_clear(frnd); + return; + } + + frnd->last = net_buf_slist_get(&frnd->queue); + if (!frnd->last) { + BT_WARN("%s, Friendship not established with 0x%04x", __func__, frnd->lpn); + friend_clear(frnd); + return; + } + + BT_DBG("Sending buf %p from Friend Queue of LPN 0x%04x", + frnd->last, frnd->lpn); + frnd->queue_size--; + +send_last: + frnd->pending_req = 0U; + frnd->pending_buf = 1U; + bt_mesh_adv_send(frnd->last, &buf_sent_cb, frnd); +} + +int bt_mesh_friend_init(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) { + struct bt_mesh_friend *frnd = &bt_mesh.frnd[i]; + int j; + + frnd->net_idx = BLE_MESH_KEY_UNUSED; + + sys_slist_init(&frnd->queue); + + k_delayed_work_init(&frnd->timer, friend_timeout); + k_delayed_work_init(&frnd->clear.timer, clear_timeout); + + for (j = 0; j < ARRAY_SIZE(frnd->seg); j++) { + sys_slist_init(&frnd->seg[j].queue); + } + } + + return 0; +} + +static void friend_purge_old_ack(struct bt_mesh_friend *frnd, u64_t *seq_auth, + u16_t src) +{ + sys_snode_t *cur, *prev = NULL; + + BT_DBG("SeqAuth %llx src 0x%04x", *seq_auth, src); + + for (cur = sys_slist_peek_head(&frnd->queue); + cur != NULL; prev = cur, cur = sys_slist_peek_next(cur)) { + struct net_buf *buf = (void *)cur; + + if (BLE_MESH_ADV(buf)->addr == src && + FRIEND_ADV(buf)->seq_auth == *seq_auth) { + BT_DBG("Removing old ack from Friend Queue"); + + sys_slist_remove(&frnd->queue, prev, cur); + frnd->queue_size--; + /* Make sure old slist entry state doesn't remain */ + buf->frags = NULL; + + net_buf_unref(buf); + break; + } + } +} + +static void friend_lpn_enqueue_rx(struct bt_mesh_friend *frnd, + struct bt_mesh_net_rx *rx, + enum bt_mesh_friend_pdu_type type, + u64_t *seq_auth, struct net_buf_simple *sbuf) +{ + struct friend_pdu_info info; + struct net_buf *buf; + + BT_DBG("LPN 0x%04x queue_size %u", frnd->lpn, frnd->queue_size); + + if (type == BLE_MESH_FRIEND_PDU_SINGLE && seq_auth) { + friend_purge_old_ack(frnd, seq_auth, rx->ctx.addr); + } + + info.src = rx->ctx.addr; + info.dst = rx->ctx.recv_dst; + + if (rx->net_if == BLE_MESH_NET_IF_LOCAL) { + info.ttl = rx->ctx.recv_ttl; + } else { + info.ttl = rx->ctx.recv_ttl - 1U; + } + + info.ctl = rx->ctl; + + info.seq[0] = (rx->seq >> 16); + info.seq[1] = (rx->seq >> 8); + info.seq[2] = rx->seq; + + info.iv_index = BLE_MESH_NET_IVI_RX(rx); + + buf = create_friend_pdu(frnd, &info, sbuf); + if (!buf) { + BT_ERR("%s, Failed to encode Friend buffer", __func__); + return; + } + + if (seq_auth) { + FRIEND_ADV(buf)->seq_auth = *seq_auth; + } + + enqueue_friend_pdu(frnd, type, buf); + + BT_DBG("Queued message for LPN 0x%04x, queue_size %u", + frnd->lpn, frnd->queue_size); +} + +static void friend_lpn_enqueue_tx(struct bt_mesh_friend *frnd, + struct bt_mesh_net_tx *tx, + enum bt_mesh_friend_pdu_type type, + u64_t *seq_auth, struct net_buf_simple *sbuf) +{ + struct friend_pdu_info info; + struct net_buf *buf; + u32_t seq; + + BT_DBG("LPN 0x%04x", frnd->lpn); + + if (type == BLE_MESH_FRIEND_PDU_SINGLE && seq_auth) { + friend_purge_old_ack(frnd, seq_auth, tx->src); + } + + info.src = tx->src; + info.dst = tx->ctx->addr; + + info.ttl = tx->ctx->send_ttl; + info.ctl = (tx->ctx->app_idx == BLE_MESH_KEY_UNUSED); + + seq = bt_mesh_next_seq(); + info.seq[0] = seq >> 16; + info.seq[1] = seq >> 8; + info.seq[2] = seq; + + info.iv_index = BLE_MESH_NET_IVI_TX; + + buf = create_friend_pdu(frnd, &info, sbuf); + if (!buf) { + BT_ERR("%s, Failed to encode Friend buffer", __func__); + return; + } + + if (seq_auth) { + FRIEND_ADV(buf)->seq_auth = *seq_auth; + } + + enqueue_friend_pdu(frnd, type, buf); + + BT_DBG("Queued message for LPN 0x%04x", frnd->lpn); +} + +static bool friend_lpn_matches(struct bt_mesh_friend *frnd, u16_t net_idx, + u16_t addr) +{ + int i; + + if (!frnd->established) { + return false; + } + + if (net_idx != frnd->net_idx) { + return false; + } + + if (BLE_MESH_ADDR_IS_UNICAST(addr)) { + return is_lpn_unicast(frnd, addr); + } + + for (i = 0; i < ARRAY_SIZE(frnd->sub_list); i++) { + if (frnd->sub_list[i] == addr) { + return true; + } + } + + return false; +} + +bool bt_mesh_friend_match(u16_t net_idx, u16_t addr) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) { + struct bt_mesh_friend *frnd = &bt_mesh.frnd[i]; + + if (friend_lpn_matches(frnd, net_idx, addr)) { + BT_DBG("LPN 0x%04x matched address 0x%04x", + frnd->lpn, addr); + return true; + } + } + + BT_DBG("No matching LPN for address 0x%04x", addr); + + return false; +} + +void bt_mesh_friend_enqueue_rx(struct bt_mesh_net_rx *rx, + enum bt_mesh_friend_pdu_type type, + u64_t *seq_auth, struct net_buf_simple *sbuf) +{ + int i; + + if (!rx->friend_match || + (rx->ctx.recv_ttl <= 1U && rx->net_if != BLE_MESH_NET_IF_LOCAL) || + bt_mesh_friend_get() != BLE_MESH_FRIEND_ENABLED) { + return; + } + + BT_DBG("recv_ttl %u net_idx 0x%04x src 0x%04x dst 0x%04x", + rx->ctx.recv_ttl, rx->sub->net_idx, rx->ctx.addr, + rx->ctx.recv_dst); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) { + struct bt_mesh_friend *frnd = &bt_mesh.frnd[i]; + + if (friend_lpn_matches(frnd, rx->sub->net_idx, + rx->ctx.recv_dst)) { + friend_lpn_enqueue_rx(frnd, rx, type, seq_auth, sbuf); + } + } +} + +bool bt_mesh_friend_enqueue_tx(struct bt_mesh_net_tx *tx, + enum bt_mesh_friend_pdu_type type, + u64_t *seq_auth, struct net_buf_simple *sbuf) +{ + bool matched = false; + int i; + + if (!bt_mesh_friend_match(tx->sub->net_idx, tx->ctx->addr) || + bt_mesh_friend_get() != BLE_MESH_FRIEND_ENABLED) { + return matched; + } + + BT_DBG("net_idx 0x%04x dst 0x%04x src 0x%04x", tx->sub->net_idx, + tx->ctx->addr, tx->src); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) { + struct bt_mesh_friend *frnd = &bt_mesh.frnd[i]; + + if (friend_lpn_matches(frnd, tx->sub->net_idx, tx->ctx->addr)) { + friend_lpn_enqueue_tx(frnd, tx, type, seq_auth, sbuf); + matched = true; + } + } + + return matched; +} + +void bt_mesh_friend_clear_incomplete(struct bt_mesh_subnet *sub, u16_t src, + u16_t dst, u64_t *seq_auth) +{ + int i; + + BT_DBG("%s", __func__); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) { + struct bt_mesh_friend *frnd = &bt_mesh.frnd[i]; + int j; + + if (!friend_lpn_matches(frnd, sub->net_idx, dst)) { + continue; + } + + for (j = 0; j < ARRAY_SIZE(frnd->seg); j++) { + struct bt_mesh_friend_seg *seg = &frnd->seg[j]; + struct net_buf *buf; + + buf = (void *)sys_slist_peek_head(&seg->queue); + if (!buf) { + continue; + } + + if (BLE_MESH_ADV(buf)->addr != src) { + continue; + } + + if (FRIEND_ADV(buf)->seq_auth != *seq_auth) { + continue; + } + + BT_WARN("%s, Clearing incomplete segments for 0x%04x", __func__, src); + + while (!sys_slist_is_empty(&seg->queue)) { + net_buf_unref(net_buf_slist_get(&seg->queue)); + } + } + } +} + +#endif /* CONFIG_BLE_MESH_FRIEND */ diff --git a/components/bt/ble_mesh/mesh_core/friend.h b/components/bt/ble_mesh/mesh_core/friend.h new file mode 100644 index 0000000000..008a342c9b --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/friend.h @@ -0,0 +1,49 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _FRIEND_H_ +#define _FRIEND_H_ + +enum bt_mesh_friend_pdu_type { + BLE_MESH_FRIEND_PDU_SINGLE, + BLE_MESH_FRIEND_PDU_PARTIAL, + BLE_MESH_FRIEND_PDU_COMPLETE, +}; + +bool bt_mesh_friend_match(u16_t net_idx, u16_t addr); + +struct bt_mesh_friend *bt_mesh_friend_find(u16_t net_idx, u16_t lpn_addr, + bool valid, bool established); + +void bt_mesh_friend_enqueue_rx(struct bt_mesh_net_rx *rx, + enum bt_mesh_friend_pdu_type type, + u64_t *seq_auth, struct net_buf_simple *sbuf); +bool bt_mesh_friend_enqueue_tx(struct bt_mesh_net_tx *tx, + enum bt_mesh_friend_pdu_type type, + u64_t *seq_auth, struct net_buf_simple *sbuf); + +void bt_mesh_friend_clear_incomplete(struct bt_mesh_subnet *sub, u16_t src, + u16_t dst, u64_t *seq_auth); + +void bt_mesh_friend_sec_update(u16_t net_idx); + +void bt_mesh_friend_clear_net_idx(u16_t net_idx); + +int bt_mesh_friend_poll(struct bt_mesh_net_rx *rx, struct net_buf_simple *buf); +int bt_mesh_friend_req(struct bt_mesh_net_rx *rx, struct net_buf_simple *buf); +int bt_mesh_friend_clear(struct bt_mesh_net_rx *rx, struct net_buf_simple *buf); +int bt_mesh_friend_clear_cfm(struct bt_mesh_net_rx *rx, + struct net_buf_simple *buf); +int bt_mesh_friend_sub_add(struct bt_mesh_net_rx *rx, + struct net_buf_simple *buf); +int bt_mesh_friend_sub_rem(struct bt_mesh_net_rx *rx, + struct net_buf_simple *buf); + +int bt_mesh_friend_init(void); + +#endif /* _FRIEND_H_ */ diff --git a/components/bt/ble_mesh/mesh_core/health_cli.c b/components/bt/ble_mesh/mesh_core/health_cli.c new file mode 100644 index 0000000000..c66e42afcd --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/health_cli.c @@ -0,0 +1,462 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include "osi/allocator.h" +#include "sdkconfig.h" +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BLE_MESH_DEBUG_MODEL) + +#include "mesh_types.h" +#include "mesh_util.h" +#include "mesh_trace.h" +#include "health_cli.h" + +#include "foundation.h" +#include "mesh_common.h" +#include "btc_ble_mesh_health_model.h" + +s32_t health_msg_timeout; + +static bt_mesh_health_client_t *health_cli; + +static const bt_mesh_client_op_pair_t health_op_pair[] = { + { OP_HEALTH_FAULT_GET, OP_HEALTH_FAULT_STATUS }, + { OP_HEALTH_FAULT_CLEAR, OP_HEALTH_FAULT_STATUS }, + { OP_HEALTH_FAULT_TEST, OP_HEALTH_FAULT_STATUS }, + { OP_HEALTH_PERIOD_GET, OP_HEALTH_PERIOD_STATUS }, + { OP_HEALTH_PERIOD_SET, OP_HEALTH_PERIOD_STATUS }, + { OP_ATTENTION_GET, OP_ATTENTION_STATUS }, + { OP_ATTENTION_SET, OP_ATTENTION_STATUS }, +}; + +static void timeout_handler(struct k_work *work) +{ + health_internal_data_t *internal = NULL; + bt_mesh_health_client_t *client = NULL; + bt_mesh_client_node_t *node = NULL; + + BT_WARN("Receive health status message timeout"); + + node = CONTAINER_OF(work, bt_mesh_client_node_t, timer.work); + if (!node || !node->ctx.model) { + BT_ERR("%s, Invalid parameter", __func__); + return; + } + + client = (bt_mesh_health_client_t *)node->ctx.model->user_data; + if (!client) { + BT_ERR("%s, Health Client user_data is NULL", __func__); + return; + } + + internal = (health_internal_data_t *)client->internal_data; + if (!internal) { + BT_ERR("%s, Health Client internal_data is NULL", __func__); + return; + } + + bt_mesh_callback_health_status_to_btc(node->opcode, 0x03, node->ctx.model, + &node->ctx, NULL, 0); + + bt_mesh_client_free_node(&internal->queue, node); + + return; +} + +static void health_client_cancel(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + void *status, size_t len) +{ + health_internal_data_t *data = NULL; + bt_mesh_client_node_t *node = NULL; + struct net_buf_simple buf = {0}; + u8_t evt_type = 0xFF; + + if (!model || !ctx || !status || !len) { + BT_ERR("%s, Invalid parameter", __func__); + return; + } + + data = (health_internal_data_t *)health_cli->internal_data; + if (!data) { + BT_ERR("%s, Health Client internal_data is NULL", __func__); + return; + } + + /* If it is a publish message, sent to the user directly. */ + buf.data = (u8_t *)status; + buf.len = (u16_t)len; + node = bt_mesh_is_model_message_publish(model, ctx, &buf, true); + if (!node) { + BT_DBG("Unexpected health status message 0x%x", ctx->recv_op); + } else { + switch (node->opcode) { + case OP_HEALTH_FAULT_GET: + case OP_HEALTH_PERIOD_GET: + case OP_ATTENTION_GET: + evt_type = 0x00; + break; + case OP_HEALTH_FAULT_CLEAR: + case OP_HEALTH_FAULT_TEST: + case OP_HEALTH_PERIOD_SET: + case OP_ATTENTION_SET: + evt_type = 0x01; + break; + default: + break; + } + + bt_mesh_callback_health_status_to_btc(node->opcode, evt_type, model, + ctx, (const u8_t *)status, len); + // Don't forget to release the node at the end. + bt_mesh_client_free_node(&data->queue, node); + } + + switch (ctx->recv_op) { + case OP_HEALTH_FAULT_STATUS: { + struct bt_mesh_health_fault_status *val; + val = (struct bt_mesh_health_fault_status *)status; + bt_mesh_free_buf(val->fault_array); + break; + } + default: + break; + } +} + +static void health_fault_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_health_fault_status status = {0}; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->len, + bt_hex(buf->data, buf->len)); + + status.test_id = net_buf_simple_pull_u8(buf); + status.cid = net_buf_simple_pull_le16(buf); + status.fault_array = bt_mesh_alloc_buf(buf->len); + if (!status.fault_array) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + + net_buf_simple_add_mem(status.fault_array, buf->data, buf->len); + + health_client_cancel(model, ctx, &status, sizeof(struct bt_mesh_health_fault_status)); +} + +static void health_current_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + bt_mesh_client_node_t *node = NULL; + u8_t test_id; + u16_t cid; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->len, + bt_hex(buf->data, buf->len)); + + /* Health current status is a publish message, sent to the user directly. */ + if (!(node = bt_mesh_is_model_message_publish(model, ctx, buf, true))) { + return; + } + + test_id = net_buf_simple_pull_u8(buf); + cid = net_buf_simple_pull_le16(buf); + + BT_DBG("Test ID 0x%02x Company ID 0x%04x Fault Count %u", + test_id, cid, buf->len); +} + +static void health_period_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + u8_t status = 0; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->len, + bt_hex(buf->data, buf->len)); + + status = net_buf_simple_pull_u8(buf); + + health_client_cancel(model, ctx, &status, sizeof(u8_t)); +} + +static void health_attention_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + u8_t status = 0; + + BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->len, + bt_hex(buf->data, buf->len)); + + status = net_buf_simple_pull_u8(buf); + + health_client_cancel(model, ctx, &status, sizeof(u8_t)); +} + +const struct bt_mesh_model_op bt_mesh_health_cli_op[] = { + { OP_HEALTH_FAULT_STATUS, 3, health_fault_status }, + { OP_HEALTH_CURRENT_STATUS, 3, health_current_status }, + { OP_HEALTH_PERIOD_STATUS, 1, health_period_status }, + { OP_ATTENTION_STATUS, 1, health_attention_status }, + BLE_MESH_MODEL_OP_END, +}; + +int bt_mesh_health_attention_get(struct bt_mesh_msg_ctx *ctx) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 0 + 4); + int err; + + if (!ctx || !ctx->addr) { + return -EINVAL; + } + + bt_mesh_model_msg_init(&msg, OP_ATTENTION_GET); + + err = bt_mesh_client_send_msg(health_cli->model, OP_ATTENTION_GET, ctx, + &msg, timeout_handler, health_msg_timeout, + true, NULL, NULL); + if (err) { + BT_ERR("%s, send failed (err %d)", __func__, err); + } + + return err; +} + +int bt_mesh_health_attention_set(struct bt_mesh_msg_ctx *ctx, + u8_t attention, bool need_ack) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 1 + 4); + u32_t opcode; + int err; + + if (!ctx || !ctx->addr) { + return -EINVAL; + } + + if (need_ack) { + opcode = OP_ATTENTION_SET; + } else { + opcode = OP_ATTENTION_SET_UNREL; + } + bt_mesh_model_msg_init(&msg, opcode); + net_buf_simple_add_u8(&msg, attention); + + err = bt_mesh_client_send_msg(health_cli->model, opcode, ctx, &msg, + timeout_handler, health_msg_timeout, + need_ack, NULL, NULL); + if (err) { + BT_ERR("%s, send failed (err %d)", __func__, err); + } + + return err; +} + +int bt_mesh_health_period_get(struct bt_mesh_msg_ctx *ctx) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 0 + 4); + int err; + + if (!ctx || !ctx->addr) { + return -EINVAL; + } + + bt_mesh_model_msg_init(&msg, OP_HEALTH_PERIOD_GET); + + err = bt_mesh_client_send_msg(health_cli->model, OP_HEALTH_PERIOD_GET, + ctx, &msg, timeout_handler, health_msg_timeout, + true, NULL, NULL); + if (err) { + BT_ERR("%s, send failed (err %d)", __func__, err); + } + + return err; +} + +int bt_mesh_health_period_set(struct bt_mesh_msg_ctx *ctx, + u8_t divisor, bool need_ack) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 1 + 4); + u32_t opcode; + int err; + + if (!ctx || !ctx->addr) { + return -EINVAL; + } + + if (need_ack) { + opcode = OP_HEALTH_PERIOD_SET; + } else { + opcode = OP_HEALTH_PERIOD_SET_UNREL; + } + bt_mesh_model_msg_init(&msg, opcode); + net_buf_simple_add_u8(&msg, divisor); + + err = bt_mesh_client_send_msg(health_cli->model, opcode, ctx, &msg, + timeout_handler, health_msg_timeout, + need_ack, NULL, NULL); + if (err) { + BT_ERR("%s, send failed (err %d)", __func__, err); + } + + return err; +} + +int bt_mesh_health_fault_test(struct bt_mesh_msg_ctx *ctx, + u16_t cid, u8_t test_id, bool need_ack) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 3 + 4); + u32_t opcode; + int err; + + if (!ctx || !ctx->addr) { + return -EINVAL; + } + + if (need_ack) { + opcode = OP_HEALTH_FAULT_TEST; + } else { + opcode = OP_HEALTH_FAULT_TEST_UNREL; + } + bt_mesh_model_msg_init(&msg, opcode); + net_buf_simple_add_u8(&msg, test_id); + net_buf_simple_add_le16(&msg, cid); + + err = bt_mesh_client_send_msg(health_cli->model, opcode, ctx, &msg, + timeout_handler, health_msg_timeout, + need_ack, NULL, NULL); + if (err) { + BT_ERR("%s, send failed (err %d)", __func__, err); + } + + return err; +} + +int bt_mesh_health_fault_clear(struct bt_mesh_msg_ctx *ctx, + u16_t cid, bool need_ack) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 2 + 4); + u32_t opcode; + int err; + + if (!ctx || !ctx->addr) { + return -EINVAL; + } + + if (need_ack) { + opcode = OP_HEALTH_FAULT_CLEAR; + } else { + opcode = OP_HEALTH_FAULT_CLEAR_UNREL; + } + bt_mesh_model_msg_init(&msg, opcode); + net_buf_simple_add_le16(&msg, cid); + + err = bt_mesh_client_send_msg(health_cli->model, opcode, ctx, &msg, + timeout_handler, health_msg_timeout, + need_ack, NULL, NULL); + if (err) { + BT_ERR("%s, send failed (err %d)", __func__, err); + } + + return err; +} + +int bt_mesh_health_fault_get(struct bt_mesh_msg_ctx *ctx, u16_t cid) +{ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 2 + 4); + int err; + + if (!ctx || !ctx->addr) { + return -EINVAL; + } + + bt_mesh_model_msg_init(&msg, OP_HEALTH_FAULT_GET); + net_buf_simple_add_le16(&msg, cid); + + err = bt_mesh_client_send_msg(health_cli->model, OP_HEALTH_FAULT_GET, ctx, + &msg, timeout_handler, health_msg_timeout, + true, NULL, NULL); + if (err) { + BT_ERR("%s, send failed (err %d)", __func__, err); + } + + return err; +} + +s32_t bt_mesh_health_cli_timeout_get(void) +{ + return health_msg_timeout; +} + +void bt_mesh_health_cli_timeout_set(s32_t timeout) +{ + health_msg_timeout = timeout; +} + +int bt_mesh_health_cli_set(struct bt_mesh_model *model) +{ + if (!model || !model->user_data) { + BT_ERR("%s, No Health Client context for given model", __func__); + return -EINVAL; + } + + health_cli = model->user_data; + + return 0; +} + +int bt_mesh_health_cli_init(struct bt_mesh_model *model, bool primary) +{ + health_internal_data_t *internal = NULL; + bt_mesh_health_client_t *client = NULL; + + BT_DBG("primary %u", primary); + + if (!model) { + BT_ERR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + client = (bt_mesh_health_client_t *)model->user_data; + if (!client) { + BT_ERR("%s, No Health Client context provided", __func__); + return -EINVAL; + } + + /* TODO: call osi_free() when deinit function is invoked*/ + internal = osi_calloc(sizeof(health_internal_data_t)); + if (!internal) { + BT_ERR("%s, Failed to allocate memory", __func__); + return -ENOMEM; + } + + sys_slist_init(&internal->queue); + + client->model = model; + client->op_pair_size = ARRAY_SIZE(health_op_pair); + client->op_pair = health_op_pair; + client->internal_data = internal; + + /* Set the default health client pointer */ + if (!health_cli) { + health_cli = client; + } + + return 0; +} diff --git a/components/bt/ble_mesh/mesh_core/health_srv.c b/components/bt/ble_mesh/mesh_core/health_srv.c new file mode 100644 index 0000000000..96e16692dd --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/health_srv.c @@ -0,0 +1,529 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include "sdkconfig.h" +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BLE_MESH_DEBUG_MODEL) + +#include "mesh_types.h" +#include "mesh_util.h" +#include "mesh_trace.h" +#include "health_srv.h" + +#include "mesh.h" +#include "adv.h" +#include "net.h" +#include "transport.h" +#include "access.h" +#include "foundation.h" +#include "mesh_common.h" + +#define HEALTH_TEST_STANDARD 0x00 + +/* Maximum message length is 384 in BLE Mesh. Here for health fault status, + * due to 1 octet opcode and 4 octets TransMIC, 379 octets can be used to + * store health fault status. + */ +#define HEALTH_FAULT_MAX_LEN 379 + +/* Health Server context of the primary element */ +struct bt_mesh_health_srv *health_srv; + +static void health_get_registered(struct bt_mesh_model *mod, + u16_t company_id, + struct net_buf_simple *msg) +{ + struct bt_mesh_health_srv *srv = mod->user_data; + u8_t *test_id; + + BT_DBG("Company ID 0x%04x", company_id); + + if (!srv) { + BT_ERR("%s, No Health Server context provided", __func__); + return; + } + + bt_mesh_model_msg_init(msg, OP_HEALTH_FAULT_STATUS); + + test_id = net_buf_simple_add(msg, 1); + net_buf_simple_add_le16(msg, company_id); + + if (srv->cb && srv->cb->fault_get_reg) { + u8_t fault_count = net_buf_simple_tailroom(msg) - 4; + int err; + + err = srv->cb->fault_get_reg(mod, company_id, test_id, + net_buf_simple_tail(msg), + &fault_count); + if (err) { + BT_ERR("%s, Failed to get faults (err %d)", __func__, err); + *test_id = HEALTH_TEST_STANDARD; + } else { + net_buf_simple_add(msg, fault_count); + } + } else { + BT_WARN("No callback for getting faults"); + *test_id = HEALTH_TEST_STANDARD; + } +} + +static size_t health_get_current(struct bt_mesh_model *mod, + struct net_buf_simple *msg) +{ + struct bt_mesh_health_srv *srv = mod->user_data; + const struct bt_mesh_comp *comp; + u8_t *test_id, *company_ptr; + u16_t company_id; + u8_t fault_count; + int err; + + if (!srv) { + BT_ERR("%s, No Health Server context provided", __func__); + return 0; + } + + bt_mesh_model_msg_init(msg, OP_HEALTH_CURRENT_STATUS); + + test_id = net_buf_simple_add(msg, 1); + company_ptr = net_buf_simple_add(msg, sizeof(company_id)); + comp = bt_mesh_comp_get(); + + if (srv->cb && srv->cb->fault_get_cur) { + fault_count = net_buf_simple_tailroom(msg); + err = srv->cb->fault_get_cur(mod, test_id, &company_id, + net_buf_simple_tail(msg), + &fault_count); + if (err) { + BT_ERR("%s, Failed to get faults (err %d)", __func__, err); + sys_put_le16(comp->cid, company_ptr); + *test_id = HEALTH_TEST_STANDARD; + fault_count = 0U; + } else { + sys_put_le16(company_id, company_ptr); + net_buf_simple_add(msg, fault_count); + } + } else { + BT_WARN("No callback for getting faults"); + sys_put_le16(comp->cid, company_ptr); + *test_id = HEALTH_TEST_STANDARD; + fault_count = 0U; + } + + return fault_count; +} + +static void health_fault_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct net_buf_simple *sdu = NULL; + u16_t company_id; + + company_id = net_buf_simple_pull_le16(buf); + + BT_DBG("company_id 0x%04x", company_id); + + sdu = bt_mesh_alloc_buf(MIN(BLE_MESH_TX_SDU_MAX, HEALTH_FAULT_MAX_LEN)); + if (!sdu) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + + health_get_registered(model, company_id, sdu); + + if (bt_mesh_model_send(model, ctx, sdu, NULL, NULL)) { + BT_ERR("%s, Unable to send Health Current Status", __func__); + } + + bt_mesh_free_buf(sdu); + return; +} + +static void health_fault_clear_unrel(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_health_srv *srv = model->user_data; + u16_t company_id; + + if (!srv) { + BT_ERR("%s, No Health Server context provided", __func__); + return; + } + + company_id = net_buf_simple_pull_le16(buf); + + BT_DBG("company_id 0x%04x", company_id); + + if (srv->cb && srv->cb->fault_clear) { + srv->cb->fault_clear(model, company_id); + } +} + +static void health_fault_clear(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_health_srv *srv = model->user_data; + struct net_buf_simple *sdu = NULL; + u16_t company_id; + + if (!srv) { + BT_ERR("%s, No Health Server context provided", __func__); + return; + } + + company_id = net_buf_simple_pull_le16(buf); + + BT_DBG("company_id 0x%04x", company_id); + + if (srv->cb && srv->cb->fault_clear) { + srv->cb->fault_clear(model, company_id); + } + + sdu = bt_mesh_alloc_buf(MIN(BLE_MESH_TX_SDU_MAX, HEALTH_FAULT_MAX_LEN)); + if (!sdu) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + + health_get_registered(model, company_id, sdu); + + if (bt_mesh_model_send(model, ctx, sdu, NULL, NULL)) { + BT_ERR("%s, Unable to send Health Current Status", __func__); + } + + bt_mesh_free_buf(sdu); + return; +} + +static void health_fault_test_unrel(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_health_srv *srv = model->user_data; + u16_t company_id; + u8_t test_id; + + if (!srv) { + BT_ERR("%s, No Health Server context provided", __func__); + return; + } + + test_id = net_buf_simple_pull_u8(buf); + company_id = net_buf_simple_pull_le16(buf); + + BT_DBG("test 0x%02x company 0x%04x", test_id, company_id); + + if (srv->cb && srv->cb->fault_test) { + srv->cb->fault_test(model, test_id, company_id); + } +} + +static void health_fault_test(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_health_srv *srv = model->user_data; + struct net_buf_simple *sdu = NULL; + u16_t company_id; + u8_t test_id; + + BT_DBG("%s", __func__); + + if (!srv) { + BT_ERR("%s, No Health Server context provided", __func__); + return; + } + + test_id = net_buf_simple_pull_u8(buf); + company_id = net_buf_simple_pull_le16(buf); + + BT_DBG("test 0x%02x company 0x%04x", test_id, company_id); + + if (srv->cb && srv->cb->fault_test) { + int err; + + err = srv->cb->fault_test(model, test_id, company_id); + if (err) { + BT_WARN("Running fault test failed with err %d", err); + return; + } + } + + sdu = bt_mesh_alloc_buf(MIN(BLE_MESH_TX_SDU_MAX, HEALTH_FAULT_MAX_LEN)); + if (!sdu) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + + health_get_registered(model, company_id, sdu); + + if (bt_mesh_model_send(model, ctx, sdu, NULL, NULL)) { + BT_ERR("%s, Unable to send Health Current Status", __func__); + } + + bt_mesh_free_buf(sdu); + return; +} + +static void send_attention_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx) +{ + /* Needed size: opcode (2 bytes) + msg + MIC */ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 1 + 4); + struct bt_mesh_health_srv *srv = model->user_data; + u8_t time; + + if (!srv) { + BT_ERR("%s, No Health Server context provided", __func__); + return; + } + + time = k_delayed_work_remaining_get(&srv->attn_timer) / 1000; + BT_DBG("%u second%s", time, (time == 1U) ? "" : "s"); + + bt_mesh_model_msg_init(&msg, OP_ATTENTION_STATUS); + + net_buf_simple_add_u8(&msg, time); + + if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) { + BT_ERR("%s, Unable to send Health Attention Status", __func__); + } +} + +static void attention_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + BT_DBG("%s", __func__); + + send_attention_status(model, ctx); +} + +static void attention_set_unrel(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + u8_t time; + + time = net_buf_simple_pull_u8(buf); + + BT_DBG("%u second%s", time, (time == 1U) ? "" : "s"); + + bt_mesh_attention(model, time); +} + +static void attention_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + BT_DBG("%s", __func__); + + attention_set_unrel(model, ctx, buf); + + send_attention_status(model, ctx); +} + +static void send_health_period_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx) +{ + /* Needed size: opcode (2 bytes) + msg + MIC */ + NET_BUF_SIMPLE_DEFINE(msg, 2 + 1 + 4); + + bt_mesh_model_msg_init(&msg, OP_HEALTH_PERIOD_STATUS); + + net_buf_simple_add_u8(&msg, model->pub->period_div); + + if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) { + BT_ERR("%s, Unable to send Health Period Status", __func__); + } +} + +static void health_period_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + BT_DBG("%s", __func__); + + send_health_period_status(model, ctx); +} + +static void health_period_set_unrel(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + u8_t period; + + period = net_buf_simple_pull_u8(buf); + if (period > 15) { + BT_WARN("%s, Prohibited period value %u", __func__, period); + return; + } + + BT_DBG("period %u", period); + + model->pub->period_div = period; +} + +static void health_period_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + BT_DBG("%s", __func__); + + health_period_set_unrel(model, ctx, buf); + + send_health_period_status(model, ctx); +} + +const struct bt_mesh_model_op bt_mesh_health_srv_op[] = { + { OP_HEALTH_FAULT_GET, 2, health_fault_get }, + { OP_HEALTH_FAULT_CLEAR, 2, health_fault_clear }, + { OP_HEALTH_FAULT_CLEAR_UNREL, 2, health_fault_clear_unrel }, + { OP_HEALTH_FAULT_TEST, 3, health_fault_test }, + { OP_HEALTH_FAULT_TEST_UNREL, 3, health_fault_test_unrel }, + { OP_HEALTH_PERIOD_GET, 0, health_period_get }, + { OP_HEALTH_PERIOD_SET, 1, health_period_set }, + { OP_HEALTH_PERIOD_SET_UNREL, 1, health_period_set_unrel }, + { OP_ATTENTION_GET, 0, attention_get }, + { OP_ATTENTION_SET, 1, attention_set }, + { OP_ATTENTION_SET_UNREL, 1, attention_set_unrel }, + BLE_MESH_MODEL_OP_END, +}; + +static int health_pub_update(struct bt_mesh_model *mod) +{ + struct bt_mesh_model_pub *pub = mod->pub; + size_t count; + + BT_DBG("%s", __func__); + + count = health_get_current(mod, pub->msg); + if (count) { + pub->fast_period = 1U; + } else { + pub->fast_period = 0U; + } + + return 0; +} + +int bt_mesh_fault_update(struct bt_mesh_elem *elem) +{ + struct bt_mesh_model *mod; + + mod = bt_mesh_model_find(elem, BLE_MESH_MODEL_ID_HEALTH_SRV); + if (!mod) { + BT_ERR("%s, Health Server does not exist", __func__); + return -EINVAL; + } + + if (!mod->pub) { + BT_ERR("%s, Health Server has no publication support", __func__); + return -EIO; + } + + /* Let periodic publishing, if enabled, take care of sending the + * Health Current Status. + */ + if (bt_mesh_model_pub_period_get(mod)) { + return 0; + } + + health_pub_update(mod); + + return bt_mesh_model_publish(mod); +} + +static void attention_off(struct k_work *work) +{ + struct bt_mesh_health_srv *srv = CONTAINER_OF(work, + struct bt_mesh_health_srv, + attn_timer.work); + BT_DBG("%s", __func__); + + if (!srv) { + BT_ERR("%s, No Health Server context provided", __func__); + return; + } + + if (srv->cb && srv->cb->attn_off) { + srv->cb->attn_off(srv->model); + } +} + +int bt_mesh_health_srv_init(struct bt_mesh_model *model, bool primary) +{ + struct bt_mesh_health_srv *srv = model->user_data; + + if (!srv) { + if (!primary) { + return 0; + } + + BT_ERR("%s, No Health Server context provided", __func__); + return -EINVAL; + } + + if (!model->pub) { + BT_ERR("%s, Health Server has no publication support", __func__); + return -EINVAL; + } + + model->pub->update = health_pub_update; + + k_delayed_work_init(&srv->attn_timer, attention_off); + + srv->model = model; + + if (primary) { + health_srv = srv; + } + + return 0; +} + +void bt_mesh_attention(struct bt_mesh_model *model, u8_t time) +{ + struct bt_mesh_health_srv *srv; + + if (!model) { + srv = health_srv; + if (!srv) { + BT_WARN("%s, No Health Server context provided", __func__); + return; + } + + model = srv->model; + } else { + srv = model->user_data; + if (!srv) { + BT_WARN("%s, No Health Server context provided", __func__); + return; + } + } + + if (time) { + if (srv->cb && srv->cb->attn_on) { + srv->cb->attn_on(model); + } + + k_delayed_work_submit(&srv->attn_timer, time * 1000U); + } else { + k_delayed_work_cancel(&srv->attn_timer); + + if (srv->cb && srv->cb->attn_off) { + srv->cb->attn_off(model); + } + } +} diff --git a/components/bt/ble_mesh/mesh_core/include/cfg_cli.h b/components/bt/ble_mesh/mesh_core/include/cfg_cli.h new file mode 100644 index 0000000000..b001d84b8a --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/include/cfg_cli.h @@ -0,0 +1,297 @@ +/** @file + * @brief Bluetooth Mesh Configuration Client Model APIs. + */ + +/* + * Copyright (c) 2017 Intel Corporation + * Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef _BLE_MESH_CFG_CLI_H_ +#define _BLE_MESH_CFG_CLI_H_ + +#include "mesh_access.h" +#include "mesh_kernel.h" +#include "model_common.h" + +/** + * @brief Bluetooth Mesh + * @defgroup bt_mesh_cfg_cli Bluetooth Mesh Configuration Client Model + * @ingroup bt_mesh + * @{ + */ + +/* Config client model common structure */ +typedef bt_mesh_client_common_t bt_mesh_config_client_t; +typedef bt_mesh_internal_data_t config_internal_data_t; + +extern const struct bt_mesh_model_op bt_mesh_cfg_cli_op[]; + +#define BLE_MESH_MODEL_CFG_CLI(cli_data) \ + BLE_MESH_MODEL(BLE_MESH_MODEL_ID_CFG_CLI, \ + bt_mesh_cfg_cli_op, NULL, cli_data) + +int bt_mesh_cfg_comp_data_get(struct bt_mesh_msg_ctx *ctx, u8_t page); + +int bt_mesh_cfg_beacon_get(struct bt_mesh_msg_ctx *ctx); + +int bt_mesh_cfg_beacon_set(struct bt_mesh_msg_ctx *ctx, u8_t val); + +int bt_mesh_cfg_ttl_get(struct bt_mesh_msg_ctx *ctx); + +int bt_mesh_cfg_ttl_set(struct bt_mesh_msg_ctx *ctx, u8_t val); + +int bt_mesh_cfg_friend_get(struct bt_mesh_msg_ctx *ctx); + +int bt_mesh_cfg_friend_set(struct bt_mesh_msg_ctx *ctx, u8_t val); + +int bt_mesh_cfg_gatt_proxy_get(struct bt_mesh_msg_ctx *ctx); + +int bt_mesh_cfg_gatt_proxy_set(struct bt_mesh_msg_ctx *ctx, u8_t val); + +int bt_mesh_cfg_relay_get(struct bt_mesh_msg_ctx *ctx); + +int bt_mesh_cfg_relay_set(struct bt_mesh_msg_ctx *ctx, u8_t new_relay, u8_t new_transmit); + +int bt_mesh_cfg_net_key_add(struct bt_mesh_msg_ctx *ctx, u16_t key_net_idx, + const u8_t net_key[16]); + +int bt_mesh_cfg_app_key_add(struct bt_mesh_msg_ctx *ctx, u16_t key_net_idx, + u16_t key_app_idx, const u8_t app_key[16]); + +int bt_mesh_cfg_mod_app_bind(struct bt_mesh_msg_ctx *ctx, u16_t elem_addr, + u16_t mod_app_idx, u16_t mod_id, u16_t cid); + +struct bt_mesh_cfg_mod_pub { + u16_t addr; + u16_t app_idx; + bool cred_flag; + u8_t ttl; + u8_t period; + u8_t transmit; +}; + +int bt_mesh_cfg_mod_pub_get(struct bt_mesh_msg_ctx *ctx, u16_t elem_addr, + u16_t mod_id, u16_t cid); + +int bt_mesh_cfg_mod_pub_set(struct bt_mesh_msg_ctx *ctx, u16_t elem_addr, + u16_t mod_id, u16_t cid, + struct bt_mesh_cfg_mod_pub *pub); + +int bt_mesh_cfg_mod_sub_add(struct bt_mesh_msg_ctx *ctx, u16_t elem_addr, + u16_t sub_addr, u16_t mod_id, u16_t cid); + +int bt_mesh_cfg_mod_sub_del(struct bt_mesh_msg_ctx *ctx, u16_t elem_addr, + u16_t sub_addr, u16_t mod_id, u16_t cid); + +int bt_mesh_cfg_mod_sub_overwrite(struct bt_mesh_msg_ctx *ctx, u16_t elem_addr, + u16_t sub_addr, u16_t mod_id, u16_t cid); + +int bt_mesh_cfg_mod_sub_va_add(struct bt_mesh_msg_ctx *ctx, u16_t elem_addr, + const u8_t label[16], u16_t mod_id, u16_t cid); + +int bt_mesh_cfg_mod_sub_va_del(struct bt_mesh_msg_ctx *ctx, u16_t elem_addr, + const u8_t label[16], u16_t mod_id, u16_t cid); + +int bt_mesh_cfg_mod_sub_va_overwrite(struct bt_mesh_msg_ctx *ctx, u16_t elem_addr, + const u8_t label[16], u16_t mod_id, u16_t cid); + +struct bt_mesh_cfg_hb_sub { + u16_t src; + u16_t dst; + u8_t period; +}; + +int bt_mesh_cfg_hb_sub_set(struct bt_mesh_msg_ctx *ctx, + struct bt_mesh_cfg_hb_sub *sub); + +int bt_mesh_cfg_hb_sub_get(struct bt_mesh_msg_ctx *ctx); + +struct bt_mesh_cfg_hb_pub { + u16_t dst; + u8_t count; + u8_t period; + u8_t ttl; + u16_t feat; + u16_t net_idx; +}; + +int bt_mesh_cfg_hb_pub_set(struct bt_mesh_msg_ctx *ctx, + const struct bt_mesh_cfg_hb_pub *pub); + +int bt_mesh_cfg_hb_pub_get(struct bt_mesh_msg_ctx *ctx); + +int bt_mesh_cfg_node_reset(struct bt_mesh_msg_ctx *ctx); + +s32_t bt_mesh_cfg_cli_timeout_get(void); +void bt_mesh_cfg_cli_timeout_set(s32_t timeout); + +/* Configuration Client Status Message Context */ + +struct bt_mesh_cfg_comp_data_status { + u8_t page; + struct net_buf_simple *comp_data; +}; + +struct bt_mesh_cfg_relay_status { + u8_t relay; + u8_t retransmit; +}; + +struct bt_mesh_cfg_netkey_status { + u8_t status; + u16_t net_idx; +}; + +struct bt_mesh_cfg_appkey_status { + u8_t status; + u16_t net_idx; + u16_t app_idx; +}; + +struct bt_mesh_cfg_mod_app_status { + u8_t status; + u16_t elem_addr; + u16_t app_idx; + u16_t cid; + u16_t mod_id; +}; + +struct bt_mesh_cfg_mod_pub_status { + u8_t status; + u16_t elem_addr; + u16_t addr; + u16_t app_idx; + bool cred_flag; + u8_t ttl; + u8_t period; + u8_t transmit; + u16_t cid; + u16_t mod_id; +}; + +struct bt_mesh_cfg_mod_sub_status { + u8_t status; + u16_t elem_addr; + u16_t sub_addr; + u16_t cid; + u16_t mod_id; +}; + +struct bt_mesh_cfg_hb_sub_status { + u8_t status; + u16_t src; + u16_t dst; + u8_t period; + u8_t count; + u8_t min; + u8_t max; +}; + +struct bt_mesh_cfg_hb_pub_status { + u8_t status; + u16_t dst; + u8_t count; + u8_t period; + u8_t ttl; + u16_t feat; + u16_t net_idx; +}; + +struct bt_mesh_cfg_mod_sub_list { + u8_t status; + u16_t elem_addr; + u16_t cid; + u16_t mod_id; + struct net_buf_simple *addr; +}; + +struct bt_mesh_cfg_net_key_list { + struct net_buf_simple *net_idx; +}; + +struct bt_mesh_cfg_app_key_list { + u8_t status; + u16_t net_idx; + struct net_buf_simple *app_idx; +}; + +struct bt_mesh_cfg_node_id_status { + u8_t status; + u16_t net_idx; + u8_t identity; +}; + +struct bt_mesh_cfg_mod_app_list { + u8_t status; + u16_t elem_addr; + u16_t cid; + u16_t mod_id; + struct net_buf_simple *app_idx; +}; + +struct bt_mesh_cfg_key_refresh_status { + u8_t status; + u16_t net_idx; + u8_t phase; +}; + +struct bt_mesh_cfg_lpn_pollto_status { + u16_t lpn_addr; + s32_t timeout; +}; + +int bt_mesh_cfg_mod_pub_va_set(struct bt_mesh_msg_ctx *ctx, u16_t elem_addr, + u16_t mod_id, u16_t cid, const u8_t label[16], + struct bt_mesh_cfg_mod_pub *pub); + +int bt_mesh_cfg_mod_sub_del_all(struct bt_mesh_msg_ctx *ctx, u16_t elem_addr, + u16_t mod_id, u16_t cid); + +int bt_mesh_cfg_mod_sub_get(struct bt_mesh_msg_ctx *ctx, u16_t elem_addr, u16_t mod_id); + +int bt_mesh_cfg_mod_sub_get_vnd(struct bt_mesh_msg_ctx *ctx, u16_t elem_addr, + u16_t mod_id, u16_t cid); + +int bt_mesh_cfg_net_key_update(struct bt_mesh_msg_ctx *ctx, u16_t net_idx, + const u8_t net_key[16]); + +int bt_mesh_cfg_net_key_delete(struct bt_mesh_msg_ctx *ctx, u16_t net_idx); + +int bt_mesh_cfg_net_key_get(struct bt_mesh_msg_ctx *ctx); + +int bt_mesh_cfg_app_key_update(struct bt_mesh_msg_ctx *ctx, u16_t net_idx, + u16_t app_idx, const u8_t app_key[16]); + +int bt_mesh_cfg_app_key_delete(struct bt_mesh_msg_ctx *ctx, u16_t net_idx, u16_t app_idx); + +int bt_mesh_cfg_app_key_get(struct bt_mesh_msg_ctx *ctx, u16_t net_idx); + +int bt_mesh_cfg_node_identity_get(struct bt_mesh_msg_ctx *ctx, u16_t net_idx); + +int bt_mesh_cfg_node_identity_set(struct bt_mesh_msg_ctx *ctx, u16_t net_idx, u8_t identity); + +int bt_mesh_cfg_mod_app_unbind(struct bt_mesh_msg_ctx *ctx, u16_t elem_addr, + u16_t app_idx, u16_t mod_id, u16_t cid); + +int bt_mesh_cfg_mod_app_get(struct bt_mesh_msg_ctx *ctx, u16_t elem_addr, u16_t mod_id); + +int bt_mesh_cfg_mod_app_get_vnd(struct bt_mesh_msg_ctx *ctx, u16_t elem_addr, + u16_t mod_id, u16_t cid); + +int bt_mesh_cfg_kr_phase_get(struct bt_mesh_msg_ctx *ctx, u16_t net_idx); + +int bt_mesh_cfg_kr_phase_set(struct bt_mesh_msg_ctx *ctx, u16_t net_idx, u8_t transition); + +int bt_mesh_cfg_lpn_timeout_get(struct bt_mesh_msg_ctx *ctx, u16_t lpn_addr); + +int bt_mesh_cfg_net_transmit_get(struct bt_mesh_msg_ctx *ctx); + +int bt_mesh_cfg_net_transmit_set(struct bt_mesh_msg_ctx *ctx, u8_t transmit); + +/** + * @} + */ + +#endif /* __BLE_MESH_CFG_CLI_H */ diff --git a/components/bt/ble_mesh/mesh_core/include/cfg_srv.h b/components/bt/ble_mesh/mesh_core/include/cfg_srv.h new file mode 100644 index 0000000000..d5f77e7b01 --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/include/cfg_srv.h @@ -0,0 +1,72 @@ +/** @file + * @brief Bluetooth Mesh Configuration Server Model APIs. + */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef _BLE_MESH_CFG_SRV_H_ +#define _BLE_MESH_CFG_SRV_H_ + +#include "mesh_access.h" +#include "mesh_kernel.h" + +/** + * @brief Bluetooth Mesh + * @defgroup bt_mesh_cfg_srv Bluetooth Mesh Configuration Server Model + * @ingroup bt_mesh + * @{ + */ + +/** Mesh Configuration Server Model Context */ +struct bt_mesh_cfg_srv { + struct bt_mesh_model *model; + + u8_t net_transmit; /* Network Transmit state */ + u8_t relay; /* Relay Mode state */ + u8_t relay_retransmit; /* Relay Retransmit state */ + u8_t beacon; /* Secure Network Beacon state */ + u8_t gatt_proxy; /* GATT Proxy state */ + u8_t frnd; /* Friend state */ + u8_t default_ttl; /* Default TTL */ + + /* Heartbeat Publication */ + struct bt_mesh_hb_pub { + struct k_delayed_work timer; + + u16_t dst; + u16_t count; + u8_t period; + u8_t ttl; + u16_t feat; + u16_t net_idx; + } hb_pub; + + /* Heartbeat Subscription */ + struct bt_mesh_hb_sub { + s64_t expiry; + + u16_t src; + u16_t dst; + u16_t count; + u8_t min_hops; + u8_t max_hops; + + /* Optional subscription tracking function */ + void (*func)(u8_t hops, u16_t feat); + } hb_sub; +}; + +extern const struct bt_mesh_model_op bt_mesh_cfg_srv_op[]; + +#define BLE_MESH_MODEL_CFG_SRV(srv_data) \ + BLE_MESH_MODEL(BLE_MESH_MODEL_ID_CFG_SRV, \ + bt_mesh_cfg_srv_op, NULL, srv_data) + +/** + * @} + */ + +#endif /* __BLE_MESH_CFG_SRV_H */ diff --git a/components/bt/ble_mesh/mesh_core/include/health_cli.h b/components/bt/ble_mesh/mesh_core/include/health_cli.h new file mode 100644 index 0000000000..9d7230ebac --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/include/health_cli.h @@ -0,0 +1,78 @@ +/** @file + * @brief Bluetooth Mesh Health Client Model APIs. + */ + +/* + * Copyright (c) 2017 Intel Corporation + * Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef _BLE_MESH_HEALTH_CLI_H_ +#define _BLE_MESH_HEALTH_CLI_H_ + +#include "mesh_access.h" +#include "mesh_kernel.h" +#include "model_common.h" + +/** + * @brief Bluetooth Mesh + * @defgroup bt_mesh_health_cli Bluetooth Mesh Health Client Model + * @ingroup bt_mesh + * @{ + */ + +/* Health client model common structure */ +typedef bt_mesh_client_common_t bt_mesh_health_client_t; +typedef bt_mesh_internal_data_t health_internal_data_t; + +typedef bt_mesh_internal_data_t health_client_internal_data_t; + +extern const struct bt_mesh_model_op bt_mesh_health_cli_op[]; + +#define BLE_MESH_MODEL_HEALTH_CLI(cli_data) \ + BLE_MESH_MODEL(BLE_MESH_MODEL_ID_HEALTH_CLI, \ + bt_mesh_health_cli_op, NULL, cli_data) + +int bt_mesh_health_cli_set(struct bt_mesh_model *model); + +int bt_mesh_health_fault_get(struct bt_mesh_msg_ctx *ctx, u16_t cid); + +int bt_mesh_health_fault_clear(struct bt_mesh_msg_ctx *ctx, u16_t cid, + bool need_ack); + +int bt_mesh_health_fault_test(struct bt_mesh_msg_ctx *ctx, + u16_t cid, u8_t test_id, bool need_ack); + +int bt_mesh_health_period_get(struct bt_mesh_msg_ctx *ctx); + +int bt_mesh_health_period_set(struct bt_mesh_msg_ctx *ctx, + u8_t divisor, bool need_ack); + +int bt_mesh_health_attention_get(struct bt_mesh_msg_ctx *ctx); + +int bt_mesh_health_attention_set(struct bt_mesh_msg_ctx *ctx, + u8_t attention, bool need_ack); + +s32_t bt_mesh_health_cli_timeout_get(void); +void bt_mesh_health_cli_timeout_set(s32_t timeout); + +/* Health Client Status Message Context */ + +struct bt_mesh_health_current_status { + u8_t test_id; + u16_t cid; + struct net_buf_simple *fault_array; +}; + +struct bt_mesh_health_fault_status { + u8_t test_id; + u16_t cid; + struct net_buf_simple *fault_array; +}; + +/** + * @} + */ + +#endif /* __BLE_MESH_HEALTH_CLI_H */ diff --git a/components/bt/ble_mesh/mesh_core/include/health_srv.h b/components/bt/ble_mesh/mesh_core/include/health_srv.h new file mode 100644 index 0000000000..4e9b840776 --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/include/health_srv.h @@ -0,0 +1,93 @@ +/** @file + * @brief Bluetooth Mesh Health Server Model APIs. + */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef _BLE_MESH_HEALTH_SRV_H_ +#define _BLE_MESH_HEALTH_SRV_H_ + +#include "mesh_access.h" +#include "mesh_kernel.h" + +/** + * @brief Bluetooth Mesh Health Server Model + * @defgroup bt_mesh_health_srv Bluetooth Mesh Health Server Model + * @ingroup bt_mesh + * @{ + */ + +struct bt_mesh_health_srv_cb { + /* Fetch current faults */ + int (*fault_get_cur)(struct bt_mesh_model *model, u8_t *test_id, + u16_t *company_id, u8_t *faults, + u8_t *fault_count); + + /* Fetch registered faults */ + int (*fault_get_reg)(struct bt_mesh_model *model, u16_t company_id, + u8_t *test_id, u8_t *faults, + u8_t *fault_count); + + /* Clear registered faults */ + int (*fault_clear)(struct bt_mesh_model *model, u16_t company_id); + + /* Run a specific test */ + int (*fault_test)(struct bt_mesh_model *model, u8_t test_id, + u16_t company_id); + + /* Attention on */ + void (*attn_on)(struct bt_mesh_model *model); + + /* Attention off */ + void (*attn_off)(struct bt_mesh_model *model); +}; + +/** @def BLE_MESH_HEALTH_PUB_DEFINE + * + * A helper to define a health publication context + * + * @param _name Name given to the publication context variable. + * @param _max_faults Maximum number of faults the element can have. + */ +#define BLE_MESH_HEALTH_PUB_DEFINE(_name, _max_faults) \ + BLE_MESH_MODEL_PUB_DEFINE(_name, NULL, (1 + 3 + (_max_faults))) + +/** Mesh Health Server Model Context */ +struct bt_mesh_health_srv { + struct bt_mesh_model *model; + + /* Optional callback struct */ + const struct bt_mesh_health_srv_cb *cb; + + /* Attention Timer state */ + struct k_delayed_work attn_timer; +}; + +extern const struct bt_mesh_model_op bt_mesh_health_srv_op[]; + +/** @def BLE_MESH_MODEL_HEALTH_SRV + * + * Define a new health server model. Note that this API needs to be + * repeated for each element which the application wants to have a + * health server model on. Each instance also needs a unique + * bt_mesh_health_srv and bt_mesh_model_pub context. + * + * @param srv Pointer to a unique struct bt_mesh_health_srv. + * @param pub Pointer to a unique struct bt_mesh_model_pub. + * + * @return New mesh model instance. + */ +#define BLE_MESH_MODEL_HEALTH_SRV(srv, pub) \ + BLE_MESH_MODEL(BLE_MESH_MODEL_ID_HEALTH_SRV, \ + bt_mesh_health_srv_op, pub, srv) + +int bt_mesh_fault_update(struct bt_mesh_elem *elem); + +/** + * @} + */ + +#endif /* __BLE_MESH_HEALTH_SRV_H */ diff --git a/components/bt/ble_mesh/mesh_core/include/mesh_access.h b/components/bt/ble_mesh/mesh_core/include/mesh_access.h new file mode 100644 index 0000000000..bdabab15ca --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/include/mesh_access.h @@ -0,0 +1,444 @@ +/** @file + * @brief Bluetooth Mesh Access Layer APIs. + */ + +/* + * Copyright (c) 2017 Intel Corporation + * Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef _BLE_MESH_ACCESS_H_ +#define _BLE_MESH_ACCESS_H_ + +#include +#include "mesh_types.h" +#include "mesh_util.h" +#include "mesh_buf.h" +#include "sdkconfig.h" + +/** + * @brief Bluetooth Mesh Access Layer + * @defgroup bt_mesh_access Bluetooth Mesh Access Layer + * @ingroup bt_mesh + * @{ + */ + +#define BLE_MESH_ADDR_UNASSIGNED 0x0000 +#define BLE_MESH_ADDR_ALL_NODES 0xffff +#define BLE_MESH_ADDR_PROXIES 0xfffc +#define BLE_MESH_ADDR_FRIENDS 0xfffd +#define BLE_MESH_ADDR_RELAYS 0xfffe + +#define BLE_MESH_KEY_UNUSED 0xffff +#define BLE_MESH_KEY_DEV 0xfffe + +/** Helper to define a mesh element within an array. + * + * In case the element has no SIG or Vendor models the helper + * macro BLE_MESH_MODEL_NONE can be given instead. + * + * @param _loc Location Descriptor. + * @param _mods Array of models. + * @param _vnd_mods Array of vendor models. + */ +#define BLE_MESH_ELEM(_loc, _mods, _vnd_mods) \ +{ \ + .loc = (_loc), \ + .model_count = ARRAY_SIZE(_mods), \ + .models = (_mods), \ + .vnd_model_count = ARRAY_SIZE(_vnd_mods), \ + .vnd_models = (_vnd_mods), \ +} + +/** Abstraction that describes a Mesh Element */ +struct bt_mesh_elem { + /* Unicast Address. Set at runtime during provisioning. */ + u16_t addr; + + /* Location Descriptor (GATT Bluetooth Namespace Descriptors) */ + const u16_t loc; + + const u8_t model_count; + const u8_t vnd_model_count; + + struct bt_mesh_model *const models; + struct bt_mesh_model *const vnd_models; +}; + +/* Foundation Models */ +#define BLE_MESH_MODEL_ID_CFG_SRV 0x0000 +#define BLE_MESH_MODEL_ID_CFG_CLI 0x0001 +#define BLE_MESH_MODEL_ID_HEALTH_SRV 0x0002 +#define BLE_MESH_MODEL_ID_HEALTH_CLI 0x0003 + +/* Models from the Mesh Model Specification */ +#define BLE_MESH_MODEL_ID_GEN_ONOFF_SRV 0x1000 +#define BLE_MESH_MODEL_ID_GEN_ONOFF_CLI 0x1001 +#define BLE_MESH_MODEL_ID_GEN_LEVEL_SRV 0x1002 +#define BLE_MESH_MODEL_ID_GEN_LEVEL_CLI 0x1003 +#define BLE_MESH_MODEL_ID_GEN_DEF_TRANS_TIME_SRV 0x1004 +#define BLE_MESH_MODEL_ID_GEN_DEF_TRANS_TIME_CLI 0x1005 +#define BLE_MESH_MODEL_ID_GEN_POWER_ONOFF_SRV 0x1006 +#define BLE_MESH_MODEL_ID_GEN_POWER_ONOFF_SETUP_SRV 0x1007 +#define BLE_MESH_MODEL_ID_GEN_POWER_ONOFF_CLI 0x1008 +#define BLE_MESH_MODEL_ID_GEN_POWER_LEVEL_SRV 0x1009 +#define BLE_MESH_MODEL_ID_GEN_POWER_LEVEL_SETUP_SRV 0x100a +#define BLE_MESH_MODEL_ID_GEN_POWER_LEVEL_CLI 0x100b +#define BLE_MESH_MODEL_ID_GEN_BATTERY_SRV 0x100c +#define BLE_MESH_MODEL_ID_GEN_BATTERY_CLI 0x100d +#define BLE_MESH_MODEL_ID_GEN_LOCATION_SRV 0x100e +#define BLE_MESH_MODEL_ID_GEN_LOCATION_SETUPSRV 0x100f +#define BLE_MESH_MODEL_ID_GEN_LOCATION_CLI 0x1010 +#define BLE_MESH_MODEL_ID_GEN_ADMIN_PROP_SRV 0x1011 +#define BLE_MESH_MODEL_ID_GEN_MANUFACTURER_PROP_SRV 0x1012 +#define BLE_MESH_MODEL_ID_GEN_USER_PROP_SRV 0x1013 +#define BLE_MESH_MODEL_ID_GEN_CLIENT_PROP_SRV 0x1014 +#define BLE_MESH_MODEL_ID_GEN_PROP_CLI 0x1015 +#define BLE_MESH_MODEL_ID_SENSOR_SRV 0x1100 +#define BLE_MESH_MODEL_ID_SENSOR_SETUP_SRV 0x1101 +#define BLE_MESH_MODEL_ID_SENSOR_CLI 0x1102 +#define BLE_MESH_MODEL_ID_TIME_SRV 0x1200 +#define BLE_MESH_MODEL_ID_TIME_SETUP_SRV 0x1201 +#define BLE_MESH_MODEL_ID_TIME_CLI 0x1202 +#define BLE_MESH_MODEL_ID_SCENE_SRV 0x1203 +#define BLE_MESH_MODEL_ID_SCENE_SETUP_SRV 0x1204 +#define BLE_MESH_MODEL_ID_SCENE_CLI 0x1205 +#define BLE_MESH_MODEL_ID_SCHEDULER_SRV 0x1206 +#define BLE_MESH_MODEL_ID_SCHEDULER_SETUP_SRV 0x1207 +#define BLE_MESH_MODEL_ID_SCHEDULER_CLI 0x1208 +#define BLE_MESH_MODEL_ID_LIGHT_LIGHTNESS_SRV 0x1300 +#define BLE_MESH_MODEL_ID_LIGHT_LIGHTNESS_SETUP_SRV 0x1301 +#define BLE_MESH_MODEL_ID_LIGHT_LIGHTNESS_CLI 0x1302 +#define BLE_MESH_MODEL_ID_LIGHT_CTL_SRV 0x1303 +#define BLE_MESH_MODEL_ID_LIGHT_CTL_SETUP_SRV 0x1304 +#define BLE_MESH_MODEL_ID_LIGHT_CTL_CLI 0x1305 +#define BLE_MESH_MODEL_ID_LIGHT_CTL_TEMP_SRV 0x1306 +#define BLE_MESH_MODEL_ID_LIGHT_HSL_SRV 0x1307 +#define BLE_MESH_MODEL_ID_LIGHT_HSL_SETUP_SRV 0x1308 +#define BLE_MESH_MODEL_ID_LIGHT_HSL_CLI 0x1309 +#define BLE_MESH_MODEL_ID_LIGHT_HSL_HUE_SRV 0x130a +#define BLE_MESH_MODEL_ID_LIGHT_HSL_SAT_SRV 0x130b +#define BLE_MESH_MODEL_ID_LIGHT_XYL_SRV 0x130c +#define BLE_MESH_MODEL_ID_LIGHT_XYL_SETUP_SRV 0x130d +#define BLE_MESH_MODEL_ID_LIGHT_XYL_CLI 0x130e +#define BLE_MESH_MODEL_ID_LIGHT_LC_SRV 0x130f +#define BLE_MESH_MODEL_ID_LIGHT_LC_SETUPSRV 0x1310 +#define BLE_MESH_MODEL_ID_LIGHT_LC_CLI 0x1311 + +/** Message sending context. */ +struct bt_mesh_msg_ctx { + /** NetKey Index of the subnet to send the message on. */ + u16_t net_idx; + + /** AppKey Index to encrypt the message with. */ + u16_t app_idx; + + /** Remote address. */ + u16_t addr; + + /** Destination address of a received message. Not used for sending. */ + u16_t recv_dst; + + /** Received TTL value. Not used for sending. */ + u8_t recv_ttl: 7; + + /** Force sending reliably by using segment acknowledgement */ + u8_t send_rel: 1; + + /** TTL, or BLE_MESH_TTL_DEFAULT for default TTL. */ + u8_t send_ttl; + + /** Change by Espressif, opcode of a received message. + * Not used for sending message. */ + u32_t recv_op; + + /** Change by Espressif, model corresponds to the message */ + struct bt_mesh_model *model; + + /** Change by Espressif, if the message is sent by a server + * model. Not used for receiving message. */ + bool srv_send; +}; + +struct bt_mesh_model_op { + /* OpCode encoded using the BLE_MESH_MODEL_OP_* macros */ + const u32_t opcode; + + /* Minimum required message length */ + const size_t min_len; + + /* Message handler for the opcode */ + void (*const func)(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf); +}; + +#define BLE_MESH_MODEL_OP_1(b0) (b0) +#define BLE_MESH_MODEL_OP_2(b0, b1) (((b0) << 8) | (b1)) +#define BLE_MESH_MODEL_OP_3(b0, cid) ((((b0) << 16) | 0xc00000) | (cid)) + +#define BLE_MESH_MODEL_OP_END { 0, 0, NULL } +#define BLE_MESH_MODEL_NO_OPS ((struct bt_mesh_model_op []) \ + { BLE_MESH_MODEL_OP_END }) + +/** Helper to define an empty model array */ +#define BLE_MESH_MODEL_NONE ((struct bt_mesh_model []){}) + +#define BLE_MESH_MODEL(_id, _op, _pub, _user_data) \ +{ \ + .id = (_id), \ + .op = _op, \ + .keys = { [0 ... (CONFIG_BLE_MESH_MODEL_KEY_COUNT - 1)] = \ + BLE_MESH_KEY_UNUSED }, \ + .pub = _pub, \ + .groups = { [0 ... (CONFIG_BLE_MESH_MODEL_GROUP_COUNT - 1)] = \ + BLE_MESH_ADDR_UNASSIGNED }, \ + .user_data = _user_data, \ +} + +#define BLE_MESH_MODEL_VND(_company, _id, _op, _pub, _user_data) \ +{ \ + .vnd.company = (_company), \ + .vnd.id = (_id), \ + .op = _op, \ + .pub = _pub, \ + .keys = { [0 ... (CONFIG_BLE_MESH_MODEL_KEY_COUNT - 1)] = \ + BLE_MESH_KEY_UNUSED }, \ + .groups = { [0 ... (CONFIG_BLE_MESH_MODEL_GROUP_COUNT - 1)] = \ + BLE_MESH_ADDR_UNASSIGNED }, \ + .user_data = _user_data, \ +} + +/** @def BLE_MESH_TRANSMIT + * + * @brief Encode transmission count & interval steps. + * + * @param count Number of retransmissions (first transmission is excluded). + * @param int_ms Interval steps in milliseconds. Must be greater than 0 + * and a multiple of 10. + * + * @return Mesh transmit value that can be used e.g. for the default + * values of the configuration model data. + */ +#define BLE_MESH_TRANSMIT(count, int_ms) ((count) | (((int_ms / 10) - 1) << 3)) + +/** @def BLE_MESH_TRANSMIT_COUNT + * + * @brief Decode transmit count from a transmit value. + * + * @param transmit Encoded transmit count & interval value. + * + * @return Transmission count (actual transmissions is N + 1). + */ +#define BLE_MESH_TRANSMIT_COUNT(transmit) (((transmit) & (u8_t)BIT_MASK(3))) + +/** @def BLE_MESH_TRANSMIT_INT + * + * @brief Decode transmit interval from a transmit value. + * + * @param transmit Encoded transmit count & interval value. + * + * @return Transmission interval in milliseconds. + */ +#define BLE_MESH_TRANSMIT_INT(transmit) ((((transmit) >> 3) + 1) * 10) + +/** @def BLE_MESH_PUB_TRANSMIT + * + * @brief Encode Publish Retransmit count & interval steps. + * + * @param count Number of retransmissions (first transmission is excluded). + * @param int_ms Interval steps in milliseconds. Must be greater than 0 + * and a multiple of 50. + * + * @return Mesh transmit value that can be used e.g. for the default + * values of the configuration model data. + */ +#define BLE_MESH_PUB_TRANSMIT(count, int_ms) BLE_MESH_TRANSMIT(count, (int_ms) / 5) + +/** @def BLE_MESH_PUB_TRANSMIT_COUNT + * + * @brief Decode Pubhlish Retransmit count from a given value. + * + * @param transmit Encoded Publish Retransmit count & interval value. + * + * @return Retransmission count (actual transmissions is N + 1). + */ +#define BLE_MESH_PUB_TRANSMIT_COUNT(transmit) BLE_MESH_TRANSMIT_COUNT(transmit) + +/** @def BLE_MESH_PUB_TRANSMIT_INT + * + * @brief Decode Publish Retransmit interval from a given value. + * + * @param transmit Encoded Publish Retransmit count & interval value. + * + * @return Transmission interval in milliseconds. + */ +#define BLE_MESH_PUB_TRANSMIT_INT(transmit) ((((transmit) >> 3) + 1) * 50) + +/** Model publication context. */ +struct bt_mesh_model_pub { + /** The model the context belongs to. Initialized by the stack. */ + struct bt_mesh_model *mod; + + u16_t addr; /**< Publish Address. */ + u16_t key; /**< Publish AppKey Index. */ + + u8_t ttl; /**< Publish Time to Live. */ + u8_t retransmit; /**< Retransmit Count & Interval Steps. */ + u8_t period; /**< Publish Period. */ + u16_t period_div: 4, /**< Divisor for the Period. */ + cred: 1, /**< Friendship Credentials Flag. */ + fast_period: 1, /**< Use FastPeriodDivisor */ + count: 3; /**< Retransmissions left. */ + + u32_t period_start; /**< Start of the current period. */ + + /** @brief Publication buffer, containing the publication message. + * + * This will get correctly created when the publication context + * has been defined using the BLE_MESH_MODEL_PUB_DEFINE macro. + * + * BLE_MESH_MODEL_PUB_DEFINE(name, update, size); + */ + struct net_buf_simple *msg; + + /** @brief Callback for updating the publication buffer. + * + * When set to NULL, the model is assumed not to support + * periodic publishing. When set to non-NULL the callback + * will be called periodically and is expected to update + * @ref bt_mesh_model_pub.msg with a valid publication + * message. + * + * @param mod The Model the Publication Context belogs to. + * + * @return Zero on success or (negative) error code otherwise. + */ + int (*update)(struct bt_mesh_model *mod); + + /* Change by Espressif, role of the device going to publish messages */ + u8_t dev_role; + + /** Publish Period Timer. Only for stack-internal use. */ + struct k_delayed_work timer; +}; + +/** @def BLE_MESH_MODEL_PUB_DEFINE + * + * Define a model publication context. + * + * @param _name Variable name given to the context. + * @param _update Optional message update callback (may be NULL). + * @param _msg_len Length of the publication message. + */ +#define BLE_MESH_MODEL_PUB_DEFINE(_name, _update, _msg_len) \ + NET_BUF_SIMPLE_DEFINE_STATIC(bt_mesh_pub_msg_##_name, _msg_len); \ + static struct bt_mesh_model_pub _name = { \ + .update = _update, \ + .msg = &bt_mesh_pub_msg_##_name, \ + } + +/** Abstraction that describes a Mesh Model instance */ +struct bt_mesh_model { + union { + const u16_t id; + struct { + u16_t company; + u16_t id; + } vnd; + }; + + /* Internal information, mainly for persistent storage */ + u8_t elem_idx; /* Belongs to Nth element */ + u8_t model_idx; /* Is the Nth model in the element */ + u16_t flags; /* Information about what has changed */ + + /* The Element this Model belongs to */ + struct bt_mesh_elem *elem; + + /* Model Publication */ + struct bt_mesh_model_pub *const pub; + + /* AppKey List */ + u16_t keys[CONFIG_BLE_MESH_MODEL_KEY_COUNT]; + + /* Subscription List (group or virtual addresses) */ + u16_t groups[CONFIG_BLE_MESH_MODEL_GROUP_COUNT]; + + const struct bt_mesh_model_op *const op; + + /* Model-specific user data */ + void *user_data; +}; + +struct bt_mesh_send_cb { + void (*start)(u16_t duration, int err, void *cb_data); + void (*end)(int err, void *cb_data); +}; + +void bt_mesh_model_msg_init(struct net_buf_simple *msg, u32_t opcode); + +/** Special TTL value to request using configured default TTL */ +#define BLE_MESH_TTL_DEFAULT 0xff + +/** Maximum allowed TTL value */ +#define BLE_MESH_TTL_MAX 0x7f + +/** + * @brief Send an Access Layer message. + * + * @param model Mesh (client) Model that the message belongs to. + * @param ctx Message context, includes keys, TTL, etc. + * @param msg Access Layer payload (the actual message to be sent). + * @param cb Optional "message sent" callback. + * @param cb_data User data to be passed to the callback. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_model_send(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *msg, + const struct bt_mesh_send_cb *cb, + void *cb_data); + +/** + * @brief Send a model publication message. + * + * Before calling this function, the user needs to ensure that the model + * publication message (@ref bt_mesh_model_pub.msg) contains a valid + * message to be sent. Note that this API is only to be used for + * non-period publishing. For periodic publishing the app only needs + * to make sure that @ref bt_mesh_model_pub.msg contains a valid message + * whenever the @ref bt_mesh_model_pub.update callback is called. + * + * @param model Mesh (client) Model that's publishing the message. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_model_publish(struct bt_mesh_model *model); + +/** + * @brief Get the element that a model belongs to. + * + * @param mod Mesh model. + * + * @return Pointer to the element that the given model belongs to. + */ +struct bt_mesh_elem *bt_mesh_model_elem(struct bt_mesh_model *mod); + +/** Node Composition */ +struct bt_mesh_comp { + u16_t cid; + u16_t pid; + u16_t vid; + + size_t elem_count; + struct bt_mesh_elem *elem; +}; + +/** + * @} + */ + +#endif /* __BLE_MESH_ACCESS_H */ diff --git a/components/bt/ble_mesh/mesh_core/include/mesh_aes_encrypt.h b/components/bt/ble_mesh/mesh_core/include/mesh_aes_encrypt.h new file mode 100644 index 0000000000..afaa6b27dd --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/include/mesh_aes_encrypt.h @@ -0,0 +1,171 @@ +/* aes.h - TinyCrypt interface to an AES-128 implementation */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * @brief -- Interface to an AES-128 implementation. + * + * Overview: AES-128 is a NIST approved block cipher specified in + * FIPS 197. Block ciphers are deterministic algorithms that + * perform a transformation specified by a symmetric key in fixed- + * length data sets, also called blocks. + * + * Security: AES-128 provides approximately 128 bits of security. + * + * Usage: 1) call tc_aes128_set_encrypt/decrypt_key to set the key. + * + * 2) call tc_aes_encrypt/decrypt to process the data. + */ + +#ifndef _BLE_MESH_AES_ENCRYPT_H_ +#define _BLE_MESH_AES_ENCRYPT_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define Nb (4) /* number of columns (32-bit words) comprising the state */ +#define Nk (4) /* number of 32-bit words comprising the key */ +#define Nr (10) /* number of rounds */ +#define TC_AES_BLOCK_SIZE (Nb*Nk) +#define TC_AES_KEY_SIZE (Nb*Nk) + +#define TC_CRYPTO_SUCCESS 1 +#define TC_CRYPTO_FAIL 0 + +#define TC_ZERO_BYTE 0x00 + +/* padding for last message block */ +#define TC_CMAC_PADDING 0x80 + +typedef struct tc_aes_key_sched_struct { + unsigned int words[Nb * (Nr + 1)]; +} *TCAesKeySched_t; + +/* struct tc_cmac_struct represents the state of a CMAC computation */ +typedef struct tc_cmac_struct { + /* initialization vector */ + uint8_t iv[TC_AES_BLOCK_SIZE]; + /* used if message length is a multiple of block_size bytes */ + uint8_t K1[TC_AES_BLOCK_SIZE]; + /* used if message length isn't a multiple block_size bytes */ + uint8_t K2[TC_AES_BLOCK_SIZE]; + /* where to put bytes that didn't fill a block */ + uint8_t leftover[TC_AES_BLOCK_SIZE]; + /* identifies the encryption key */ + unsigned int keyid; + /* next available leftover location */ + unsigned int leftover_offset; + /* AES key schedule */ + TCAesKeySched_t sched; + /* calls to tc_cmac_update left before re-key */ + uint64_t countdown; +} *TCCmacState_t; + +/** + * @brief Set AES-128 encryption key + * Uses key k to initialize s + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: s == NULL or k == NULL + * @note This implementation skips the additional steps required for keys + * larger than 128 bits, and must not be used for AES-192 or + * AES-256 key schedule -- see FIPS 197 for details + * @param s IN/OUT -- initialized struct tc_aes_key_sched_struct + * @param k IN -- points to the AES key + */ +int tc_aes128_set_encrypt_key(TCAesKeySched_t s, const uint8_t *k); + +/** + * @brief AES-128 Encryption procedure + * Encrypts contents of in buffer into out buffer under key; + * schedule s + * @note Assumes s was initialized by aes_set_encrypt_key; + * out and in point to 16 byte buffers + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: out == NULL or in == NULL or s == NULL + * @param out IN/OUT -- buffer to receive ciphertext block + * @param in IN -- a plaintext block to encrypt + * @param s IN -- initialized AES key schedule + */ +int tc_aes_encrypt(uint8_t *out, const uint8_t *in, + const TCAesKeySched_t s); + +/** + * @brief Set the AES-128 decryption key + * Uses key k to initialize s + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: s == NULL or k == NULL + * @note This is the implementation of the straightforward inverse cipher + * using the cipher documented in FIPS-197 figure 12, not the + * equivalent inverse cipher presented in Figure 15 + * @warning This routine skips the additional steps required for keys larger + * than 128, and must not be used for AES-192 or AES-256 key + * schedule -- see FIPS 197 for details + * @param s IN/OUT -- initialized struct tc_aes_key_sched_struct + * @param k IN -- points to the AES key + */ +int tc_aes128_set_decrypt_key(TCAesKeySched_t s, const uint8_t *k); + +/** + * @brief AES-128 Encryption procedure + * Decrypts in buffer into out buffer under key schedule s + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: out is NULL or in is NULL or s is NULL + * @note Assumes s was initialized by aes_set_encrypt_key + * out and in point to 16 byte buffers + * @param out IN/OUT -- buffer to receive ciphertext block + * @param in IN -- a plaintext block to encrypt + * @param s IN -- initialized AES key schedule + */ +int tc_aes_decrypt(uint8_t *out, const uint8_t *in, + const TCAesKeySched_t s); + +int tc_cmac_setup(TCCmacState_t s, const uint8_t *key, TCAesKeySched_t sched); + +void gf_double(uint8_t *out, uint8_t *in); + +int tc_cmac_init(TCCmacState_t s); + +int tc_cmac_update(TCCmacState_t s, const uint8_t *data, size_t data_length); + +int tc_cmac_final(uint8_t *tag, TCCmacState_t s); + +int tc_cmac_erase(TCCmacState_t s); + +#ifdef __cplusplus +} +#endif + +#endif /* _BLE_MESH_AES_ENCRYPT_H_ */ diff --git a/components/bt/ble_mesh/mesh_core/include/mesh_atomic.h b/components/bt/ble_mesh/mesh_core/include/mesh_atomic.h new file mode 100644 index 0000000000..5c8bf17b89 --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/include/mesh_atomic.h @@ -0,0 +1,305 @@ +/* atomic operations */ + +/* + * Copyright (c) 1997-2015, Wind River Systems, Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _BLE_MESH_ATOMIC_H_ +#define _BLE_MESH_ATOMIC_H_ + +#include "mesh_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef bt_mesh_atomic_t bt_mesh_atomic_val_t; + +/** + * @defgroup atomic_apis Atomic Services APIs + * @ingroup kernel_apis + * @{ + */ + +/** + * + * @brief Atomic increment. + * + * This routine performs an atomic increment by 1 on @a target. + * + * @param target Address of atomic variable. + * + * @return Previous value of @a target. + */ +#ifdef CONFIG_ATOMIC_OPERATIONS_BUILTIN +static inline bt_mesh_atomic_val_t bt_mesh_atomic_inc(bt_mesh_atomic_t *target) +{ + return bt_mesh_atomic_add(target, 1); +} +#else +extern bt_mesh_atomic_val_t bt_mesh_atomic_inc(bt_mesh_atomic_t *target); +#endif + +/** + * + * @brief Atomic decrement. + * + * This routine performs an atomic decrement by 1 on @a target. + * + * @param target Address of atomic variable. + * + * @return Previous value of @a target. + */ +#ifdef CONFIG_ATOMIC_OPERATIONS_BUILTIN +static inline bt_mesh_atomic_val_t bt_mesh_atomic_dec(bt_mesh_atomic_t *target) +{ + return bt_mesh_atomic_sub(target, 1); +} +#else +extern bt_mesh_atomic_val_t bt_mesh_atomic_dec(bt_mesh_atomic_t *target); +#endif + +/** + * + * @brief Atomic get. + * + * This routine performs an atomic read on @a target. + * + * @param target Address of atomic variable. + * + * @return Value of @a target. + */ +#ifdef CONFIG_ATOMIC_OPERATIONS_BUILTIN +static inline bt_mesh_atomic_val_t bt_mesh_atomic_get(const bt_mesh_atomic_t *target) +{ + return __atomic_load_n(target, __ATOMIC_SEQ_CST); +} +#else +extern bt_mesh_atomic_val_t bt_mesh_atomic_get(const bt_mesh_atomic_t *target); +#endif + +/** + * + * @brief Atomic get-and-set. + * + * This routine atomically sets @a target to @a value and returns + * the previous value of @a target. + * + * @param target Address of atomic variable. + * @param value Value to write to @a target. + * + * @return Previous value of @a target. + */ +#ifdef CONFIG_ATOMIC_OPERATIONS_BUILTIN +static inline bt_mesh_atomic_val_t bt_mesh_atomic_set(bt_mesh_atomic_t *target, bt_mesh_atomic_val_t value) +{ + /* This builtin, as described by Intel, is not a traditional + * test-and-set operation, but rather an atomic exchange operation. It + * writes value into *ptr, and returns the previous contents of *ptr. + */ + return __atomic_exchange_n(target, value, __ATOMIC_SEQ_CST); +} +#else +extern bt_mesh_atomic_val_t bt_mesh_atomic_set(bt_mesh_atomic_t *target, bt_mesh_atomic_val_t value); +#endif + +/** + * + * @brief Atomic bitwise inclusive OR. + * + * This routine atomically sets @a target to the bitwise inclusive OR of + * @a target and @a value. + * + * @param target Address of atomic variable. + * @param value Value to OR. + * + * @return Previous value of @a target. + */ +#ifdef CONFIG_ATOMIC_OPERATIONS_BUILTIN +static inline bt_mesh_atomic_val_t bt_mesh_atomic_or(bt_mesh_atomic_t *target, bt_mesh_atomic_val_t value) +{ + return __atomic_fetch_or(target, value, __ATOMIC_SEQ_CST); +} +#else +extern bt_mesh_atomic_val_t bt_mesh_atomic_or(bt_mesh_atomic_t *target, bt_mesh_atomic_val_t value); +#endif + +/** + * + * @brief Atomic bitwise AND. + * + * This routine atomically sets @a target to the bitwise AND of @a target + * and @a value. + * + * @param target Address of atomic variable. + * @param value Value to AND. + * + * @return Previous value of @a target. + */ +#ifdef CONFIG_ATOMIC_OPERATIONS_BUILTIN +static inline bt_mesh_atomic_val_t bt_mesh_atomic_and(bt_mesh_atomic_t *target, bt_mesh_atomic_val_t value) +{ + return __atomic_fetch_and(target, value, __ATOMIC_SEQ_CST); +} +#else +extern bt_mesh_atomic_val_t bt_mesh_atomic_and(bt_mesh_atomic_t *target, bt_mesh_atomic_val_t value); +#endif + +/** + * @cond INTERNAL_HIDDEN + */ + +#define BLE_MESH_ATOMIC_BITS (sizeof(bt_mesh_atomic_val_t) * 8) +#define BLE_MESH_ATOMIC_MASK(bit) (1 << ((bit) & (BLE_MESH_ATOMIC_BITS - 1))) +#define BLE_MESH_ATOMIC_ELEM(addr, bit) ((addr) + ((bit) / BLE_MESH_ATOMIC_BITS)) + +/** + * INTERNAL_HIDDEN @endcond + */ + +/** + * @brief Define an array of atomic variables. + * + * This macro defines an array of atomic variables containing at least + * @a num_bits bits. + * + * @note + * If used from file scope, the bits of the array are initialized to zero; + * if used from within a function, the bits are left uninitialized. + * + * @param name Name of array of atomic variables. + * @param num_bits Number of bits needed. + */ +#define BLE_MESH_ATOMIC_DEFINE(name, num_bits) \ + bt_mesh_atomic_t name[1 + ((num_bits) - 1) / BLE_MESH_ATOMIC_BITS] + +/** + * @brief Atomically test a bit. + * + * This routine tests whether bit number @a bit of @a target is set or not. + * The target may be a single atomic variable or an array of them. + * + * @param target Address of atomic variable or array. + * @param bit Bit number (starting from 0). + * + * @return 1 if the bit was set, 0 if it wasn't. + */ +static inline int bt_mesh_atomic_test_bit(const bt_mesh_atomic_t *target, int bit) +{ + bt_mesh_atomic_val_t val = bt_mesh_atomic_get(BLE_MESH_ATOMIC_ELEM(target, bit)); + + return (1 & (val >> (bit & (BLE_MESH_ATOMIC_BITS - 1)))); +} + +/** + * @brief Atomically test and clear a bit. + * + * Atomically clear bit number @a bit of @a target and return its old value. + * The target may be a single atomic variable or an array of them. + * + * @param target Address of atomic variable or array. + * @param bit Bit number (starting from 0). + * + * @return 1 if the bit was set, 0 if it wasn't. + */ +static inline int bt_mesh_atomic_test_and_clear_bit(bt_mesh_atomic_t *target, int bit) +{ + bt_mesh_atomic_val_t mask = BLE_MESH_ATOMIC_MASK(bit); + bt_mesh_atomic_val_t old; + + old = bt_mesh_atomic_and(BLE_MESH_ATOMIC_ELEM(target, bit), ~mask); + + return (old & mask) != 0; +} + +/** + * @brief Atomically set a bit. + * + * Atomically set bit number @a bit of @a target and return its old value. + * The target may be a single atomic variable or an array of them. + * + * @param target Address of atomic variable or array. + * @param bit Bit number (starting from 0). + * + * @return 1 if the bit was set, 0 if it wasn't. + */ +static inline int bt_mesh_atomic_test_and_set_bit(bt_mesh_atomic_t *target, int bit) +{ + bt_mesh_atomic_val_t mask = BLE_MESH_ATOMIC_MASK(bit); + bt_mesh_atomic_val_t old; + + old = bt_mesh_atomic_or(BLE_MESH_ATOMIC_ELEM(target, bit), mask); + + return (old & mask) != 0; +} + +/** + * @brief Atomically clear a bit. + * + * Atomically clear bit number @a bit of @a target. + * The target may be a single atomic variable or an array of them. + * + * @param target Address of atomic variable or array. + * @param bit Bit number (starting from 0). + * + * @return N/A + */ +static inline void bt_mesh_atomic_clear_bit(bt_mesh_atomic_t *target, int bit) +{ + bt_mesh_atomic_val_t mask = BLE_MESH_ATOMIC_MASK(bit); + + (void)bt_mesh_atomic_and(BLE_MESH_ATOMIC_ELEM(target, bit), ~mask); +} + +/** + * @brief Atomically set a bit. + * + * Atomically set bit number @a bit of @a target. + * The target may be a single atomic variable or an array of them. + * + * @param target Address of atomic variable or array. + * @param bit Bit number (starting from 0). + * + * @return N/A + */ +static inline void bt_mesh_atomic_set_bit(bt_mesh_atomic_t *target, int bit) +{ + bt_mesh_atomic_val_t mask = BLE_MESH_ATOMIC_MASK(bit); + + (void)bt_mesh_atomic_or(BLE_MESH_ATOMIC_ELEM(target, bit), mask); +} + +/** + * @brief Atomically set a bit to a given value. + * + * Atomically set bit number @a bit of @a target to value @a val. + * The target may be a single atomic variable or an array of them. + * + * @param target Address of atomic variable or array. + * @param bit Bit number (starting from 0). + * @param val true for 1, false for 0. + * + * @return N/A + */ +static inline void bt_mesh_atomic_set_bit_to(bt_mesh_atomic_t *target, int bit, bool val) +{ + bt_mesh_atomic_val_t mask = BLE_MESH_ATOMIC_MASK(bit); + + if (val) { + (void)bt_mesh_atomic_or(BLE_MESH_ATOMIC_ELEM(target, bit), mask); + } else { + (void)bt_mesh_atomic_and(BLE_MESH_ATOMIC_ELEM(target, bit), ~mask); + } +} + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* _BLE_MESH_ATOMIC_H_ */ diff --git a/components/bt/ble_mesh/mesh_core/include/mesh_bearer_adapt.h b/components/bt/ble_mesh/mesh_core/include/mesh_bearer_adapt.h new file mode 100644 index 0000000000..1382031878 --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/include/mesh_bearer_adapt.h @@ -0,0 +1,733 @@ +/* + * Copyright (c) 2017 Nordic Semiconductor ASA + * Copyright (c) 2015-2017 Intel Corporation + * Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _BLE_MESH_BEARER_ADRPT_H_ +#define _BLE_MESH_BEARER_ADRPT_H_ + +#include +#include "mesh_types.h" +#include "mesh_util.h" +#include "mesh_buf.h" +#include "mesh_uuid.h" + +/* BLE Mesh Max Connection Count */ +#define BLE_MESH_MAX_CONN CONFIG_BT_ACL_CONNECTIONS + +/* BD ADDR types */ +#define BLE_MESH_ADDR_PUBLIC 0x00 +#define BLE_MESH_ADDR_RANDOM 0x01 +#define BLE_MESH_ADDR_PUBLIC_ID 0x02 +#define BLE_MESH_ADDR_RANDOM_ID 0x03 + +/* BD ADDR length */ +#define BLE_MESH_ADDR_LEN 0x06 + +/* Advertising types */ +#define BLE_MESH_ADV_IND 0x00 +#define BLE_MESH_ADV_DIRECT_IND 0x01 +#define BLE_MESH_ADV_SCAN_IND 0x02 +#define BLE_MESH_ADV_NONCONN_IND 0x03 +#define BLE_MESH_ADV_DIRECT_IND_LOW_DUTY 0x04 + +/* advertising channel map */ +#define BLE_MESH_ADV_CHNL_37 BIT(0) +#define BLE_MESH_ADV_CHNL_38 BIT(1) +#define BLE_MESH_ADV_CHNL_39 BIT(2) + +/* Advertising filter policy */ +#define BLE_MESH_AP_SCAN_CONN_ALL 0x00 +#define BLE_MESH_AP_SCAN_WL_CONN_ALL 0x01 +#define BLE_MESH_AP_SCAN_ALL_CONN_WL 0x02 +#define BLE_MESH_AP_SCAN_CONN_WL 0x03 + +/* Scan types */ +#define BLE_MESH_SCAN_PASSIVE 0x00 +#define BLE_MESH_SCAN_ACTIVE 0x01 + +/* Scan operation */ +#define BLE_MESH_SCAN_DISABLE 0x00 +#define BLE_MESH_SCAN_ENABLE 0x01 + +/* Scan duplicate operation */ +#define BLE_MESH_SCAN_FILTER_DUP_DISABLE 0x00 +#define BLE_MESH_SCAN_FILTER_DUP_ENABLE 0x01 + +/* Scan filter policy */ +#define BLE_MESH_SP_ADV_ALL 0x00 +#define BLE_MESH_SP_ADV_WL 0x01 +#define BLE_MESH_SP_ADV_ALL_RPA_DIR_ADV 0x02 +#define BLE_MESH_SP_ADV_WL_RPA_DIR_ADV 0x03 + +/* Error codes for Error response PDU */ +#define BLE_MESH_ATT_ERR_INVALID_HANDLE 0x01 +#define BLE_MESH_ATT_ERR_READ_NOT_PERMITTED 0x02 +#define BLE_MESH_ATT_ERR_WRITE_NOT_PERMITTED 0x03 +#define BLE_MESH_ATT_ERR_INVALID_PDU 0x04 +#define BLE_MESH_ATT_ERR_AUTHENTICATION 0x05 +#define BLE_MESH_ATT_ERR_NOT_SUPPORTED 0x06 +#define BLE_MESH_ATT_ERR_INVALID_OFFSET 0x07 +#define BLE_MESH_ATT_ERR_AUTHORIZATION 0x08 +#define BLE_MESH_ATT_ERR_PREPARE_QUEUE_FULL 0x09 +#define BLE_MESH_ATT_ERR_ATTRIBUTE_NOT_FOUND 0x0a +#define BLE_MESH_ATT_ERR_ATTRIBUTE_NOT_LONG 0x0b +#define BLE_MESH_ATT_ERR_ENCRYPTION_KEY_SIZE 0x0c +#define BLE_MESH_ATT_ERR_INVALID_ATTRIBUTE_LEN 0x0d +#define BLE_MESH_ATT_ERR_UNLIKELY 0x0e +#define BLE_MESH_ATT_ERR_INSUFFICIENT_ENCRYPTION 0x0f +#define BLE_MESH_ATT_ERR_UNSUPPORTED_GROUP_TYPE 0x10 +#define BLE_MESH_ATT_ERR_INSUFFICIENT_RESOURCES 0x11 + +/* Common Profile Error Codes (from CSS) */ +#define BLE_MESH_ATT_ERR_WRITE_REQ_REJECTED 0xfc +#define BLE_MESH_ATT_ERR_CCC_IMPROPER_CONF 0xfd +#define BLE_MESH_ATT_ERR_PROCEDURE_IN_PROGRESS 0xfe +#define BLE_MESH_ATT_ERR_OUT_OF_RANGE 0xff + +/* EIR/AD data type definitions */ +#define BLE_MESH_DATA_FLAGS 0x01 /* AD flags */ +#define BLE_MESH_DATA_UUID16_SOME 0x02 /* 16-bit UUID, more available */ +#define BLE_MESH_DATA_UUID16_ALL 0x03 /* 16-bit UUID, all listed */ +#define BLE_MESH_DATA_UUID32_SOME 0x04 /* 32-bit UUID, more available */ +#define BLE_MESH_DATA_UUID32_ALL 0x05 /* 32-bit UUID, all listed */ +#define BLE_MESH_DATA_UUID128_SOME 0x06 /* 128-bit UUID, more available */ +#define BLE_MESH_DATA_UUID128_ALL 0x07 /* 128-bit UUID, all listed */ +#define BLE_MESH_DATA_NAME_SHORTENED 0x08 /* Shortened name */ +#define BLE_MESH_DATA_NAME_COMPLETE 0x09 /* Complete name */ +#define BLE_MESH_DATA_TX_POWER 0x0a /* Tx Power */ +#define BLE_MESH_DATA_SOLICIT16 0x14 /* Solicit UUIDs, 16-bit */ +#define BLE_MESH_DATA_SOLICIT128 0x15 /* Solicit UUIDs, 128-bit */ +#define BLE_MESH_DATA_SVC_DATA16 0x16 /* Service data, 16-bit UUID */ +#define BLE_MESH_DATA_GAP_APPEARANCE 0x19 /* GAP appearance */ +#define BLE_MESH_DATA_SOLICIT32 0x1f /* Solicit UUIDs, 32-bit */ +#define BLE_MESH_DATA_SVC_DATA32 0x20 /* Service data, 32-bit UUID */ +#define BLE_MESH_DATA_SVC_DATA128 0x21 /* Service data, 128-bit UUID */ +#define BLE_MESH_DATA_URI 0x24 /* URI */ +#define BLE_MESH_DATA_MESH_PROV 0x29 /* Mesh Provisioning PDU */ +#define BLE_MESH_DATA_MESH_MESSAGE 0x2a /* Mesh Networking PDU */ +#define BLE_MESH_DATA_MESH_BEACON 0x2b /* Mesh Beacon */ + +#define BLE_MESH_DATA_MANUFACTURER_DATA 0xff /* Manufacturer Specific Data */ + +#define BLE_MESH_AD_LIMITED 0x01 /* Limited Discoverable */ +#define BLE_MESH_AD_GENERAL 0x02 /* General Discoverable */ +#define BLE_MESH_AD_NO_BREDR 0x04 /* BR/EDR not supported */ + +/* Client Characteristic Configuration Values */ + +/** @def BLE_MESH_GATT_CCC_NOTIFY + * @brief Client Characteristic Configuration Notification. + * + * If set, changes to Characteristic Value shall be notified. + */ +#define BLE_MESH_GATT_CCC_NOTIFY 0x0001 + +/** @def BLE_MESH_GATT_CCC_INDICATE + * @brief Client Characteristic Configuration Indication. + * + * If set, changes to Characteristic Value shall be indicated. + */ +#define BLE_MESH_GATT_CCC_INDICATE 0x0002 + +/** @def BLE_MESH_GATT_ERR + * @brief Construct error return value for attribute read and write callbacks. + * + * @param _att_err ATT error code + * + * @return Appropriate error code for the attribute callbacks. + * + */ +#define BLE_MESH_GATT_ERR(_att_err) (-(_att_err)) + +enum { + BLE_MESH_GATT_ITER_STOP = 0, + BLE_MESH_GATT_ITER_CONTINUE, +}; + +/* GATT attribute permission bit field values */ +enum { + /** No operations supported, e.g. for notify-only */ + BLE_MESH_GATT_PERM_NONE = 0, + + /** Attribute read permission. */ + BLE_MESH_GATT_PERM_READ = BIT(0), + + /** Attribute write permission. */ + BLE_MESH_GATT_PERM_WRITE = BIT(1), + + /** Attribute read permission with encryption. + * + * If set, requires encryption for read access. + */ + BLE_MESH_GATT_PERM_READ_ENCRYPT = BIT(2), + + /** Attribute write permission with encryption. + * + * If set, requires encryption for write access. + */ + BLE_MESH_GATT_PERM_WRITE_ENCRYPT = BIT(3), + + /** Attribute read permission with authentication. + * + * If set, requires encryption using authenticated link-key for read + * access. + */ + BLE_MESH_GATT_PERM_READ_AUTHEN = BIT(4), + + /** Attribute write permission with authentication. + * + * If set, requires encryption using authenticated link-key for write + * access. + */ + BLE_MESH_GATT_PERM_WRITE_AUTHEN = BIT(5), + + /** Attribute prepare write permission. + * + * If set, allows prepare writes with use of BT_GATT_WRITE_FLAG_PREPARE + * passed to write callback. + */ + BLE_MESH_GATT_PERM_PREPARE_WRITE = BIT(6), +}; + +/** Advertising options */ +enum { + /** Convenience value when no options are specified. */ + BLE_MESH_ADV_OPT_NONE = 0, + + /** Advertise as connectable. Type of advertising is determined by + * providing SCAN_RSP data and/or enabling local privacy support. + */ + BLE_MESH_ADV_OPT_CONNECTABLE = BIT(0), + + /** Don't try to resume connectable advertising after a connection. + * This option is only meaningful when used together with + * BLE_MESH_ADV_OPT_CONNECTABLE. If set the advertising will be stopped + * when bt_le_adv_stop() is called or when an incoming (slave) + * connection happens. If this option is not set the stack will + * take care of keeping advertising enabled even as connections + * occur. + */ + BLE_MESH_ADV_OPT_ONE_TIME = BIT(1), +}; + +/* Defined GAP timers */ +#define BLE_MESH_GAP_SCAN_FAST_INTERVAL 0x0060 /* 60 ms */ +#define BLE_MESH_GAP_SCAN_FAST_WINDOW 0x0030 /* 30 ms */ +#define BLE_MESH_GAP_SCAN_SLOW_INTERVAL_1 0x0800 /* 1.28 s */ +#define BLE_MESH_GAP_SCAN_SLOW_WINDOW_1 0x0012 /* 11.25 ms */ +#define BLE_MESH_GAP_SCAN_SLOW_INTERVAL_2 0x1000 /* 2.56 s */ +#define BLE_MESH_GAP_SCAN_SLOW_WINDOW_2 0x0012 /* 11.25 ms */ +#define BLE_MESH_GAP_ADV_FAST_INT_MIN_0 0x0020 /* 20 ms */ +#define BLE_MESH_GAP_ADV_FAST_INT_MAX_0 0x0020 /* 20 ms */ +#define BLE_MESH_GAP_ADV_FAST_INT_MIN_1 0x0030 /* 30 ms */ +#define BLE_MESH_GAP_ADV_FAST_INT_MAX_1 0x0060 /* 60 ms */ +#define BLE_MESH_GAP_ADV_FAST_INT_MIN_2 0x00a0 /* 100 ms */ +#define BLE_MESH_GAP_ADV_FAST_INT_MAX_2 0x00f0 /* 150 ms */ +#define BLE_MESH_GAP_ADV_SLOW_INT_MIN 0x0320 /* 500 ms */ +#define BLE_MESH_GAP_ADV_SLOW_INT_MAX 0x0320 /* 500 ms */ +#define BLE_MESH_GAP_INIT_CONN_INT_MIN 0x0018 /* 30 ms */ +#define BLE_MESH_GAP_INIT_CONN_INT_MAX 0x0028 /* 50 ms */ + +/* Characteristic Properties Bit field values */ + +/** @def BLE_MESH_GATT_CHRC_BROADCAST + * @brief Characteristic broadcast property. + * + * If set, permits broadcasts of the Characteristic Value using Server + * Characteristic Configuration Descriptor. + */ +#define BLE_MESH_GATT_CHRC_BROADCAST 0x01 + +/** @def BLE_MESH_GATT_CHRC_READ + * @brief Characteristic read property. + * + * If set, permits reads of the Characteristic Value. + */ +#define BLE_MESH_GATT_CHRC_READ 0x02 + +/** @def BLE_MESH_GATT_CHRC_WRITE_WITHOUT_RESP + * @brief Characteristic write without response property. + * + * If set, permits write of the Characteristic Value without response. + */ +#define BLE_MESH_GATT_CHRC_WRITE_WITHOUT_RESP 0x04 + +/** @def BLE_MESH_GATT_CHRC_WRITE + * @brief Characteristic write with response property. + * + * If set, permits write of the Characteristic Value with response. + */ +#define BLE_MESH_GATT_CHRC_WRITE 0x08 + +/** @def BLE_MESH_GATT_CHRC_NOTIFY + * @brief Characteristic notify property. + * + * If set, permits notifications of a Characteristic Value without + * acknowledgment. + */ +#define BLE_MESH_GATT_CHRC_NOTIFY 0x10 + +/** @def BLE_MESH_GATT_CHRC_INDICATE + * @brief Characteristic indicate property. + * + * If set, permits indications of a Characteristic Value with acknowledgment. + */ +#define BLE_MESH_GATT_CHRC_INDICATE 0x20 + +/** @def BLE_MESH_GATT_CHRC_AUTH + * @brief Characteristic Authenticated Signed Writes property. + * + * If set, permits signed writes to the Characteristic Value. + */ +#define BLE_MESH_GATT_CHRC_AUTH 0x40 + +/** @def BLE_MESH_GATT_CHRC_EXT_PROP + * @brief Characteristic Extended Properties property. + * + * If set, additional characteristic properties are defined in the + * Characteristic Extended Properties Descriptor. + */ +#define BLE_MESH_GATT_CHRC_EXT_PROP 0x80 + +/** @brief Characteristic Attribute Value. */ +struct bt_mesh_gatt_char { + /** Characteristic UUID. */ + const struct bt_mesh_uuid *uuid; + /** Characteristic properties. */ + u8_t properties; +}; + +/** @brief GATT Service structure */ +struct bt_mesh_gatt_service { + /** Service Attributes */ + struct bt_mesh_gatt_attr *attrs; + /** Service Attribute count */ + u16_t attr_count; + sys_snode_t node; +}; + +struct bt_mesh_ecb_param { + u8_t key[16]; + u8_t clear_text[16]; + u8_t cipher_text[16]; +} __packed; + +typedef struct { + u8_t type; + u8_t val[6]; +} bt_mesh_addr_t; + +/** Description of different data types that can be encoded into + * advertising data. Used to form arrays that are passed to the + * bt_le_adv_start() function. + */ +struct bt_mesh_adv_data { + u8_t type; + u8_t data_len; + const u8_t *data; +}; + +/** @brief Helper to declare elements of bt_data arrays + * + * This macro is mainly for creating an array of struct + * bt_mesh_adv_data elements which is then passed to + * bt_le_adv_start(). + * + * @param _type Type of advertising data field + * @param _data Pointer to the data field payload + * @param _data_len Number of bytes behind the _data pointer + */ +#define BLE_MESH_ADV_DATA(_type, _data, _data_len) \ + { \ + .type = (_type), \ + .data_len = (_data_len), \ + .data = (const u8_t *)(_data), \ + } + +/** @brief Helper to declare elements of bt_data arrays + * + * This macro is mainly for creating an array of struct bt_mesh_adv_data + * elements which is then passed to bt_le_adv_start(). + * + * @param _type Type of advertising data field + * @param _bytes Variable number of single-byte parameters + */ +#define BLE_MESH_ADV_DATA_BYTES(_type, _bytes...) \ + BLE_MESH_ADV_DATA(_type, ((u8_t []) { _bytes }), \ + sizeof((u8_t []) { _bytes })) + +/* BLE Mesh Advertising Parameters */ +struct bt_mesh_adv_param { + /** Bit-field of advertising options */ + u8_t options; + + /** Minimum Advertising Interval (N * 0.625) */ + u16_t interval_min; + + /** Maximum Advertising Interval (N * 0.625) */ + u16_t interval_max; +}; + +/* BLE Mesh scan parameters */ +struct bt_mesh_scan_param { + /** Scan type (BLE_MESH_SCAN_ACTIVE or BLE_MESH_SCAN_PASSIVE) */ + u8_t type; + + /** Duplicate filtering (BLE_MESH_SCAN_FILTER_DUP_ENABLE or + * BLE_MESH_SCAN_FILTER_DUP_DISABLE) + */ + u8_t filter_dup; + + /** Scan interval (N * 0.625 ms) */ + u16_t interval; + + /** Scan window (N * 0.625 ms) */ + u16_t window; +}; + +struct bt_mesh_conn { + u16_t handle; + bt_mesh_atomic_t ref; +}; + +/** @typedef bt_mesh_scan_cb_t + * @brief Callback type for reporting LE scan results. + * + * A function of this type is given to the bt_le_scan_start() function + * and will be called for any discovered LE device. + * + * @param addr Advertiser LE address and type. + * @param rssi Strength of advertiser signal. + * @param adv_type Type of advertising response from advertiser. + * @param data Buffer containing advertiser data. + */ +typedef void bt_mesh_scan_cb_t(const bt_mesh_addr_t *addr, s8_t rssi, + u8_t adv_type, struct net_buf_simple *buf); + +/* @typedef bt_mesh_dh_key_cb_t + * @brief Callback type for DH Key calculation. + * + * Used to notify of the calculated DH Key. + * + * @param key Public key. + * @param idx Provisioning link index, only used by Provisioner. + * + * @return The DH Key, or NULL in case of failure. + */ +typedef void (*bt_mesh_dh_key_cb_t)(const u8_t key[32], const u8_t idx); + +/** @typedef bt_mesh_gatt_attr_func_t + * @brief Attribute iterator callback. + * + * @param attr Attribute found. + * @param user_data Data given. + * + * @return BLE_MESH_GATT_ITER_CONTINUE if should continue to the next attribute + * or BLE_MESH_GATT_ITER_STOP to stop. + */ +typedef u8_t (*bt_mesh_gatt_attr_func_t)(const struct bt_mesh_gatt_attr *attr, + void *user_data); + +/** @brief Connection callback structure. + * + * This structure is used for tracking the state of a connection. + * It is registered with the help of the bt_mesh_gatts_conn_cb_register() API. + * It's permissible to register multiple instances of this @ref bt_conn_cb + * type, in case different modules of an application are interested in + * tracking the connection state. If a callback is not of interest for + * an instance, it may be set to NULL and will as a consequence not be + * used for that instance. + */ +struct bt_mesh_conn_cb { + /** @brief A new connection has been established. + * + * This callback notifies the application of a new connection. + * In case the err parameter is non-zero it means that the + * connection establishment failed. + * + * @param conn New connection object. + * @param err HCI error. Zero for success, non-zero otherwise. + */ + void (*connected)(struct bt_mesh_conn *conn, u8_t err); + + /** @brief A connection has been disconnected. + * + * This callback notifies the application that a connection + * has been disconnected. + * + * @param conn Connection object. + * @param reason HCI reason for the disconnection. + */ + void (*disconnected)(struct bt_mesh_conn *conn, u8_t reason); +}; + +struct bt_mesh_prov_conn_cb { + void (*connected)(const u8_t addr[6], struct bt_mesh_conn *conn, int id); + + void (*disconnected)(struct bt_mesh_conn *conn, u8_t reason); + + ssize_t (*prov_write_descr)(struct bt_mesh_conn *conn, u8_t *addr); + + ssize_t (*prov_notify)(struct bt_mesh_conn *conn, u8_t *data, u16_t len); + + ssize_t (*proxy_write_descr)(struct bt_mesh_conn *conn); + + ssize_t (*proxy_notify)(struct bt_mesh_conn *conn, u8_t *data, u16_t len); +}; + +/** @brief GATT Attribute structure. */ +struct bt_mesh_gatt_attr { + /** Attribute UUID */ + const struct bt_mesh_uuid *uuid; + + /** Attribute read callback + * + * @param conn The connection that is requesting to read + * @param attr The attribute that's being read + * @param buf Buffer to place the read result in + * @param len Length of data to read + * @param offset Offset to start reading from + * + * @return Number fo bytes read, or in case of an error + * BLE_MESH_GATT_ERR() with a specific ATT error code. + */ + ssize_t (*read)(struct bt_mesh_conn *conn, + const struct bt_mesh_gatt_attr *attr, + void *buf, u16_t len, + u16_t offset); + + /** Attribute write callback + * + * @param conn The connection that is requesting to write + * @param attr The attribute that's being written + * @param buf Buffer with the data to write + * @param len Number of bytes in the buffer + * @param offset Offset to start writing from + * @param flags Flags (BT_GATT_WRITE_*) + * + * @return Number of bytes written, or in case of an error + * BLE_MESH_GATT_ERR() with a specific ATT error code. + */ + ssize_t (*write)(struct bt_mesh_conn *conn, + const struct bt_mesh_gatt_attr *attr, + const void *buf, u16_t len, + u16_t offset, u8_t flags); + + /** Attribute user data */ + void *user_data; + /** Attribute handle */ + u16_t handle; + /** Attribute permissions */ + u8_t perm; +}; + +/** @def BLE_MESH_GATT_PRIMARY_SERVICE + * @brief Primary Service Declaration Macro. + * + * Helper macro to declare a primary service attribute. + * + * @param _service Service attribute value. + */ +#define BLE_MESH_GATT_PRIMARY_SERVICE(_service) \ +{ \ + .uuid = BLE_MESH_UUID_GATT_PRIMARY, \ + .perm = BLE_MESH_GATT_PERM_READ, \ + .read = bt_mesh_gatts_attr_read_service, \ + .user_data = _service, \ +} + +/** @def BLE_MESH_GATT_SECONDARY_SERVICE + * @brief Secondary Service Declaration Macro. + * + * Helper macro to declare a secondary service attribute. + * + * @param _service Service attribute value. + */ +#define BLE_MESH_GATT_SECONDARY_SERVICE(_service) \ +{ \ + .uuid = BLE_MESH_UUID_GATT_SECONDARY, \ + .perm = BLE_MESH_GATT_PERM_READ, \ + .read = bt_mesh_gatts_attr_read_service, \ + .user_data = _service, \ +} + +/** @def BLE_MESH_GATT_INCLUDE_SERVICE + * @brief Include Service Declaration Macro. + * + * Helper macro to declare database internal include service attribute. + * + * @param _service_incl the first service attribute of service to include + */ +#define BLE_MESH_GATT_INCLUDE_SERVICE(_service_incl) \ +{ \ + .uuid = BLE_MESH_UUID_GATT_INCLUDE, \ + .perm = BLE_MESH_GATT_PERM_READ, \ + .read = bt_mesh_gatts_attr_read_included, \ + .user_data = _service_incl, \ +} + +/** @def BLE_MESH_GATT_CHARACTERISTIC + * @brief Characteristic Declaration Macro. + * + * Helper macro to declare a characteristic attribute. + * + * @param _uuid Characteristic attribute uuid. + * @param _props Characteristic attribute properties. + */ +#define BLE_MESH_GATT_CHARACTERISTIC(_uuid, _props) \ +{ \ + .uuid = BLE_MESH_UUID_GATT_CHRC, \ + .perm = BLE_MESH_GATT_PERM_READ, \ + .read = bt_mesh_gatts_attr_read_chrc, \ + .user_data = (&(struct bt_mesh_gatt_char) { .uuid = _uuid, \ + .properties = _props, }), \ +} + +/** @def BLE_MESH_GATT_DESCRIPTOR + * @brief Descriptor Declaration Macro. + * + * Helper macro to declare a descriptor attribute. + * + * @param _uuid Descriptor attribute uuid. + * @param _perm Descriptor attribute access permissions. + * @param _read Descriptor attribute read callback. + * @param _write Descriptor attribute write callback. + * @param _value Descriptor attribute value. + */ +#define BLE_MESH_GATT_DESCRIPTOR(_uuid, _perm, _read, _write, _value) \ +{ \ + .uuid = _uuid, \ + .perm = _perm, \ + .read = _read, \ + .write = _write, \ + .user_data = _value, \ +} + +/** @def BLE_MESH_GATT_SERVICE + * @brief Service Structure Declaration Macro. + * + * Helper macro to declare a service structure. + * + * @param _attrs Service attributes. + */ +#define BLE_MESH_GATT_SERVICE(_attrs) \ +{ \ + .attrs = _attrs, \ + .attr_count = ARRAY_SIZE(_attrs), \ +} + +int bt_le_adv_start(const struct bt_mesh_adv_param *param, + const struct bt_mesh_adv_data *ad, size_t ad_len, + const struct bt_mesh_adv_data *sd, size_t sd_len); + +int bt_le_adv_stop(void); + +int bt_le_scan_start(const struct bt_mesh_scan_param *param, bt_mesh_scan_cb_t cb); + +int bt_le_scan_stop(void); + +void bt_mesh_gatts_conn_cb_register(struct bt_mesh_conn_cb *cb); + +int bt_mesh_gatts_disconnect(struct bt_mesh_conn *conn, u8_t reason); + +int bt_mesh_gatts_service_register(struct bt_mesh_gatt_service *svc); + +int bt_mesh_gatts_service_unregister(struct bt_mesh_gatt_service *svc); + +ssize_t bt_mesh_gatts_attr_read_included(struct bt_mesh_conn *conn, + const struct bt_mesh_gatt_attr *attr, + void *buf, u16_t len, u16_t offset); + +ssize_t bt_mesh_gatts_attr_read(struct bt_mesh_conn *conn, const struct bt_mesh_gatt_attr *attr, + void *buf, u16_t buf_len, u16_t offset, + const void *value, u16_t value_len); + +ssize_t bt_mesh_gatts_attr_read_service(struct bt_mesh_conn *conn, + const struct bt_mesh_gatt_attr *attr, + void *buf, u16_t len, u16_t offset); + +ssize_t bt_mesh_gatts_attr_read_chrc(struct bt_mesh_conn *conn, + const struct bt_mesh_gatt_attr *attr, void *buf, + u16_t len, u16_t offset); + +int bt_mesh_gatts_notify(struct bt_mesh_conn *conn, const struct bt_mesh_gatt_attr *attr, + const void *data, u16_t len); + +u16_t bt_mesh_gatt_get_mtu(struct bt_mesh_conn *conn); + +/** APIs added by Espressif */ +int bt_mesh_gatts_service_stop(struct bt_mesh_gatt_service *svc); +int bt_mesh_gatts_service_start(struct bt_mesh_gatt_service *svc); + +void bt_mesh_gattc_conn_cb_register(struct bt_mesh_prov_conn_cb *cb); + +u16_t bt_mesh_gattc_get_service_uuid(struct bt_mesh_conn *conn); + +int bt_mesh_gattc_conn_create(const bt_mesh_addr_t *addr, u16_t service_uuid); + +void bt_gattc_conn_close(struct bt_mesh_conn *conn); + +void bt_mesh_gattc_exchange_mtu(u8_t index); + +u16_t bt_mesh_gattc_get_mtu_info(struct bt_mesh_conn *conn); + +int bt_mesh_gattc_write_no_rsp(struct bt_mesh_conn *conn, const struct bt_mesh_gatt_attr *attr, + const void *data, u16_t len); + +void bt_mesh_gattc_disconnect(struct bt_mesh_conn *conn); + +struct bt_mesh_conn *bt_mesh_conn_ref(struct bt_mesh_conn *conn); + +void bt_mesh_conn_unref(struct bt_mesh_conn *conn); + +void bt_mesh_gatt_init(void); + +void bt_mesh_adapt_init(void); + +int bt_mesh_rand(void *buf, size_t len); + +void bt_mesh_set_private_key(const u8_t pri_key[32]); + +const u8_t *bt_mesh_pub_key_get(void); + +bool bt_mesh_check_public_key(const uint8_t key[64]); + +int bt_mesh_dh_key_gen(const u8_t remote_pk[64], bt_mesh_dh_key_cb_t cb, const u8_t idx); + +int bt_mesh_encrypt_le(const u8_t key[16], const u8_t plaintext[16], + u8_t enc_data[16]); + +int bt_mesh_encrypt_be(const u8_t key[16], const u8_t plaintext[16], + u8_t enc_data[16]); + +enum { + BLE_MESH_EXCEP_LIST_ADD = 0, + BLE_MESH_EXCEP_LIST_REMOVE, + BLE_MESH_EXCEP_LIST_CLEAN, +}; + +enum { + BLE_MESH_EXCEP_INFO_ADV_ADDR = 0, + BLE_MESH_EXCEP_INFO_MESH_LINK_ID, + BLE_MESH_EXCEP_INFO_MESH_BEACON, + BLE_MESH_EXCEP_INFO_MESH_PROV_ADV, + BLE_MESH_EXCEP_INFO_MESH_PROXY_ADV, +}; + +enum { + BLE_MESH_EXCEP_CLEAN_ADDR_LIST = BIT(0), + BLE_MESH_EXCEP_CLEAN_MESH_LINK_ID_LIST = BIT(1), + BLE_MESH_EXCEP_CLEAN_MESH_BEACON_LIST = BIT(2), + BLE_MESH_EXCEP_CLEAN_MESH_PROV_ADV_LIST = BIT(3), + BLE_MESH_EXCEP_CLEAN_MESH_PROXY_ADV_LIST = BIT(4), + BLE_MESH_EXCEP_CLEAN_ALL_LIST = 0xFFFF, +}; + +int bt_mesh_update_exceptional_list(u8_t sub_code, u8_t type, void *info); + +#endif /* _BLE_MESH_BEARER_ADRPT_H_ */ + diff --git a/components/bt/ble_mesh/mesh_core/include/mesh_buf.h b/components/bt/ble_mesh/mesh_core/include/mesh_buf.h new file mode 100644 index 0000000000..e6c62877ea --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/include/mesh_buf.h @@ -0,0 +1,1064 @@ +/** @file + * @brief Buffer management. + */ + +/* + * Copyright (c) 2015 Intel Corporation + * Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef _BLE_MESH_BUF_H_ +#define _BLE_MESH_BUF_H_ + +#include +#include "sys/cdefs.h" +#include "mesh_types.h" +#include "mesh_slist.h" +#include "mesh_kernel.h" +#include "mesh_util.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Unaligned access */ +#define UNALIGNED_GET(p) \ +__extension__ ({ \ + struct __attribute__((__packed__)) { \ + __typeof__(*(p)) __v; \ + } *__p = (__typeof__(__p)) (p); \ + __p->__v; \ +}) + +#define BLE_MESH_NET_BUF_USER_DATA_SIZE 4 + +/** + * @brief Network buffer library + * @defgroup net_buf Network Buffer Library + * @ingroup networking + * @{ + */ + +/* Alignment needed for various parts of the buffer definition */ +#define __net_buf_align __aligned(sizeof(int)) + +/** + * @def NET_BUF_SIMPLE_DEFINE + * @brief Define a net_buf_simple stack variable. + * + * This is a helper macro which is used to define a net_buf_simple object + * on the stack. + * + * @param _name Name of the net_buf_simple object. + * @param _size Maximum data storage for the buffer. + */ +#define NET_BUF_SIMPLE_DEFINE(_name, _size) \ + u8_t net_buf_data_##_name[_size]; \ + struct net_buf_simple _name = { \ + .data = net_buf_data_##_name, \ + .len = 0, \ + .size = _size, \ + .__buf = net_buf_data_##_name, \ + } + +/** + * @def NET_BUF_SIMPLE_DEFINE_STATIC + * @brief Define a static net_buf_simple variable. + * + * This is a helper macro which is used to define a static net_buf_simple + * object. + * + * @param _name Name of the net_buf_simple object. + * @param _size Maximum data storage for the buffer. + */ +#define NET_BUF_SIMPLE_DEFINE_STATIC(_name, _size) \ + static u8_t net_buf_data_##_name[_size]; \ + static struct net_buf_simple _name = { \ + .data = net_buf_data_##_name, \ + .len = 0, \ + .size = _size, \ + .__buf = net_buf_data_##_name, \ + } + +/** + * @brief Simple network buffer representation. + * + * This is a simpler variant of the net_buf object (in fact net_buf uses + * net_buf_simple internally). It doesn't provide any kind of reference + * counting, user data, dynamic allocation, or in general the ability to + * pass through kernel objects such as FIFOs. + * + * The main use of this is for scenarios where the meta-data of the normal + * net_buf isn't needed and causes too much overhead. This could be e.g. + * when the buffer only needs to be allocated on the stack or when the + * access to and lifetime of the buffer is well controlled and constrained. + */ +struct net_buf_simple { + /** Pointer to the start of data in the buffer. */ + u8_t *data; + + /** Length of the data behind the data pointer. */ + u16_t len; + + /** Amount of data that this buffer can store. */ + u16_t size; + + /** Start of the data storage. Not to be accessed directly + * (the data pointer should be used instead). + */ + u8_t *__buf; +}; + +/** + * @def NET_BUF_SIMPLE + * @brief Define a net_buf_simple stack variable and get a pointer to it. + * + * This is a helper macro which is used to define a net_buf_simple object on + * the stack and the get a pointer to it as follows: + * + * struct net_buf_simple *my_buf = NET_BUF_SIMPLE(10); + * + * After creating the object it needs to be initialized by calling + * net_buf_simple_init(). + * + * @param _size Maximum data storage for the buffer. + * + * @return Pointer to stack-allocated net_buf_simple object. + */ +#define NET_BUF_SIMPLE(_size) \ + ((struct net_buf_simple *)(&(struct { \ + struct net_buf_simple buf; \ + u8_t data[_size] __net_buf_align; \ + }) { \ + .buf.size = _size, \ + .buf.__buf = NULL, \ + })) + +/** + * @brief Initialize a net_buf_simple object. + * + * This needs to be called after creating a net_buf_simple object using + * the NET_BUF_SIMPLE macro. + * + * @param buf Buffer to initialize. + * @param reserve_head Headroom to reserve. + */ +static inline void net_buf_simple_init(struct net_buf_simple *buf, + size_t reserve_head) +{ + if (!buf->__buf) { + buf->__buf = (u8_t *)buf + sizeof(*buf); + } + + buf->data = buf->__buf + reserve_head; + buf->len = 0; +} + +/** + * @brief Reset buffer + * + * Reset buffer data so it can be reused for other purposes. + * + * @param buf Buffer to reset. + */ +static inline void net_buf_simple_reset(struct net_buf_simple *buf) +{ + buf->len = 0; + buf->data = buf->__buf; +} + +/** + * @brief Prepare data to be added at the end of the buffer + * + * Increments the data length of a buffer to account for more data + * at the end. + * + * @param buf Buffer to update. + * @param len Number of bytes to increment the length with. + * + * @return The original tail of the buffer. + */ +void *net_buf_simple_add(struct net_buf_simple *buf, size_t len); + +/** + * @brief Copy given number of bytes from memory to the end of the buffer + * + * Increments the data length of the buffer to account for more data at the + * end. + * + * @param buf Buffer to update. + * @param mem Location of data to be added. + * @param len Length of data to be added + * + * @return The original tail of the buffer. + */ +void *net_buf_simple_add_mem(struct net_buf_simple *buf, const void *mem, + size_t len); + +/** + * @brief Add (8-bit) byte at the end of the buffer + * + * Increments the data length of the buffer to account for more data at the + * end. + * + * @param buf Buffer to update. + * @param val byte value to be added. + * + * @return Pointer to the value added + */ +u8_t *net_buf_simple_add_u8(struct net_buf_simple *buf, u8_t val); + +/** + * @brief Add 16-bit value at the end of the buffer + * + * Adds 16-bit value in little endian format at the end of buffer. + * Increments the data length of a buffer to account for more data + * at the end. + * + * @param buf Buffer to update. + * @param val 16-bit value to be added. + */ +void net_buf_simple_add_le16(struct net_buf_simple *buf, u16_t val); + +/** + * @brief Add 16-bit value at the end of the buffer + * + * Adds 16-bit value in big endian format at the end of buffer. + * Increments the data length of a buffer to account for more data + * at the end. + * + * @param buf Buffer to update. + * @param val 16-bit value to be added. + */ +void net_buf_simple_add_be16(struct net_buf_simple *buf, u16_t val); + +/** + * @brief Add 32-bit value at the end of the buffer + * + * Adds 32-bit value in little endian format at the end of buffer. + * Increments the data length of a buffer to account for more data + * at the end. + * + * @param buf Buffer to update. + * @param val 32-bit value to be added. + */ +void net_buf_simple_add_le32(struct net_buf_simple *buf, u32_t val); + +/** + * @brief Add 32-bit value at the end of the buffer + * + * Adds 32-bit value in big endian format at the end of buffer. + * Increments the data length of a buffer to account for more data + * at the end. + * + * @param buf Buffer to update. + * @param val 32-bit value to be added. + */ +void net_buf_simple_add_be32(struct net_buf_simple *buf, u32_t val); + +/** + * @brief Push data to the beginning of the buffer. + * + * Modifies the data pointer and buffer length to account for more data + * in the beginning of the buffer. + * + * @param buf Buffer to update. + * @param len Number of bytes to add to the beginning. + * + * @return The new beginning of the buffer data. + */ +void *net_buf_simple_push(struct net_buf_simple *buf, size_t len); + +/** + * @brief Push 16-bit value to the beginning of the buffer + * + * Adds 16-bit value in little endian format to the beginning of the + * buffer. + * + * @param buf Buffer to update. + * @param val 16-bit value to be pushed to the buffer. + */ +void net_buf_simple_push_le16(struct net_buf_simple *buf, u16_t val); + +/** + * @brief Push 16-bit value to the beginning of the buffer + * + * Adds 16-bit value in big endian format to the beginning of the + * buffer. + * + * @param buf Buffer to update. + * @param val 16-bit value to be pushed to the buffer. + */ +void net_buf_simple_push_be16(struct net_buf_simple *buf, u16_t val); + +/** + * @brief Push 8-bit value to the beginning of the buffer + * + * Adds 8-bit value the beginning of the buffer. + * + * @param buf Buffer to update. + * @param val 8-bit value to be pushed to the buffer. + */ +void net_buf_simple_push_u8(struct net_buf_simple *buf, u8_t val); + +/** + * @brief Remove data from the beginning of the buffer. + * + * Removes data from the beginning of the buffer by modifying the data + * pointer and buffer length. + * + * @param buf Buffer to update. + * @param len Number of bytes to remove. + * + * @return New beginning of the buffer data. + */ +void *net_buf_simple_pull(struct net_buf_simple *buf, size_t len); + +/** + * @brief Remove data from the beginning of the buffer. + * + * Removes data from the beginning of the buffer by modifying the data + * pointer and buffer length. + * + * @param buf Buffer to update. + * @param len Number of bytes to remove. + * + * @return Pointer to the old location of the buffer data. + */ +void *net_buf_simple_pull_mem(struct net_buf_simple *buf, size_t len); + +/** + * @brief Remove a 8-bit value from the beginning of the buffer + * + * Same idea as with net_buf_simple_pull(), but a helper for operating + * on 8-bit values. + * + * @param buf A valid pointer on a buffer. + * + * @return The 8-bit removed value + */ +u8_t net_buf_simple_pull_u8(struct net_buf_simple *buf); + +/** + * @brief Remove and convert 16 bits from the beginning of the buffer. + * + * Same idea as with net_buf_simple_pull(), but a helper for operating + * on 16-bit little endian data. + * + * @param buf A valid pointer on a buffer. + * + * @return 16-bit value converted from little endian to host endian. + */ +u16_t net_buf_simple_pull_le16(struct net_buf_simple *buf); + +/** + * @brief Remove and convert 16 bits from the beginning of the buffer. + * + * Same idea as with net_buf_simple_pull(), but a helper for operating + * on 16-bit big endian data. + * + * @param buf A valid pointer on a buffer. + * + * @return 16-bit value converted from big endian to host endian. + */ +u16_t net_buf_simple_pull_be16(struct net_buf_simple *buf); + +/** + * @brief Remove and convert 32 bits from the beginning of the buffer. + * + * Same idea as with net_buf_simple_pull(), but a helper for operating + * on 32-bit little endian data. + * + * @param buf A valid pointer on a buffer. + * + * @return 32-bit value converted from little endian to host endian. + */ +u32_t net_buf_simple_pull_le32(struct net_buf_simple *buf); + +/** + * @brief Remove and convert 32 bits from the beginning of the buffer. + * + * Same idea as with net_buf_simple_pull(), but a helper for operating + * on 32-bit big endian data. + * + * @param buf A valid pointer on a buffer. + * + * @return 32-bit value converted from big endian to host endian. + */ +u32_t net_buf_simple_pull_be32(struct net_buf_simple *buf); + +/** + * @brief Get the tail pointer for a buffer. + * + * Get a pointer to the end of the data in a buffer. + * + * @param buf Buffer. + * + * @return Tail pointer for the buffer. + */ +static inline u8_t *net_buf_simple_tail(struct net_buf_simple *buf) +{ + return buf->data + buf->len; +} + +/** + * @brief Check buffer headroom. + * + * Check how much free space there is in the beginning of the buffer. + * + * buf A valid pointer on a buffer + * + * @return Number of bytes available in the beginning of the buffer. + */ +size_t net_buf_simple_headroom(struct net_buf_simple *buf); + +/** + * @brief Check buffer tailroom. + * + * Check how much free space there is at the end of the buffer. + * + * @param buf A valid pointer on a buffer + * + * @return Number of bytes available at the end of the buffer. + */ +size_t net_buf_simple_tailroom(struct net_buf_simple *buf); + +/** + * @brief Parsing state of a buffer. + * + * This is used for temporarily storing the parsing state of a buffer + * while giving control of the parsing to a routine which we don't + * control. + */ +struct net_buf_simple_state { + /** Offset of the data pointer from the beginning of the storage */ + u16_t offset; + /** Length of data */ + u16_t len; +}; + +/** + * @brief Save the parsing state of a buffer. + * + * Saves the parsing state of a buffer so it can be restored later. + * + * @param buf Buffer from which the state should be saved. + * @param state Storage for the state. + */ +static inline void net_buf_simple_save(struct net_buf_simple *buf, + struct net_buf_simple_state *state) +{ + state->offset = net_buf_simple_headroom(buf); + state->len = buf->len; +} + +/** + * @brief Restore the parsing state of a buffer. + * + * Restores the parsing state of a buffer from a state previously stored + * by net_buf_simple_save(). + * + * @param buf Buffer to which the state should be restored. + * @param state Stored state. + */ +static inline void net_buf_simple_restore(struct net_buf_simple *buf, + struct net_buf_simple_state *state) +{ + buf->data = buf->__buf + state->offset; + buf->len = state->len; +} + +/** + * @brief Initialize buffer with the given headroom. + * + * The buffer is not expected to contain any data when this API is called. + * + * @param buf Buffer to initialize. + * @param reserve How much headroom to reserve. + */ +void net_buf_simple_reserve(struct net_buf_simple *buf, size_t reserve); + +/** + * Flag indicating that the buffer has associated fragments. Only used + * internally by the buffer handling code while the buffer is inside a + * FIFO, meaning this never needs to be explicitly set or unset by the + * net_buf API user. As long as the buffer is outside of a FIFO, i.e. + * in practice always for the user for this API, the buf->frags pointer + * should be used instead. + */ +#define NET_BUF_FRAGS BIT(0) + +/** + * @brief Network buffer representation. + * + * This struct is used to represent network buffers. Such buffers are + * normally defined through the NET_BUF_POOL_*_DEFINE() APIs and allocated + * using the net_buf_alloc() API. + */ +struct net_buf { + union { + /** Allow placing the buffer into sys_slist_t */ + sys_snode_t node; + + /** Fragments associated with this buffer. */ + struct net_buf *frags; + }; + + /** Reference count. */ + u8_t ref; + + /** Bit-field of buffer flags. */ + u8_t flags; + + /** Where the buffer should go when freed up. */ + struct net_buf_pool *pool; + + /* Union for convenience access to the net_buf_simple members, also + * preserving the old API. + */ + union { + /* The ABI of this struct must match net_buf_simple */ + struct { + /** Pointer to the start of data in the buffer. */ + u8_t *data; + + /** Length of the data behind the data pointer. */ + u16_t len; + + /** Amount of data that this buffer can store. */ + u16_t size; + + /** Start of the data storage. Not to be accessed + * directly (the data pointer should be used + * instead). + */ + u8_t *__buf; + }; + + struct net_buf_simple b; + }; + + /** System metadata for this buffer. */ + u8_t user_data[BLE_MESH_NET_BUF_USER_DATA_SIZE] __net_buf_align; +}; + +struct net_buf_data_cb { + u8_t * (*alloc)(struct net_buf *buf, size_t *size, s32_t timeout); + u8_t * (*ref)(struct net_buf *buf, u8_t *data); + void (*unref)(struct net_buf *buf, u8_t *data); +}; + +struct net_buf_data_alloc { + const struct net_buf_data_cb *cb; + void *alloc_data; +}; + +struct net_buf_pool { + /** Number of buffers in pool */ + const u16_t buf_count; + + /** Number of uninitialized buffers */ + u16_t uninit_count; + +#if defined(CONFIG_BLE_MESH_NET_BUF_POOL_USAGE) + /** Amount of available buffers in the pool. */ + s16_t avail_count; + + /** Total size of the pool. */ + const u16_t pool_size; + + /** Name of the pool. Used when printing pool information. */ + const char *name; +#endif /* CONFIG_BLE_MESH_NET_BUF_POOL_USAGE */ + + /** Optional destroy callback when buffer is freed. */ + void (*const destroy)(struct net_buf *buf); + + /** Data allocation handlers. */ + const struct net_buf_data_alloc *alloc; + + /** Helper to access the start of storage (for net_buf_pool_init) */ + struct net_buf *const __bufs; +}; + +#if defined(CONFIG_BLE_MESH_NET_BUF_POOL_USAGE) +#define NET_BUF_POOL_INITIALIZER(_pool, _alloc, _bufs, _count, _destroy) \ + { \ + .alloc = _alloc, \ + .__bufs = (struct net_buf *)_bufs, \ + .buf_count = _count, \ + .uninit_count = _count, \ + .avail_count = _count, \ + .destroy = _destroy, \ + .name = STRINGIFY(_pool), \ + } +#else +#define NET_BUF_POOL_INITIALIZER(_pool, _alloc, _bufs, _count, _destroy) \ + { \ + .alloc = _alloc, \ + .__bufs = (struct net_buf *)_bufs, \ + .buf_count = _count, \ + .uninit_count = _count, \ + .destroy = _destroy, \ + } +#endif /* CONFIG_BLE_MESH_NET_BUF_POOL_USAGE */ + +struct net_buf_pool_fixed { + size_t data_size; + u8_t *data_pool; +}; + +/** @cond INTERNAL_HIDDEN */ +extern const struct net_buf_data_cb net_buf_fixed_cb; + +/** + * @def NET_BUF_POOL_FIXED_DEFINE + * @brief Define a new pool for buffers based on fixed-size data + * + * Defines a net_buf_pool struct and the necessary memory storage (array of + * structs) for the needed amount of buffers. After this, the buffers can be + * accessed from the pool through net_buf_alloc. The pool is defined as a + * static variable, so if it needs to be exported outside the current module + * this needs to happen with the help of a separate pointer rather than an + * extern declaration. + * + * The data payload of the buffers will be allocated from a byte array + * of fixed sized chunks. This kind of pool does not support blocking on + * the data allocation, so the timeout passed to net_buf_alloc will be + * always treated as K_NO_WAIT when trying to allocate the data. This means + * that allocation failures, i.e. NULL returns, must always be handled + * cleanly. + * + * If provided with a custom destroy callback, this callback is + * responsible for eventually calling net_buf_destroy() to complete the + * process of returning the buffer to the pool. + * + * @param _name Name of the pool variable. + * @param _count Number of buffers in the pool. + * @param _data_size Maximum data payload per buffer. + * @param _destroy Optional destroy callback when buffer is freed. + */ +#define NET_BUF_POOL_FIXED_DEFINE(_name, _count, _data_size, _destroy) \ + static struct net_buf net_buf_##_name[_count]; \ + static u8_t net_buf_data_##_name[_count][_data_size]; \ + static const struct net_buf_pool_fixed net_buf_fixed_##_name = { \ + .data_size = _data_size, \ + .data_pool = (u8_t *)net_buf_data_##_name, \ + }; \ + static const struct net_buf_data_alloc net_buf_fixed_alloc_##_name = { \ + .cb = &net_buf_fixed_cb, \ + .alloc_data = (void *)&net_buf_fixed_##_name, \ + }; \ + struct net_buf_pool _name __net_buf_align \ + __in_section(_net_buf_pool, static, _name) = \ + NET_BUF_POOL_INITIALIZER(_name, &net_buf_fixed_alloc_##_name, \ + net_buf_##_name, _count, _destroy) + +/** + * @def NET_BUF_POOL_DEFINE + * @brief Define a new pool for buffers + * + * Defines a net_buf_pool struct and the necessary memory storage (array of + * structs) for the needed amount of buffers. After this,the buffers can be + * accessed from the pool through net_buf_alloc. The pool is defined as a + * static variable, so if it needs to be exported outside the current module + * this needs to happen with the help of a separate pointer rather than an + * extern declaration. + * + * If provided with a custom destroy callback this callback is + * responsible for eventually calling net_buf_destroy() to complete the + * process of returning the buffer to the pool. + * + * @param _name Name of the pool variable. + * @param _count Number of buffers in the pool. + * @param _size Maximum data size for each buffer. + * @param _ud_size Amount of user data space to reserve. + * @param _destroy Optional destroy callback when buffer is freed. + */ +#define NET_BUF_POOL_DEFINE(_name, _count, _size, _ud_size, _destroy) \ + NET_BUF_POOL_FIXED_DEFINE(_name, _count, _size, _destroy) + +/** + * @brief Get a zero-based index for a buffer. + * + * This function will translate a buffer into a zero-based index, + * based on its placement in its buffer pool. This can be useful if you + * want to associate an external array of meta-data contexts with the + * buffers of a pool. + * + * @param buf Network buffer. + * + * @return Zero-based index for the buffer. + */ +int net_buf_id(struct net_buf *buf); + +/** + * @brief Allocate a new fixed buffer from a pool. + * + * @param pool Which pool to allocate the buffer from. + * @param timeout Affects the action taken should the pool be empty. + * If K_NO_WAIT, then return immediately. If K_FOREVER, then + * wait as long as necessary. Otherwise, wait up to the specified + * number of milliseconds before timing out. Note that some types + * of data allocators do not support blocking (such as the HEAP + * type). In this case it's still possible for net_buf_alloc() to + * fail (return NULL) even if it was given K_FOREVER. + * + * @return New buffer or NULL if out of buffers. + */ +#if defined(CONFIG_BLE_MESH_NET_BUF_LOG) +struct net_buf *net_buf_alloc_fixed_debug(struct net_buf_pool *pool, s32_t timeout, + const char *func, int line); +#define net_buf_alloc_fixed(_pool, _timeout) \ + net_buf_alloc_fixed_debug(_pool, _timeout, __func__, __LINE__) +#else +struct net_buf *net_buf_alloc_fixed(struct net_buf_pool *pool, s32_t timeout); +#endif + +/** + * @def net_buf_alloc + * + * @copydetails net_buf_alloc_fixed + */ +#define net_buf_alloc(pool, timeout) net_buf_alloc_fixed(pool, timeout) + +/** + * @brief Reset buffer + * + * Reset buffer data and flags so it can be reused for other purposes. + * + * @param buf Buffer to reset. + */ +void net_buf_reset(struct net_buf *buf); + +/** + * @def net_buf_reserve + * @brief Initialize buffer with the given headroom. + * + * The buffer is not expected to contain any data when this API is called. + * + * @param buf Buffer to initialize. + * @param reserve How much headroom to reserve. + */ +#define net_buf_reserve(buf, reserve) net_buf_simple_reserve(&(buf)->b, reserve) + +/** + * @brief Put a buffer into a list + * + * Put a buffer to the end of a list. If the buffer contains follow-up + * fragments this function will take care of inserting them as well + * into the list. + * + * @param list Which list to append the buffer to. + * @param buf Buffer. + */ +void net_buf_slist_put(sys_slist_t *list, struct net_buf *buf); + +/** + * @brief Get a buffer from a list. + * + * Get buffer from a list. If the buffer had any fragments, these will + * automatically be recovered from the list as well and be placed to + * the buffer's fragment list. + * + * @param list Which list to take the buffer from. + * + * @return New buffer or NULL if the FIFO is empty. + */ +struct net_buf *net_buf_slist_get(sys_slist_t *list); + +/** + * @brief Decrements the reference count of a buffer. + * + * Decrements the reference count of a buffer and puts it back into the + * pool if the count reaches zero. + * + * @param buf A valid pointer on a buffer + */ +#if defined(CONFIG_BLE_MESH_NET_BUF_LOG) +void net_buf_unref_debug(struct net_buf *buf, const char *func, int line); +#define net_buf_unref(_buf) \ + net_buf_unref_debug(_buf, __func__, __LINE__) +#else +void net_buf_unref(struct net_buf *buf); +#endif + +/** + * @brief Increment the reference count of a buffer. + * + * @param buf A valid pointer on a buffer + * + * @return the buffer newly referenced + */ +struct net_buf *net_buf_ref(struct net_buf *buf); + +/** + * @brief Get a pointer to the user data of a buffer. + * + * @param buf A valid pointer on a buffer + * + * @return Pointer to the user data of the buffer. + */ +static inline void *net_buf_user_data(struct net_buf *buf) +{ + return (void *)buf->user_data; +} + +/** + * @def net_buf_add + * @brief Prepare data to be added at the end of the buffer + * + * Increments the data length of a buffer to account for more data + * at the end. + * + * @param buf Buffer to update. + * @param len Number of bytes to increment the length with. + * + * @return The original tail of the buffer. + */ +#define net_buf_add(buf, len) net_buf_simple_add(&(buf)->b, len) + +/** + * @def net_buf_add_mem + * @brief Copy bytes from memory to the end of the buffer + * + * Copies the given number of bytes to the end of the buffer. Increments the + * data length of the buffer to account for more data at the end. + * + * @param buf Buffer to update. + * @param mem Location of data to be added. + * @param len Length of data to be added + * + * @return The original tail of the buffer. + */ +#define net_buf_add_mem(buf, mem, len) net_buf_simple_add_mem(&(buf)->b, mem, len) + +/** + * @def net_buf_add_u8 + * @brief Add (8-bit) byte at the end of the buffer + * + * Adds a byte at the end of the buffer. Increments the data length of + * the buffer to account for more data at the end. + * + * @param buf Buffer to update. + * @param val byte value to be added. + * + * @return Pointer to the value added + */ +#define net_buf_add_u8(buf, val) net_buf_simple_add_u8(&(buf)->b, val) + +/** + * @def net_buf_add_le16 + * @brief Add 16-bit value at the end of the buffer + * + * Adds 16-bit value in little endian format at the end of buffer. + * Increments the data length of a buffer to account for more data + * at the end. + * + * @param buf Buffer to update. + * @param val 16-bit value to be added. + */ +#define net_buf_add_le16(buf, val) net_buf_simple_add_le16(&(buf)->b, val) + +/** + * @def net_buf_add_be16 + * @brief Add 16-bit value at the end of the buffer + * + * Adds 16-bit value in big endian format at the end of buffer. + * Increments the data length of a buffer to account for more data + * at the end. + * + * @param buf Buffer to update. + * @param val 16-bit value to be added. + */ +#define net_buf_add_be16(buf, val) net_buf_simple_add_be16(&(buf)->b, val) + +/** + * @def net_buf_add_le32 + * @brief Add 32-bit value at the end of the buffer + * + * Adds 32-bit value in little endian format at the end of buffer. + * Increments the data length of a buffer to account for more data + * at the end. + * + * @param buf Buffer to update. + * @param val 32-bit value to be added. + */ +#define net_buf_add_le32(buf, val) net_buf_simple_add_le32(&(buf)->b, val) + +/** + * @def net_buf_add_be32 + * @brief Add 32-bit value at the end of the buffer + * + * Adds 32-bit value in big endian format at the end of buffer. + * Increments the data length of a buffer to account for more data + * at the end. + * + * @param buf Buffer to update. + * @param val 32-bit value to be added. + */ +#define net_buf_add_be32(buf, val) net_buf_simple_add_be32(&(buf)->b, val) + +/** + * @def net_buf_push + * @brief Push data to the beginning of the buffer. + * + * Modifies the data pointer and buffer length to account for more data + * in the beginning of the buffer. + * + * @param buf Buffer to update. + * @param len Number of bytes to add to the beginning. + * + * @return The new beginning of the buffer data. + */ +#define net_buf_push(buf, len) net_buf_simple_push(&(buf)->b, len) + +/** + * @def net_buf_push_le16 + * @brief Push 16-bit value to the beginning of the buffer + * + * Adds 16-bit value in little endian format to the beginning of the + * buffer. + * + * @param buf Buffer to update. + * @param val 16-bit value to be pushed to the buffer. + */ +#define net_buf_push_le16(buf, val) net_buf_simple_push_le16(&(buf)->b, val) + +/** + * @def net_buf_push_be16 + * @brief Push 16-bit value to the beginning of the buffer + * + * Adds 16-bit value in little endian format to the beginning of the + * buffer. + * + * @param buf Buffer to update. + * @param val 16-bit value to be pushed to the buffer. + */ +#define net_buf_push_be16(buf, val) net_buf_simple_push_be16(&(buf)->b, val) + +/** + * @def net_buf_push_u8 + * @brief Push 8-bit value to the beginning of the buffer + * + * Adds 8-bit value the beginning of the buffer. + * + * @param buf Buffer to update. + * @param val 8-bit value to be pushed to the buffer. + */ +#define net_buf_push_u8(buf, val) net_buf_simple_push_u8(&(buf)->b, val) + +/** + * @def net_buf_pull + * @brief Remove data from the beginning of the buffer. + * + * Removes data from the beginning of the buffer by modifying the data + * pointer and buffer length. + * + * @param buf Buffer to update. + * @param len Number of bytes to remove. + * + * @return New beginning of the buffer data. + */ +#define net_buf_pull(buf, len) net_buf_simple_pull(&(buf)->b, len) + +/** + * @def net_buf_pull_u8 + * @brief Remove a 8-bit value from the beginning of the buffer + * + * Same idea as with net_buf_pull(), but a helper for operating on + * 8-bit values. + * + * @param buf A valid pointer on a buffer. + * + * @return The 8-bit removed value + */ +#define net_buf_pull_u8(buf) net_buf_simple_pull_u8(&(buf)->b) + +/** + * @def net_buf_pull_le16 + * @brief Remove and convert 16 bits from the beginning of the buffer. + * + * Same idea as with net_buf_pull(), but a helper for operating on + * 16-bit little endian data. + * + * @param buf A valid pointer on a buffer. + * + * @return 16-bit value converted from little endian to host endian. + */ +#define net_buf_pull_le16(buf) net_buf_simple_pull_le16(&(buf)->b) + +/** + * @def net_buf_pull_be16 + * @brief Remove and convert 16 bits from the beginning of the buffer. + * + * Same idea as with net_buf_pull(), but a helper for operating on + * 16-bit big endian data. + * + * @param buf A valid pointer on a buffer. + * + * @return 16-bit value converted from big endian to host endian. + */ +#define net_buf_pull_be16(buf) net_buf_simple_pull_be16(&(buf)->b) + +/** + * @def net_buf_pull_le32 + * @brief Remove and convert 32 bits from the beginning of the buffer. + * + * Same idea as with net_buf_pull(), but a helper for operating on + * 32-bit little endian data. + * + * @param buf A valid pointer on a buffer. + * + * @return 32-bit value converted from little endian to host endian. + */ +#define net_buf_pull_le32(buf) net_buf_simple_pull_le32(&(buf)->b) + +/** + * @def net_buf_pull_be32 + * @brief Remove and convert 32 bits from the beginning of the buffer. + * + * Same idea as with net_buf_pull(), but a helper for operating on + * 32-bit big endian data. + * + * @param buf A valid pointer on a buffer + * + * @return 32-bit value converted from big endian to host endian. + */ +#define net_buf_pull_be32(buf) net_buf_simple_pull_be32(&(buf)->b) + +/** + * @def net_buf_tailroom + * @brief Check buffer tailroom. + * + * Check how much free space there is at the end of the buffer. + * + * @param buf A valid pointer on a buffer + * + * @return Number of bytes available at the end of the buffer. + */ +#define net_buf_tailroom(buf) net_buf_simple_tailroom(&(buf)->b) + +/** + * @def net_buf_headroom + * @brief Check buffer headroom. + * + * Check how much free space there is in the beginning of the buffer. + * + * buf A valid pointer on a buffer + * + * @return Number of bytes available in the beginning of the buffer. + */ +#define net_buf_headroom(buf) net_buf_simple_headroom(&(buf)->b) + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* _BLE_MESH_BUF_H_ */ + diff --git a/components/bt/ble_mesh/mesh_core/include/mesh_dlist.h b/components/bt/ble_mesh/mesh_core/include/mesh_dlist.h new file mode 100644 index 0000000000..31eef746ec --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/include/mesh_dlist.h @@ -0,0 +1,496 @@ +/* + * Copyright (c) 2013-2015 Wind River Systems, Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief Doubly-linked list implementation + * + * Doubly-linked list implementation using inline macros/functions. + * This API is not thread safe, and thus if a list is used across threads, + * calls to functions must be protected with synchronization primitives. + * + * The lists are expected to be initialized such that both the head and tail + * pointers point to the list itself. Initializing the lists in such a fashion + * simplifies the adding and removing of nodes to/from the list. + */ + +#ifndef _BLE_MESH_DLIST_H_ +#define _BLE_MESH_DLIST_H_ + +#include +#include "mesh_util.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +struct _dnode { + union { + struct _dnode *head; /* ptr to head of list (sys_dlist_t) */ + struct _dnode *next; /* ptr to next node (sys_dnode_t) */ + }; + union { + struct _dnode *tail; /* ptr to tail of list (sys_dlist_t) */ + struct _dnode *prev; /* ptr to previous node (sys_dnode_t) */ + }; +}; + +typedef struct _dnode sys_dlist_t; +typedef struct _dnode sys_dnode_t; + +/** + * @brief Provide the primitive to iterate on a list + * Note: the loop is unsafe and thus __dn should not be removed + * + * User _MUST_ add the loop statement curly braces enclosing its own code: + * + * SYS_DLIST_FOR_EACH_NODE(l, n) { + * + * } + * + * This and other SYS_DLIST_*() macros are not thread safe. + * + * @param __dl A pointer on a sys_dlist_t to iterate on + * @param __dn A sys_dnode_t pointer to peek each node of the list + */ +#define SYS_DLIST_FOR_EACH_NODE(__dl, __dn) \ + for (__dn = sys_dlist_peek_head(__dl); __dn; \ + __dn = sys_dlist_peek_next(__dl, __dn)) + +/** + * @brief Provide the primitive to iterate on a list, from a node in the list + * Note: the loop is unsafe and thus __dn should not be removed + * + * User _MUST_ add the loop statement curly braces enclosing its own code: + * + * SYS_DLIST_ITERATE_FROM_NODE(l, n) { + * + * } + * + * Like SYS_DLIST_FOR_EACH_NODE(), but __dn already contains a node in the list + * where to start searching for the next entry from. If NULL, it starts from + * the head. + * + * This and other SYS_DLIST_*() macros are not thread safe. + * + * @param __dl A pointer on a sys_dlist_t to iterate on + * @param __dn A sys_dnode_t pointer to peek each node of the list; + * it contains the starting node, or NULL to start from the head + */ +#define SYS_DLIST_ITERATE_FROM_NODE(__dl, __dn) \ + for (__dn = __dn ? sys_dlist_peek_next_no_check(__dl, __dn) \ + : sys_dlist_peek_head(__dl); \ + __dn; \ + __dn = sys_dlist_peek_next(__dl, __dn)) + +/** + * @brief Provide the primitive to safely iterate on a list + * Note: __dn can be removed, it will not break the loop. + * + * User _MUST_ add the loop statement curly braces enclosing its own code: + * + * SYS_DLIST_FOR_EACH_NODE_SAFE(l, n, s) { + * + * } + * + * This and other SYS_DLIST_*() macros are not thread safe. + * + * @param __dl A pointer on a sys_dlist_t to iterate on + * @param __dn A sys_dnode_t pointer to peek each node of the list + * @param __dns A sys_dnode_t pointer for the loop to run safely + */ +#define SYS_DLIST_FOR_EACH_NODE_SAFE(__dl, __dn, __dns) \ + for (__dn = sys_dlist_peek_head(__dl), \ + __dns = sys_dlist_peek_next(__dl, __dn); \ + __dn; __dn = __dns, \ + __dns = sys_dlist_peek_next(__dl, __dn)) + +/* + * @brief Provide the primitive to resolve the container of a list node + * Note: it is safe to use with NULL pointer nodes + * + * @param __dn A pointer on a sys_dnode_t to get its container + * @param __cn Container struct type pointer + * @param __n The field name of sys_dnode_t within the container struct + */ +#define SYS_DLIST_CONTAINER(__dn, __cn, __n) \ + (__dn ? CONTAINER_OF(__dn, __typeof__(*__cn), __n) : NULL) +/* + * @brief Provide the primitive to peek container of the list head + * + * @param __dl A pointer on a sys_dlist_t to peek + * @param __cn Container struct type pointer + * @param __n The field name of sys_dnode_t within the container struct + */ +#define SYS_DLIST_PEEK_HEAD_CONTAINER(__dl, __cn, __n) \ + SYS_DLIST_CONTAINER(sys_dlist_peek_head(__dl), __cn, __n) + +/* + * @brief Provide the primitive to peek the next container + * + * @param __dl A pointer on a sys_dlist_t to peek + * @param __cn Container struct type pointer + * @param __n The field name of sys_dnode_t within the container struct + */ +#define SYS_DLIST_PEEK_NEXT_CONTAINER(__dl, __cn, __n) \ + ((__cn) ? SYS_DLIST_CONTAINER(sys_dlist_peek_next(__dl, &(__cn->__n)), \ + __cn, __n) : NULL) + +/** + * @brief Provide the primitive to iterate on a list under a container + * Note: the loop is unsafe and thus __cn should not be detached + * + * User _MUST_ add the loop statement curly braces enclosing its own code: + * + * SYS_DLIST_FOR_EACH_CONTAINER(l, c, n) { + * + * } + * + * @param __dl A pointer on a sys_dlist_t to iterate on + * @param __cn A pointer to peek each entry of the list + * @param __n The field name of sys_dnode_t within the container struct + */ +#define SYS_DLIST_FOR_EACH_CONTAINER(__dl, __cn, __n) \ + for (__cn = SYS_DLIST_PEEK_HEAD_CONTAINER(__dl, __cn, __n); __cn; \ + __cn = SYS_DLIST_PEEK_NEXT_CONTAINER(__dl, __cn, __n)) + +/** + * @brief Provide the primitive to safely iterate on a list under a container + * Note: __cn can be detached, it will not break the loop. + * + * User _MUST_ add the loop statement curly braces enclosing its own code: + * + * SYS_DLIST_FOR_EACH_CONTAINER_SAFE(l, c, cn, n) { + * + * } + * + * @param __dl A pointer on a sys_dlist_t to iterate on + * @param __cn A pointer to peek each entry of the list + * @param __cns A pointer for the loop to run safely + * @param __n The field name of sys_dnode_t within the container struct + */ +#define SYS_DLIST_FOR_EACH_CONTAINER_SAFE(__dl, __cn, __cns, __n) \ + for (__cn = SYS_DLIST_PEEK_HEAD_CONTAINER(__dl, __cn, __n), \ + __cns = SYS_DLIST_PEEK_NEXT_CONTAINER(__dl, __cn, __n); __cn; \ + __cn = __cns, \ + __cns = SYS_DLIST_PEEK_NEXT_CONTAINER(__dl, __cn, __n)) + +/** + * @brief initialize list + * + * @param list the doubly-linked list + * + * @return N/A + */ + +static inline void sys_dlist_init(sys_dlist_t *list) +{ + list->head = (sys_dnode_t *)list; + list->tail = (sys_dnode_t *)list; +} + +#define SYS_DLIST_STATIC_INIT(ptr_to_list) {{(ptr_to_list)}, {(ptr_to_list)}} + +/** + * @brief check if a node is the list's head + * + * @param list the doubly-linked list to operate on + * @param node the node to check + * + * @return 1 if node is the head, 0 otherwise + */ + +static inline int sys_dlist_is_head(sys_dlist_t *list, sys_dnode_t *node) +{ + return list->head == node; +} + +/** + * @brief check if a node is the list's tail + * + * @param list the doubly-linked list to operate on + * @param node the node to check + * + * @return 1 if node is the tail, 0 otherwise + */ + +static inline int sys_dlist_is_tail(sys_dlist_t *list, sys_dnode_t *node) +{ + return list->tail == node; +} + +/** + * @brief check if the list is empty + * + * @param list the doubly-linked list to operate on + * + * @return 1 if empty, 0 otherwise + */ + +static inline int sys_dlist_is_empty(sys_dlist_t *list) +{ + return list->head == list; +} + +/** + * @brief check if more than one node present + * + * This and other sys_dlist_*() functions are not thread safe. + * + * @param list the doubly-linked list to operate on + * + * @return 1 if multiple nodes, 0 otherwise + */ + +static inline int sys_dlist_has_multiple_nodes(sys_dlist_t *list) +{ + return list->head != list->tail; +} + +/** + * @brief get a reference to the head item in the list + * + * @param list the doubly-linked list to operate on + * + * @return a pointer to the head element, NULL if list is empty + */ + +static inline sys_dnode_t *sys_dlist_peek_head(sys_dlist_t *list) +{ + return sys_dlist_is_empty(list) ? NULL : list->head; +} + +/** + * @brief get a reference to the head item in the list + * + * The list must be known to be non-empty. + * + * @param list the doubly-linked list to operate on + * + * @return a pointer to the head element + */ + +static inline sys_dnode_t *sys_dlist_peek_head_not_empty(sys_dlist_t *list) +{ + return list->head; +} + +/** + * @brief get a reference to the next item in the list, node is not NULL + * + * Faster than sys_dlist_peek_next() if node is known not to be NULL. + * + * @param list the doubly-linked list to operate on + * @param node the node from which to get the next element in the list + * + * @return a pointer to the next element from a node, NULL if node is the tail + */ + +static inline sys_dnode_t *sys_dlist_peek_next_no_check(sys_dlist_t *list, + sys_dnode_t *node) +{ + return (node == list->tail) ? NULL : node->next; +} + +/** + * @brief get a reference to the next item in the list + * + * @param list the doubly-linked list to operate on + * @param node the node from which to get the next element in the list + * + * @return a pointer to the next element from a node, NULL if node is the tail + * or NULL (when node comes from reading the head of an empty list). + */ + +static inline sys_dnode_t *sys_dlist_peek_next(sys_dlist_t *list, + sys_dnode_t *node) +{ + return node ? sys_dlist_peek_next_no_check(list, node) : NULL; +} + +/** + * @brief get a reference to the tail item in the list + * + * @param list the doubly-linked list to operate on + * + * @return a pointer to the tail element, NULL if list is empty + */ + +static inline sys_dnode_t *sys_dlist_peek_tail(sys_dlist_t *list) +{ + return sys_dlist_is_empty(list) ? NULL : list->tail; +} + +/** + * @brief add node to tail of list + * + * This and other sys_dlist_*() functions are not thread safe. + * + * @param list the doubly-linked list to operate on + * @param node the element to append + * + * @return N/A + */ + +static inline void sys_dlist_append(sys_dlist_t *list, sys_dnode_t *node) +{ + node->next = list; + node->prev = list->tail; + + list->tail->next = node; + list->tail = node; +} + +/** + * @brief add node to head of list + * + * This and other sys_dlist_*() functions are not thread safe. + * + * @param list the doubly-linked list to operate on + * @param node the element to append + * + * @return N/A + */ + +static inline void sys_dlist_prepend(sys_dlist_t *list, sys_dnode_t *node) +{ + node->next = list->head; + node->prev = list; + + list->head->prev = node; + list->head = node; +} + +/** + * @brief insert node after a node + * + * Insert a node after a specified node in a list. + * This and other sys_dlist_*() functions are not thread safe. + * + * @param list the doubly-linked list to operate on + * @param insert_point the insert point in the list: if NULL, insert at head + * @param node the element to append + * + * @return N/A + */ + +static inline void sys_dlist_insert_after(sys_dlist_t *list, + sys_dnode_t *insert_point, sys_dnode_t *node) +{ + if (!insert_point) { + sys_dlist_prepend(list, node); + } else { + node->next = insert_point->next; + node->prev = insert_point; + insert_point->next->prev = node; + insert_point->next = node; + } +} + +/** + * @brief insert node before a node + * + * Insert a node before a specified node in a list. + * This and other sys_dlist_*() functions are not thread safe. + * + * @param list the doubly-linked list to operate on + * @param insert_point the insert point in the list: if NULL, insert at tail + * @param node the element to insert + * + * @return N/A + */ + +static inline void sys_dlist_insert_before(sys_dlist_t *list, + sys_dnode_t *insert_point, sys_dnode_t *node) +{ + if (!insert_point) { + sys_dlist_append(list, node); + } else { + node->prev = insert_point->prev; + node->next = insert_point; + insert_point->prev->next = node; + insert_point->prev = node; + } +} + +/** + * @brief insert node at position + * + * Insert a node in a location depending on a external condition. The cond() + * function checks if the node is to be inserted _before_ the current node + * against which it is checked. + * This and other sys_dlist_*() functions are not thread safe. + * + * @param list the doubly-linked list to operate on + * @param node the element to insert + * @param cond a function that determines if the current node is the correct + * insert point + * @param data parameter to cond() + * + * @return N/A + */ + +static inline void sys_dlist_insert_at(sys_dlist_t *list, sys_dnode_t *node, + int (*cond)(sys_dnode_t *, void *), void *data) +{ + if (sys_dlist_is_empty(list)) { + sys_dlist_append(list, node); + } else { + sys_dnode_t *pos = sys_dlist_peek_head(list); + + while (pos && !cond(pos, data)) { + pos = sys_dlist_peek_next(list, pos); + } + sys_dlist_insert_before(list, pos, node); + } +} + +/** + * @brief remove a specific node from a list + * + * The list is implicit from the node. The node must be part of a list. + * This and other sys_dlist_*() functions are not thread safe. + * + * @param node the node to remove + * + * @return N/A + */ + +static inline void sys_dlist_remove(sys_dnode_t *node) +{ + node->prev->next = node->next; + node->next->prev = node->prev; +} + +/** + * @brief get the first node in a list + * + * This and other sys_dlist_*() functions are not thread safe. + * + * @param list the doubly-linked list to operate on + * + * @return the first node in the list, NULL if list is empty + */ + +static inline sys_dnode_t *sys_dlist_get(sys_dlist_t *list) +{ + sys_dnode_t *node; + + if (sys_dlist_is_empty(list)) { + return NULL; + } + + node = list->head; + sys_dlist_remove(node); + return node; +} + +#ifdef __cplusplus +} +#endif + +#endif /* _BLE_MESH_DLIST_H_ */ diff --git a/components/bt/ble_mesh/mesh_core/include/mesh_hci.h b/components/bt/ble_mesh/mesh_core/include/mesh_hci.h new file mode 100644 index 0000000000..1c3e093e0b --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/include/mesh_hci.h @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2015-2016 Intel Corporation + * Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef _BLE_MESH_HCI_H_ +#define _BLE_MESH_HCI_H_ + +#include "mesh_kernel.h" +#include "mesh_bearer_adapt.h" +#include "mesh_atomic.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Porting form zephyr/subsys/bluetooth/host/hci_core.h */ + +#define BLE_MESH_LMP_FEAT_PAGES_COUNT 1 + +/* bt_mesh_dev flags: the flags defined here represent BT controller state */ +enum { + BLE_MESH_DEV_ENABLE, + BLE_MESH_DEV_READY, + BLE_MESH_DEV_ID_STATIC_RANDOM, + BLE_MESH_DEV_HAS_PUB_KEY, + BLE_MESH_DEV_PUB_KEY_BUSY, + + BLE_MESH_DEV_ADVERTISING, + BLE_MESH_DEV_KEEP_ADVERTISING, + BLE_MESH_DEV_SCANNING, + BLE_MESH_DEV_EXPLICIT_SCAN, + BLE_MESH_DEV_ACTIVE_SCAN, + BLE_MESH_DEV_SCAN_FILTER_DUP, + + BLE_MESH_DEV_RPA_VALID, + + BLE_MESH_DEV_ID_PENDING, + + /* Total number of flags - must be at the end of the enum */ + BLE_MESH_DEV_NUM_FLAGS, +}; + +struct bt_mesh_dev_le { + /* LE features */ + u8_t features[8]; + + /* LE states */ + u64_t states; +}; + +/* State tracking for the local Bluetooth controller */ +struct bt_mesh_dev { + /* Flags indicate which functionality is enabled */ + BLE_MESH_ATOMIC_DEFINE(flags, BLE_MESH_DEV_NUM_FLAGS); + + /* Controller version & manufacturer information */ + u8_t hci_version; + u8_t lmp_version; + u16_t hci_revision; + u16_t lmp_subversion; + u16_t manufacturer; + + /* LMP features (pages 0, 1, 2) */ + u8_t features[BLE_MESH_LMP_FEAT_PAGES_COUNT][8]; + + /* LE controller specific features */ + struct bt_mesh_dev_le le; +}; + +/*Porting from zephyr/subsys/bluetooth/host/hci_core.h */ +/* HCI version from Assigned Numbers */ +#define BLE_MESH_HCI_VERSION_1_0B 0 +#define BLE_MESH_HCI_VERSION_1_1 1 +#define BLE_MESH_HCI_VERSION_1_2 2 +#define BLE_MESH_HCI_VERSION_2_0 3 +#define BLE_MESH_HCI_VERSION_2_1 4 +#define BLE_MESH_HCI_VERSION_3_0 5 +#define BLE_MESH_HCI_VERSION_4_0 6 +#define BLE_MESH_HCI_VERSION_4_1 7 +#define BLE_MESH_HCI_VERSION_4_2 8 +#define BLE_MESH_HCI_VERSION_5_0 9 + +/* OpCode Group Fields */ +#define BLE_MESH_OGF_LINK_CTRL 0x01 +#define BLE_MESH_OGF_BASEBAND 0x03 +#define BLE_MESH_OGF_INFO 0x04 +#define BLE_MESH_OGF_STATUS 0x05 +#define BLE_MESH_OGF_LE 0x08 +#define BLE_MESH_OGF_VS 0x3f + +/* Construct OpCode from OGF and OCF */ +#define BLE_MESH_OP(ogf, ocf) ((ocf) | ((ogf) << 10)) + +/* Obtain OGF from OpCode */ +#define BLE_MESH_OGF(opcode) (((opcode) >> 10) & BIT_MASK(6)) + +/* Obtain OCF from OpCode */ +#define BLE_MESH_OCF(opcode) ((opcode) & BIT_MASK(10)) + +#define BLE_MESH_HCI_OP_SET_ADV_PARAM BLE_MESH_OP(BLE_MESH_OGF_LE, 0x0006) +struct bt_mesh_hci_cp_set_adv_param { + u16_t min_interval; + u16_t max_interval; + u8_t type; + u8_t own_addr_type; + bt_mesh_addr_t direct_addr; + u8_t channel_map; + u8_t filter_policy; +} __packed; + +#define BLE_MESH_HCI_OP_SET_ADV_DATA BLE_MESH_OP(BLE_MESH_OGF_LE, 0x0008) +struct bt_mesh_hci_cp_set_adv_data { + u8_t len; + u8_t data[31]; +} __packed; + +#define BLE_MESH_HCI_OP_SET_SCAN_RSP_DATA BLE_MESH_OP(BLE_MESH_OGF_LE, 0x0009) +struct bt_mesh_hci_cp_set_scan_rsp_data { + u8_t len; + u8_t data[31]; +} __packed; + +/* Added by Espressif */ +extern struct bt_mesh_dev bt_mesh_dev; + +void bt_mesh_hci_init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _BLE_MESH_HCI_H_ */ diff --git a/components/bt/ble_mesh/mesh_core/include/mesh_kernel.h b/components/bt/ble_mesh/mesh_core/include/mesh_kernel.h new file mode 100644 index 0000000000..785b8b8dfb --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/include/mesh_kernel.h @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2016, Wind River Systems, Inc. + * Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _BLE_MESH_KERNEL_H_ +#define _BLE_MESH_KERNEL_H_ + +#include "osi/mutex.h" +#include "mesh_types.h" +#include "mesh_slist.h" +#include "mesh_atomic.h" +#include "mesh_dlist.h" + +/* number of nsec per usec */ +#define NSEC_PER_USEC 1000 + +/* number of microseconds per millisecond */ +#define USEC_PER_MSEC 1000 + +/* number of milliseconds per second */ +#define MSEC_PER_SEC 1000 + +/* number of microseconds per second */ +#define USEC_PER_SEC ((USEC_PER_MSEC) * (MSEC_PER_SEC)) + +/* number of nanoseconds per second */ +#define NSEC_PER_SEC ((NSEC_PER_USEC) * (USEC_PER_MSEC) * (MSEC_PER_SEC)) + +/* timeout is not in use */ +#define _INACTIVE (-1) + +struct k_work; + +/** + * @typedef k_work_handler_t + * @brief Work item handler function type. + * + * A work item's handler function is executed by a workqueue's thread + * when the work item is processed by the workqueue. + * + * @param work Address of the work item. + * + * @return N/A + */ +typedef void (*k_work_handler_t)(struct k_work *work); + +typedef sys_dlist_t _wait_q_t; + +struct k_work { + void *_reserved; + k_work_handler_t handler; + int index; +}; + +#define _K_WORK_INITIALIZER(work_handler) \ +{ \ + ._reserved = NULL, \ + .handler = work_handler, \ +} + +/** + * @brief Generate null timeout delay. + * + * This macro generates a timeout delay that that instructs a kernel API + * not to wait if the requested operation cannot be performed immediately. + * + * @return Timeout delay value. + */ +#define K_NO_WAIT 0 + +/** + * @brief Generate timeout delay from milliseconds. + * + * This macro generates a timeout delay that that instructs a kernel API + * to wait up to @a ms milliseconds to perform the requested operation. + * + * @param ms Duration in milliseconds. + * + * @return Timeout delay value. + */ +#define K_MSEC(ms) (ms) + +/** + * @brief Generate timeout delay from seconds. + * + * This macro generates a timeout delay that that instructs a kernel API + * to wait up to @a s seconds to perform the requested operation. + * + * @param s Duration in seconds. + * + * @return Timeout delay value. + */ +#define K_SECONDS(s) K_MSEC((s) * MSEC_PER_SEC) + +/** + * @brief Generate timeout delay from minutes. + * + * This macro generates a timeout delay that that instructs a kernel API + * to wait up to @a m minutes to perform the requested operation. + * + * @param m Duration in minutes. + * + * @return Timeout delay value. + */ +#define K_MINUTES(m) K_SECONDS((m) * 60) + +/** + * @brief Generate timeout delay from hours. + * + * This macro generates a timeout delay that that instructs a kernel API + * to wait up to @a h hours to perform the requested operation. + * + * @param h Duration in hours. + * + * @return Timeout delay value. + */ +#define K_HOURS(h) K_MINUTES((h) * 60) + +/** + * @brief Generate infinite timeout delay. + * + * This macro generates a timeout delay that that instructs a kernel API + * to wait as long as necessary to perform the requested operation. + * + * @return Timeout delay value. + */ +#define K_FOREVER (-1) + +/** + * @brief Get system uptime (32-bit version). + * + * This routine returns the lower 32-bits of the elapsed time since the system + * booted, in milliseconds. + * + * This routine can be more efficient than k_uptime_get(), as it reduces the + * need for interrupt locking and 64-bit math. However, the 32-bit result + * cannot hold a system uptime time larger than approximately 50 days, so the + * caller must handle possible rollovers. + * + * @return Current uptime. + */ +u32_t k_uptime_get_32(void); + +struct k_delayed_work { + struct k_work work; +}; + +/** + * @brief Submit a delayed work item to the system workqueue. + * + * This routine schedules work item @a work to be processed by the system + * workqueue after a delay of @a delay milliseconds. The routine initiates + * an asynchronous countdown for the work item and then returns to the caller. + * Only when the countdown completes is the work item actually submitted to + * the workqueue and becomes pending. + * + * Submitting a previously submitted delayed work item that is still + * counting down cancels the existing submission and restarts the countdown + * using the new delay. If the work item is currently pending on the + * workqueue's queue because the countdown has completed it is too late to + * resubmit the item, and resubmission fails without impacting the work item. + * If the work item has already been processed, or is currently being processed, + * its work is considered complete and the work item can be resubmitted. + * + * @warning + * Work items submitted to the system workqueue should avoid using handlers + * that block or yield since this may prevent the system workqueue from + * processing other work items in a timely manner. + * + * @note Can be called by ISRs. + * + * @param work Address of delayed work item. + * @param delay Delay before submitting the work item (in milliseconds). + * + * @retval 0 Work item countdown started. + * @retval -EINPROGRESS Work item is already pending. + * @retval -EINVAL Work item is being processed or has completed its work. + * @retval -EADDRINUSE Work item is pending on a different workqueue. + */ +int k_delayed_work_submit(struct k_delayed_work *work, s32_t delay); + +/** + * @brief Get time remaining before a delayed work gets scheduled. + * + * This routine computes the (approximate) time remaining before a + * delayed work gets executed. If the delayed work is not waiting to be + * scheduled, it returns zero. + * + * @param work Delayed work item. + * + * @return Remaining time (in milliseconds). + */ +s32_t k_delayed_work_remaining_get(struct k_delayed_work *work); + +/** + * @brief Submit a work item to the system workqueue. + * + * This routine submits work item @a work to be processed by the system + * workqueue. If the work item is already pending in the workqueue's queue + * as a result of an earlier submission, this routine has no effect on the + * work item. If the work item has already been processed, or is currently + * being processed, its work is considered complete and the work item can be + * resubmitted. + * + * @warning + * Work items submitted to the system workqueue should avoid using handlers + * that block or yield since this may prevent the system workqueue from + * processing other work items in a timely manner. + * + * @note Can be called by ISRs. + * + * @param work Address of work item. + * + * @return N/A + */ +static inline void k_work_submit(struct k_work *work) +{ + if (work && work->handler) { + work->handler(work); + } +} + +/** + * @brief Initialize a work item. + * + * This routine initializes a workqueue work item, prior to its first use. + * + * @param work Address of work item. + * @param handler Function to invoke each time work item is processed. + * + * @return N/A + */ +static inline void k_work_init(struct k_work *work, k_work_handler_t handler) +{ + work->handler = handler; +} + +int k_delayed_work_cancel(struct k_delayed_work *work); + +int k_delayed_work_free(struct k_delayed_work *work); + +void k_delayed_work_init(struct k_delayed_work *work, k_work_handler_t handler); + +/** + * @brief Get system uptime. + * + * This routine returns the elapsed time since the system booted, + * in milliseconds. + * + * @return Current uptime. + */ +s64_t k_uptime_get(void); + +/** + * @brief Put the current thread to sleep. + * + * This routine puts the current thread to sleep for @a duration + * milliseconds. + * + * @param duration Number of milliseconds to sleep. + * + * @return N/A + */ +void k_sleep(s32_t duration); + +unsigned int bt_mesh_irq_lock(void); +void bt_mesh_irq_unlock(unsigned int key); + +void bt_mesh_k_init(void); + +#endif /* _BLE_MESH_KERNEL_H_ */ + diff --git a/components/bt/ble_mesh/mesh_core/include/mesh_main.h b/components/bt/ble_mesh/mesh_core/include/mesh_main.h new file mode 100644 index 0000000000..374c6a0770 --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/include/mesh_main.h @@ -0,0 +1,584 @@ +/** @file + * @brief Bluetooth Mesh Profile APIs. + */ + +/* + * Copyright (c) 2017 Intel Corporation + * Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef _BLE_MESH_MAIN_H_ +#define _BLE_MESH_MAIN_H_ + +#include "mesh_util.h" +#include "mesh_access.h" + +/** + * @brief Bluetooth Mesh Provisioning + * @defgroup bt_mesh_prov Bluetooth Mesh Provisioning + * @ingroup bt_mesh + * @{ + */ + +typedef enum { + BLE_MESH_NO_OUTPUT = 0, + BLE_MESH_BLINK = BIT(0), + BLE_MESH_BEEP = BIT(1), + BLE_MESH_VIBRATE = BIT(2), + BLE_MESH_DISPLAY_NUMBER = BIT(3), + BLE_MESH_DISPLAY_STRING = BIT(4), +} bt_mesh_output_action_t; + +typedef enum { + BLE_MESH_NO_INPUT = 0, + BLE_MESH_PUSH = BIT(0), + BLE_MESH_TWIST = BIT(1), + BLE_MESH_ENTER_NUMBER = BIT(2), + BLE_MESH_ENTER_STRING = BIT(3), +} bt_mesh_input_action_t; + +typedef enum { + BLE_MESH_PROV_ADV = BIT(0), + BLE_MESH_PROV_GATT = BIT(1), +} bt_mesh_prov_bearer_t; + +typedef enum { + BLE_MESH_PROV_OOB_OTHER = BIT(0), + BLE_MESH_PROV_OOB_URI = BIT(1), + BLE_MESH_PROV_OOB_2D_CODE = BIT(2), + BLE_MESH_PROV_OOB_BAR_CODE = BIT(3), + BLE_MESH_PROV_OOB_NFC = BIT(4), + BLE_MESH_PROV_OOB_NUMBER = BIT(5), + BLE_MESH_PROV_OOB_STRING = BIT(6), + /* 7 - 10 are reserved */ + BLE_MESH_PROV_OOB_ON_BOX = BIT(11), + BLE_MESH_PROV_OOB_IN_BOX = BIT(12), + BLE_MESH_PROV_OOB_ON_PAPER = BIT(13), + BLE_MESH_PROV_OOB_IN_MANUAL = BIT(14), + BLE_MESH_PROV_OOB_ON_DEV = BIT(15), +} bt_mesh_prov_oob_info_t; + +/** Provisioning properties & capabilities. */ +struct bt_mesh_prov { +#if CONFIG_BLE_MESH_NODE + /** The UUID that's used when advertising as unprovisioned */ + const u8_t *uuid; + + /** Optional URI. This will be advertised separately from the + * unprovisioned beacon, however the unprovisioned beacon will + * contain a hash of it so the two can be associated by the + * provisioner. + */ + const char *uri; + + /** Out of Band information field. */ + bt_mesh_prov_oob_info_t oob_info; + + /** Flag indicates whether unprovisioned devices support OOB public key */ + bool oob_pub_key; + + /** @brief Set device OOB public key. + * + * This callback notifies the application to + * set OOB public key & private key pair. + */ + void (*oob_pub_key_cb)(void); + + /** Static OOB value */ + const u8_t *static_val; + /** Static OOB value length */ + u8_t static_val_len; + + /** Maximum size of Output OOB supported */ + u8_t output_size; + /** Supported Output OOB Actions */ + u16_t output_actions; + + /* Maximum size of Input OOB supported */ + u8_t input_size; + /** Supported Input OOB Actions */ + u16_t input_actions; + + /** @brief Output of a number is requested. + * + * This callback notifies the application to + * output the given number using the given action. + * + * @param act Action for outputting the number. + * @param num Number to be out-put. + * + * @return Zero on success or negative error code otherwise + */ + int (*output_number)(bt_mesh_output_action_t act, u32_t num); + + /** @brief Output of a string is requested. + * + * This callback notifies the application to + * display the given string to the user. + * + * @param str String to be displayed. + * + * @return Zero on success or negative error code otherwise + */ + int (*output_string)(const char *str); + + /** @brief Input is requested. + * + * This callback notifies the application to request + * input from the user using the given action. The + * requested input will either be a string or a number, and + * the application needs to consequently call the + * bt_mesh_input_string() or bt_mesh_input_number() functions + * once the data has been acquired from the user. + * + * @param act Action for inputting data. + * @param num Maximum size of the in-put data. + * + * @return Zero on success or negative error code otherwise + */ + int (*input)(bt_mesh_input_action_t act, u8_t size); + + /** @brief Provisioning link has been opened. + * + * This callback notifies the application that a provisioning + * link has been opened on the given provisioning bearer. + * + * @param bearer Provisioning bearer. + */ + void (*link_open)(bt_mesh_prov_bearer_t bearer); + + /** @brief Provisioning link has been closed. + * + * This callback notifies the application that a provisioning + * link has been closed on the given provisioning bearer. + * + * @param bearer Provisioning bearer. + */ + void (*link_close)(bt_mesh_prov_bearer_t bearer); + + /** @brief Provisioning is complete. + * + * This callback notifies the application that provisioning has + * been successfully completed, and that the local node has been + * assigned the specified NetKeyIndex and primary element address. + * + * @param net_idx NetKeyIndex given during provisioning. + * @param addr Primary element address. + * @param flags Key Refresh & IV Update flags + * @param iv_index IV Index. + */ + void (*complete)(u16_t net_idx, u16_t addr, u8_t flags, u32_t iv_index); + + /** @brief Node has been reset. + * + * This callback notifies the application that the local node + * has been reset and needs to be reprovisioned. The node will + * not automatically advertise as unprovisioned, rather the + * bt_mesh_prov_enable() API needs to be called to enable + * unprovisioned advertising on one or more provisioning bearers. + */ + void (*reset)(void); +#endif /* CONFIG_BLE_MESH_NODE */ + +#if CONFIG_BLE_MESH_PROVISIONER + /* Provisioner device uuid */ + const u8_t *prov_uuid; + + /* + * Primary element address of the provisioner. + * No need to initialize it for fast provisioning. + */ + const u16_t prov_unicast_addr; + + /* + * Starting unicast address going to assigned. + * No need to initialize it for fast provisioning. + */ + u16_t prov_start_address; + + /* Attention timer contained in Provisioning Invite */ + u8_t prov_attention; + + /* Provisioner provisioning Algorithm */ + u8_t prov_algorithm; + + /* Provisioner public key oob */ + u8_t prov_pub_key_oob; + + /** @brief Input is requested. + * + * This callback notifies the application that it should + * read device's public key with OOB + * + * @param link_idx: The provisioning link index + * + * @return Zero on success or negative error code otherwise + */ + int (*prov_pub_key_oob_cb)(u8_t link_idx); + + /* Provisioner static oob value */ + u8_t *prov_static_oob_val; + + /* Provisioner static oob value length */ + u8_t prov_static_oob_len; + + /** @brief Provisioner input a number read from device output + * + * This callback notifies the application that it should + * input the number given by the device. + * + * @param method: The OOB authentication method + * @param act: The output action of the device + * @param size: The output size of the device + * @param link_idx: The provisioning link index + * + * @return Zero on success or negative error code otherwise + */ + int (*prov_input_num)(u8_t method, bt_mesh_output_action_t act, u8_t size, u8_t link_idx); + + /** @brief Provisioner output a number to the device + * + * This callback notifies the application that it should + * output the number to the device. + * + * @param method: The OOB authentication method + * @param act: The input action of the device + * @param data: The input number/string of the device + * @param size: The input size of the device + * @param link_idx: The provisioning link index + * + * @return Zero on success or negative error code otherwise + */ + int (*prov_output_num)(u8_t method, bt_mesh_input_action_t act, void *data, u8_t size, u8_t link_idx); + + /* + * Key refresh and IV update flag. + * No need to initialize it for fast provisioning. + */ + u8_t flags; + + /* + * IV index. No need to initialize it for fast provisioning. + */ + u32_t iv_index; + + /** @brief Provisioner has opened a provisioning link. + * + * This callback notifies the application that a provisioning + * link has been opened on the given provisioning bearer. + * + * @param bearer Provisioning bearer. + */ + void (*prov_link_open)(bt_mesh_prov_bearer_t bearer); + + /** @brief Provisioner has closed a provisioning link. + * + * This callback notifies the application that a provisioning + * link has been closed on the given provisioning bearer. + * + * @param bearer Provisioning bearer. + * @param reason Provisioning link close reason(disconnect reason) + * 0xFF: disconnect due to provisioner_pb_gatt_disable() + */ + void (*prov_link_close)(bt_mesh_prov_bearer_t bearer, u8_t reason); + + /** @brief Provision one device is complete. + * + * This callback notifies the application that provisioner has + * successfully provisioned a device, and the node has been assigned + * the specified NetKeyIndex and primary element address. + * + * @param node_idx Node index within the node(provisioned device) queue. + * @param device_uuid Provisioned device uuid pointer. + * @param unicast_addr Provisioned device assigned unicast address. + * @param element_num Provisioned device element number. + * @param netkey_idx Provisioned device assigned netkey index. + */ + void (*prov_complete)(int node_idx, const u8_t device_uuid[16], + u16_t unicast_addr, u8_t element_num, + u16_t netkey_idx); +#endif /* CONFIG_BLE_MESH_PROVISIONER */ +}; + +/* The following APIs are for BLE Mesh Node */ + +/** @brief Provide provisioning input OOB string. + * + * This is intended to be called after the bt_mesh_prov input callback + * has been called with BLE_MESH_ENTER_STRING as the action. + * + * @param str String. + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_mesh_input_string(const char *str); + +/** @brief Provide provisioning input OOB number. + * + * This is intended to be called after the bt_mesh_prov input callback + * has been called with BLE_MESH_ENTER_NUMBER as the action. + * + * @param num Number. + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_mesh_input_number(u32_t num); + +/** @brief Enable specific provisioning bearers + * + * Enable one or more provisioning bearers. + * + * @param Bit-wise OR of provisioning bearers. + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_mesh_prov_enable(bt_mesh_prov_bearer_t bearers); + +/** @brief Disable specific provisioning bearers + * + * Disable one or more provisioning bearers. + * + * @param Bit-wise OR of provisioning bearers. + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_mesh_prov_disable(bt_mesh_prov_bearer_t bearers); + +/** @brief Indicate whether provisioner is enabled + * + * @return true - enabled, false - disabled. + */ +bool bt_mesh_is_provisioner_en(void); + +/* The following API is for BLE Mesh Fast Provisioning */ + +/** @brief Change the device action + * + * @param[IN] action: role of device to be set + * 0x01 - enter, 0x02 - suspend, 0x03 - exit + * + * @return status + */ +u8_t bt_mesh_set_fast_prov_action(u8_t action); + +/* The following APIs are for BLE Mesh Provisioner */ + +/** @brief Provide provisioning input OOB string. + * + * This is intended to be called after the bt_mesh_prov input callback + * has been called with BLE_MESH_ENTER_STRING as the action. + * + * @param str String. + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_mesh_prov_input_string(const char *str); + +/** @brief Provide provisioning input OOB number. + * + * This is intended to be called after the bt_mesh_prov input callback + * has been called with BLE_MESH_ENTER_NUMBER as the action. + * + * @param num Number. + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_mesh_prov_input_number(u32_t num); + +/** @brief Enable specific provisioning bearers + * + * Enable one or more provisioning bearers. + * + * @param bearers Bit-wise OR of provisioning bearers. + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_mesh_provisioner_enable(bt_mesh_prov_bearer_t bearers); + +/** @brief Disable specific provisioning bearers + * + * Disable one or more provisioning bearers. + * + * @param bearers Bit-wise OR of provisioning bearers. + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_mesh_provisioner_disable(bt_mesh_prov_bearer_t bearers); + +/** + * @} + */ + +/** + * @brief Bluetooth Mesh + * @defgroup bt_mesh Bluetooth Mesh + * @ingroup bluetooth + * @{ + */ + +/* Primary Network Key index */ +#define BLE_MESH_NET_PRIMARY 0x000 + +#define BLE_MESH_RELAY_DISABLED 0x00 +#define BLE_MESH_RELAY_ENABLED 0x01 +#define BLE_MESH_RELAY_NOT_SUPPORTED 0x02 + +#define BLE_MESH_BEACON_DISABLED 0x00 +#define BLE_MESH_BEACON_ENABLED 0x01 + +#define BLE_MESH_GATT_PROXY_DISABLED 0x00 +#define BLE_MESH_GATT_PROXY_ENABLED 0x01 +#define BLE_MESH_GATT_PROXY_NOT_SUPPORTED 0x02 + +#define BLE_MESH_FRIEND_DISABLED 0x00 +#define BLE_MESH_FRIEND_ENABLED 0x01 +#define BLE_MESH_FRIEND_NOT_SUPPORTED 0x02 + +#define BLE_MESH_NODE_IDENTITY_STOPPED 0x00 +#define BLE_MESH_NODE_IDENTITY_RUNNING 0x01 +#define BLE_MESH_NODE_IDENTITY_NOT_SUPPORTED 0x02 + +/* Features */ +#define BLE_MESH_FEAT_RELAY BIT(0) +#define BLE_MESH_FEAT_PROXY BIT(1) +#define BLE_MESH_FEAT_FRIEND BIT(2) +#define BLE_MESH_FEAT_LOW_POWER BIT(3) +#define BLE_MESH_FEAT_SUPPORTED (BLE_MESH_FEAT_RELAY | \ + BLE_MESH_FEAT_PROXY | \ + BLE_MESH_FEAT_FRIEND | \ + BLE_MESH_FEAT_LOW_POWER) + +/** @brief Initialize Mesh support + * + * After calling this API, the node will not automatically advertise as + * unprovisioned, rather the bt_mesh_prov_enable() API needs to be called + * to enable unprovisioned advertising on one or more provisioning bearers. + * + * @param prov Node provisioning information. + * @param comp Node Composition. + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_mesh_init(const struct bt_mesh_prov *prov, + const struct bt_mesh_comp *comp); + +/** @brief Reset the state of the local Mesh node. + * + * Resets the state of the node, which means that it needs to be + * reprovisioned to become an active node in a Mesh network again. + * + * After calling this API, the node will not automatically advertise as + * unprovisioned, rather the bt_mesh_prov_enable() API needs to be called + * to enable unprovisioned advertising on one or more provisioning bearers. + * + */ +void bt_mesh_reset(void); + +/** @brief Suspend the Mesh network temporarily. + * + * This API can be used for power saving purposes, but the user should be + * aware that leaving the local node suspended for a long period of time + * may cause it to become permanently disconnected from the Mesh network. + * If at all possible, the Friendship feature should be used instead, to + * make the node into a Low Power Node. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_suspend(void); + +/** @brief Resume a suspended Mesh network. + * + * This API resumes the local node, after it has been suspended using the + * bt_mesh_suspend() API. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_resume(void); + +/** @brief Provision the local Mesh Node. + * + * This API should normally not be used directly by the application. The + * only exception is for testing purposes where manual provisioning is + * desired without an actual external provisioner. + * + * @param net_key Network Key + * @param net_idx Network Key Index + * @param flags Provisioning Flags + * @param iv_index IV Index + * @param addr Primary element address + * @param dev_key Device Key + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_mesh_provision(const u8_t net_key[16], u16_t net_idx, + u8_t flags, u32_t iv_index, u16_t addr, + const u8_t dev_key[16]); + +/** @brief Check if the local node has been provisioned. + * + * This API can be used to check if the local node has been provisioned + * or not. It can e.g. be helpful to determine if there was a stored + * network in flash, i.e. if the network was restored after calling + * settings_load(). + * + * @return True if the node is provisioned. False otherwise. + */ +bool bt_mesh_is_provisioned(void); + +/** @brief Toggle the IV Update test mode + * + * This API is only available if the IV Update test mode has been enabled + * in Kconfig. It is needed for passing most of the IV Update qualification + * test cases. + * + * @param enable true to enable IV Update test mode, false to disable it. + */ +void bt_mesh_iv_update_test(bool enable); + +/** @brief Toggle the IV Update state + * + * This API is only available if the IV Update test mode has been enabled + * in Kconfig. It is needed for passing most of the IV Update qualification + * test cases. + * + * @return true if IV Update In Progress state was entered, false otherwise. + */ +bool bt_mesh_iv_update(void); + +/** @brief Toggle the Low Power feature of the local device + * + * Enables or disables the Low Power feature of the local device. This is + * exposed as a run-time feature, since the device might want to change + * this e.g. based on being plugged into a stable power source or running + * from a battery power source. + * + * @param enable true to enable LPN functionality, false to disable it. + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_mesh_lpn_set(bool enable); + +/** @brief Send out a Friend Poll message. + * + * Send a Friend Poll message to the Friend of this node. If there is no + * established Friendship the function will return an error. + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_mesh_lpn_poll(void); + +/** @brief Register a callback for Friendship changes. + * + * Registers a callback that will be called whenever Friendship gets + * established or is lost. + * + * @param cb Function to call when the Friendship status changes. + */ +void bt_mesh_lpn_set_cb(void (*cb)(u16_t friend_addr, bool established)); + +/** + * @} + */ + +#endif /* _BLE_MESH_MAIN_H_ */ diff --git a/components/bt/ble_mesh/mesh_core/include/mesh_proxy.h b/components/bt/ble_mesh/mesh_core/include/mesh_proxy.h new file mode 100644 index 0000000000..0b14d9e184 --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/include/mesh_proxy.h @@ -0,0 +1,37 @@ +/** @file + * @brief Bluetooth Mesh Proxy APIs. + */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _BLE_MESH_PROXY_H_ +#define _BLE_MESH_PROXY_H_ + +#include +/** + * @brief Bluetooth Mesh Proxy + * @defgroup bt_mesh_proxy Bluetooth Mesh Proxy + * @ingroup bt_mesh + * @{ + */ + +/** + * @brief Enable advertising with Node Identity. + * + * This API requires that GATT Proxy support has been enabled. Once called + * each subnet will start advertising using Node Identity for the next + * 60 seconds. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_proxy_identity_enable(void); + +/** + * @} + */ + +#endif /* _BLE_MESH_PROXY_H_ */ diff --git a/components/bt/ble_mesh/mesh_core/include/mesh_slist.h b/components/bt/ble_mesh/mesh_core/include/mesh_slist.h new file mode 100644 index 0000000000..4ddf427e6a --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/include/mesh_slist.h @@ -0,0 +1,468 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * + * @brief Single-linked list implementation + * + * Single-linked list implementation using inline macros/functions. + * This API is not thread safe, and thus if a list is used across threads, + * calls to functions must be protected with synchronization primitives. + */ + +#ifndef _BLE_MESH_SLIST_H_ +#define _BLE_MESH_SLIST_H_ + +#include +#include +#include "mesh_util.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct _snode { + struct _snode *next; +}; + +typedef struct _snode sys_snode_t; + +struct _slist { + sys_snode_t *head; + sys_snode_t *tail; +}; + +typedef struct _slist sys_slist_t; + +/** + * @brief Provide the primitive to iterate on a list + * Note: the loop is unsafe and thus __sn should not be removed + * + * User _MUST_ add the loop statement curly braces enclosing its own code: + * + * SYS_SLIST_FOR_EACH_NODE(l, n) { + * + * } + * + * This and other SYS_SLIST_*() macros are not thread safe. + * + * @param __sl A pointer on a sys_slist_t to iterate on + * @param __sn A sys_snode_t pointer to peek each node of the list + */ +#define SYS_SLIST_FOR_EACH_NODE(__sl, __sn) \ + for (__sn = sys_slist_peek_head(__sl); __sn; \ + __sn = sys_slist_peek_next(__sn)) + +/** + * @brief Provide the primitive to iterate on a list, from a node in the list + * Note: the loop is unsafe and thus __sn should not be removed + * + * User _MUST_ add the loop statement curly braces enclosing its own code: + * + * SYS_SLIST_ITERATE_FROM_NODE(l, n) { + * + * } + * + * Like SYS_SLIST_FOR_EACH_NODE(), but __dn already contains a node in the list + * where to start searching for the next entry from. If NULL, it starts from + * the head. + * + * This and other SYS_SLIST_*() macros are not thread safe. + * + * @param __sl A pointer on a sys_slist_t to iterate on + * @param __sn A sys_snode_t pointer to peek each node of the list + * it contains the starting node, or NULL to start from the head + */ +#define SYS_SLIST_ITERATE_FROM_NODE(__sl, __sn) \ + for (__sn = __sn ? sys_slist_peek_next_no_check(__sn) \ + : sys_slist_peek_head(__sl); \ + __sn; \ + __sn = sys_slist_peek_next(__sn)) + +/** + * @brief Provide the primitive to safely iterate on a list + * Note: __sn can be removed, it will not break the loop. + * + * User _MUST_ add the loop statement curly braces enclosing its own code: + * + * SYS_SLIST_FOR_EACH_NODE_SAFE(l, n, s) { + * + * } + * + * This and other SYS_SLIST_*() macros are not thread safe. + * + * @param __sl A pointer on a sys_slist_t to iterate on + * @param __sn A sys_snode_t pointer to peek each node of the list + * @param __sns A sys_snode_t pointer for the loop to run safely + */ +#define SYS_SLIST_FOR_EACH_NODE_SAFE(__sl, __sn, __sns) \ + for (__sn = sys_slist_peek_head(__sl), \ + __sns = sys_slist_peek_next(__sn); \ + __sn; __sn = __sns, \ + __sns = sys_slist_peek_next(__sn)) + +/* + * @brief Provide the primitive to resolve the container of a list node + * Note: it is safe to use with NULL pointer nodes + * + * @param __ln A pointer on a sys_node_t to get its container + * @param __cn Container struct type pointer + * @param __n The field name of sys_node_t within the container struct + */ +#define SYS_SLIST_CONTAINER(__ln, __cn, __n) \ + ((__ln) ? CONTAINER_OF((__ln), __typeof__(*(__cn)), __n) : NULL) +/* + * @brief Provide the primitive to peek container of the list head + * + * @param __sl A pointer on a sys_slist_t to peek + * @param __cn Container struct type pointer + * @param __n The field name of sys_node_t within the container struct + */ +#define SYS_SLIST_PEEK_HEAD_CONTAINER(__sl, __cn, __n) \ + SYS_SLIST_CONTAINER(sys_slist_peek_head(__sl), __cn, __n) + +/* + * @brief Provide the primitive to peek container of the list tail + * + * @param __sl A pointer on a sys_slist_t to peek + * @param __cn Container struct type pointer + * @param __n The field name of sys_node_t within the container struct + */ +#define SYS_SLIST_PEEK_TAIL_CONTAINER(__sl, __cn, __n) \ + SYS_SLIST_CONTAINER(sys_slist_peek_tail(__sl), __cn, __n) + +/* + * @brief Provide the primitive to peek the next container + * + * @param __cn Container struct type pointer + * @param __n The field name of sys_node_t within the container struct + */ + +#define SYS_SLIST_PEEK_NEXT_CONTAINER(__cn, __n) \ + ((__cn) ? SYS_SLIST_CONTAINER(sys_slist_peek_next(&((__cn)->__n)), \ + __cn, __n) : NULL) + +/** + * @brief Provide the primitive to iterate on a list under a container + * Note: the loop is unsafe and thus __cn should not be detached + * + * User _MUST_ add the loop statement curly braces enclosing its own code: + * + * SYS_SLIST_FOR_EACH_CONTAINER(l, c, n) { + * + * } + * + * @param __sl A pointer on a sys_slist_t to iterate on + * @param __cn A pointer to peek each entry of the list + * @param __n The field name of sys_node_t within the container struct + */ +#define SYS_SLIST_FOR_EACH_CONTAINER(__sl, __cn, __n) \ + for (__cn = SYS_SLIST_PEEK_HEAD_CONTAINER(__sl, __cn, __n); __cn; \ + __cn = SYS_SLIST_PEEK_NEXT_CONTAINER(__cn, __n)) + +/** + * @brief Provide the primitive to safely iterate on a list under a container + * Note: __cn can be detached, it will not break the loop. + * + * User _MUST_ add the loop statement curly braces enclosing its own code: + * + * SYS_SLIST_FOR_EACH_NODE_SAFE(l, c, cn, n) { + * + * } + * + * @param __sl A pointer on a sys_slist_t to iterate on + * @param __cn A pointer to peek each entry of the list + * @param __cns A pointer for the loop to run safely + * @param __n The field name of sys_node_t within the container struct + */ +#define SYS_SLIST_FOR_EACH_CONTAINER_SAFE(__sl, __cn, __cns, __n) \ + for (__cn = SYS_SLIST_PEEK_HEAD_CONTAINER(__sl, __cn, __n), \ + __cns = SYS_SLIST_PEEK_NEXT_CONTAINER(__cn, __n); __cn; \ + __cn = __cns, __cns = SYS_SLIST_PEEK_NEXT_CONTAINER(__cn, __n)) + +/** + * @brief Initialize a list + * + * @param list A pointer on the list to initialize + */ +static inline void sys_slist_init(sys_slist_t *list) +{ + list->head = NULL; + list->tail = NULL; +} + +#define SYS_SLIST_STATIC_INIT(ptr_to_list) {NULL, NULL} + +/** + * @brief Test if the given list is empty + * + * @param list A pointer on the list to test + * + * @return a boolean, true if it's empty, false otherwise + */ +static inline bool sys_slist_is_empty(sys_slist_t *list) +{ + return (!list->head); +} + +/** + * @brief Peek the first node from the list + * + * @param list A point on the list to peek the first node from + * + * @return A pointer on the first node of the list (or NULL if none) + */ +static inline sys_snode_t *sys_slist_peek_head(sys_slist_t *list) +{ + return list->head; +} + +/** + * @brief Peek the last node from the list + * + * @param list A point on the list to peek the last node from + * + * @return A pointer on the last node of the list (or NULL if none) + */ +static inline sys_snode_t *sys_slist_peek_tail(sys_slist_t *list) +{ + return list->tail; +} + +/** + * @brief Peek the next node from current node, node is not NULL + * + * Faster then sys_slist_peek_next() if node is known not to be NULL. + * + * @param node A pointer on the node where to peek the next node + * + * @return a pointer on the next node (or NULL if none) + */ +static inline sys_snode_t *sys_slist_peek_next_no_check(sys_snode_t *node) +{ + return node->next; +} + +/** + * @brief Peek the next node from current node + * + * @param node A pointer on the node where to peek the next node + * + * @return a pointer on the next node (or NULL if none) + */ +static inline sys_snode_t *sys_slist_peek_next(sys_snode_t *node) +{ + return node ? sys_slist_peek_next_no_check(node) : NULL; +} + +/** + * @brief Prepend a node to the given list + * + * This and other sys_slist_*() functions are not thread safe. + * + * @param list A pointer on the list to affect + * @param node A pointer on the node to prepend + */ +static inline void sys_slist_prepend(sys_slist_t *list, + sys_snode_t *node) +{ + node->next = list->head; + list->head = node; + + if (!list->tail) { + list->tail = list->head; + } +} + +/** + * @brief Append a node to the given list + * + * This and other sys_slist_*() functions are not thread safe. + * + * @param list A pointer on the list to affect + * @param node A pointer on the node to append + */ +static inline void sys_slist_append(sys_slist_t *list, + sys_snode_t *node) +{ + node->next = NULL; + + if (!list->tail) { + list->tail = node; + list->head = node; + } else { + list->tail->next = node; + list->tail = node; + } +} + +/** + * @brief Append a list to the given list + * + * Append a singly-linked, NULL-terminated list consisting of nodes containing + * the pointer to the next node as the first element of a node, to @a list. + * This and other sys_slist_*() functions are not thread safe. + * + * @param list A pointer on the list to affect + * @param head A pointer to the first element of the list to append + * @param tail A pointer to the last element of the list to append + */ +static inline void sys_slist_append_list(sys_slist_t *list, + void *head, void *tail) +{ + if (!list->tail) { + list->head = (sys_snode_t *)head; + list->tail = (sys_snode_t *)tail; + } else { + list->tail->next = (sys_snode_t *)head; + list->tail = (sys_snode_t *)tail; + } +} + +/** + * @brief merge two slists, appending the second one to the first + * + * When the operation is completed, the appending list is empty. + * This and other sys_slist_*() functions are not thread safe. + * + * @param list A pointer on the list to affect + * @param list_to_append A pointer to the list to append. + */ +static inline void sys_slist_merge_slist(sys_slist_t *list, + sys_slist_t *list_to_append) +{ + sys_slist_append_list(list, list_to_append->head, + list_to_append->tail); + sys_slist_init(list_to_append); +} + +/** + * @brief Insert a node to the given list + * + * This and other sys_slist_*() functions are not thread safe. + * + * @param list A pointer on the list to affect + * @param prev A pointer on the previous node + * @param node A pointer on the node to insert + */ +static inline void sys_slist_insert(sys_slist_t *list, + sys_snode_t *prev, + sys_snode_t *node) +{ + if (!prev) { + sys_slist_prepend(list, node); + } else if (!prev->next) { + sys_slist_append(list, node); + } else { + node->next = prev->next; + prev->next = node; + } +} + +/** + * @brief Fetch and remove the first node of the given list + * + * List must be known to be non-empty. + * This and other sys_slist_*() functions are not thread safe. + * + * @param list A pointer on the list to affect + * + * @return A pointer to the first node of the list + */ +static inline sys_snode_t *sys_slist_get_not_empty(sys_slist_t *list) +{ + sys_snode_t *node = list->head; + + list->head = node->next; + if (list->tail == node) { + list->tail = list->head; + } + + return node; +} + +/** + * @brief Fetch and remove the first node of the given list + * + * This and other sys_slist_*() functions are not thread safe. + * + * @param list A pointer on the list to affect + * + * @return A pointer to the first node of the list (or NULL if empty) + */ +static inline sys_snode_t *sys_slist_get(sys_slist_t *list) +{ + return sys_slist_is_empty(list) ? NULL : sys_slist_get_not_empty(list); +} + +/** + * @brief Remove a node + * + * This and other sys_slist_*() functions are not thread safe. + * + * @param list A pointer on the list to affect + * @param prev_node A pointer on the previous node + * (can be NULL, which means the node is the list's head) + * @param node A pointer on the node to remove + */ +static inline void sys_slist_remove(sys_slist_t *list, + sys_snode_t *prev_node, + sys_snode_t *node) +{ + if (!prev_node) { + list->head = node->next; + + /* Was node also the tail? */ + if (list->tail == node) { + list->tail = list->head; + } + } else { + prev_node->next = node->next; + + /* Was node the tail? */ + if (list->tail == node) { + list->tail = prev_node; + } + } + + node->next = NULL; +} + +/** + * @brief Find and remove a node from a list + * + * This and other sys_slist_*() functions are not thread safe. + * + * @param list A pointer on the list to affect + * @param node A pointer on the node to remove from the list + * + * @return true if node was removed + */ +static inline bool sys_slist_find_and_remove(sys_slist_t *list, + sys_snode_t *node) +{ + sys_snode_t *prev = NULL; + sys_snode_t *test; + + SYS_SLIST_FOR_EACH_NODE(list, test) { + if (test == node) { + sys_slist_remove(list, prev, node); + return true; + } + + prev = test; + } + + return false; +} + +#ifdef __cplusplus +} +#endif + +#endif /* _BLE_MESH_SLIST_H_ */ + diff --git a/components/bt/ble_mesh/mesh_core/include/mesh_trace.h b/components/bt/ble_mesh/mesh_core/include/mesh_trace.h new file mode 100644 index 0000000000..325c807f93 --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/include/mesh_trace.h @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2017 Nordic Semiconductor ASA + * Copyright (c) 2015-2016 Intel Corporation + * Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _BLE_MESH_TRACE_H_ +#define _BLE_MESH_TRACE_H_ + +#include "esp_log.h" +#include "sdkconfig.h" + +/* Define common tracing for all */ +#ifndef LOG_LEVEL_ERROR +#define LOG_LEVEL_ERROR 1 +#endif /* LOG_LEVEL_ERROR */ + +#ifndef LOG_LEVEL_WARN +#define LOG_LEVEL_WARN 2 +#endif /* LOG_LEVEL_WARN */ + +#ifndef LOG_LEVEL_INFO +#define LOG_LEVEL_INFO 3 +#endif /* LOG_LEVEL_INFO */ + +#ifndef LOG_LEVEL_DEBUG +#define LOG_LEVEL_DEBUG 4 +#endif /* LOG_LEVEL_DEBUG */ + +#ifndef LOG_LEVEL_VERBOSE +#define LOG_LEVEL_VERBOSE 5 +#endif /*LOG_LEVEL_VERBOSE */ + +#ifdef CONFIG_BLE_MESH_STACK_TRACE_LEVEL +#define MESH_LOG_LEVEL CONFIG_BLE_MESH_STACK_TRACE_LEVEL +#else +#define MESH_LOG_LEVEL LOG_LEVEL_WARN +#endif + +#ifdef CONFIG_BLE_MESH_NET_BUF_TRACE_LEVEL +#define NET_BUF_LOG_LEVEL CONFIG_BLE_MESH_NET_BUF_TRACE_LEVEL +#else +#define NET_BUF_LOG_LEVEL LOG_LEVEL_WARN +#endif + +#define MESH_TRACE_TAG "BLE_MESH" + +#if (LOG_LOCAL_LEVEL >= 4) +#define BLE_MESH_LOG_LOCAL_LEVEL_MAPPING (LOG_LOCAL_LEVEL + 1) +#else +#define BLE_MESH_LOG_LOCAL_LEVEL_MAPPING LOG_LOCAL_LEVEL +#endif + +#ifndef MAX +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif /* MAX(a, b) */ + +#define BLE_MESH_LOG_LEVEL_CHECK(LAYER, LEVEL) (MAX(LAYER##_LOG_LEVEL, BLE_MESH_LOG_LOCAL_LEVEL_MAPPING) >= LOG_LEVEL_##LEVEL) + +#define BLE_MESH_PRINT_E(tag, format, ...) {esp_log_write(ESP_LOG_ERROR, tag, LOG_FORMAT(E, format), esp_log_timestamp(), tag, ##__VA_ARGS__); } +#define BLE_MESH_PRINT_W(tag, format, ...) {esp_log_write(ESP_LOG_WARN, tag, LOG_FORMAT(W, format), esp_log_timestamp(), tag, ##__VA_ARGS__); } +#define BLE_MESH_PRINT_I(tag, format, ...) {esp_log_write(ESP_LOG_INFO, tag, LOG_FORMAT(I, format), esp_log_timestamp(), tag, ##__VA_ARGS__); } +#define BLE_MESH_PRINT_D(tag, format, ...) {esp_log_write(ESP_LOG_DEBUG, tag, LOG_FORMAT(D, format), esp_log_timestamp(), tag, ##__VA_ARGS__); } +#define BLE_MESH_PRINT_V(tag, format, ...) {esp_log_write(ESP_LOG_VERBOSE, tag, LOG_FORMAT(V, format), esp_log_timestamp(), tag, ##__VA_ARGS__); } + +#define printk ets_printf + +#define _STRINGIFY(x) #x +#define STRINGIFY(s) _STRINGIFY(s) + +#ifndef __ASSERT +#define __ASSERT(test, fmt, ...) \ + do { \ + if (!(test)) { \ + printk("ASSERTION FAIL [%s] @ %s:%d:\n\t", \ + _STRINGIFY(test), \ + __FILE__, \ + __LINE__); \ + printk(fmt, ##__VA_ARGS__); \ + for (;;); \ + } \ + } while ((0)) +#endif + +#ifndef __ASSERT_NO_MSG +#define __ASSERT_NO_MSG(x) do { if (!(x)) BLE_MESH_PRINT_E(MESH_TRACE_TAG, "error %s %u", __FILE__, __LINE__); } while (0) +#endif + +#if !CONFIG_BLE_MESH_NO_LOG +#define BT_ERR(fmt, args...) do {if ((MESH_LOG_LEVEL >= LOG_LEVEL_ERROR) && BLE_MESH_LOG_LEVEL_CHECK(MESH, ERROR)) BLE_MESH_PRINT_E(MESH_TRACE_TAG, fmt, ## args);} while(0) +#define BT_WARN(fmt, args...) do {if ((MESH_LOG_LEVEL >= LOG_LEVEL_WARN) && BLE_MESH_LOG_LEVEL_CHECK(MESH, WARN)) BLE_MESH_PRINT_W(MESH_TRACE_TAG, fmt, ## args);} while(0) +#define BT_INFO(fmt, args...) do {if ((MESH_LOG_LEVEL >= LOG_LEVEL_INFO) && BLE_MESH_LOG_LEVEL_CHECK(MESH, INFO)) BLE_MESH_PRINT_I(MESH_TRACE_TAG, fmt, ## args);} while(0) +#define BT_DBG(fmt, args...) do {if ((MESH_LOG_LEVEL >= LOG_LEVEL_DEBUG) && BLE_MESH_LOG_LEVEL_CHECK(MESH, DEBUG)) BLE_MESH_PRINT_D(MESH_TRACE_TAG, fmt, ## args);} while(0) +#else +#define BT_ERR(fmt, args...) +#define BT_WARN(fmt, args...) +#define BT_INFO(fmt, args...) +#define BT_DBG(fmt, args...) +#endif + +#if defined(CONFIG_BLE_MESH_NET_BUF_LOG) && (!CONFIG_BLE_MESH_NO_LOG) +#define NET_BUF_ERR(fmt, args...) do {if ((NET_BUF_LOG_LEVEL >= LOG_LEVEL_ERROR) && BLE_MESH_LOG_LEVEL_CHECK(NET_BUF, ERROR)) BLE_MESH_PRINT_E(MESH_TRACE_TAG, fmt, ## args);} while(0) +#define NET_BUF_WARN(fmt, args...) do {if ((NET_BUF_LOG_LEVEL >= LOG_LEVEL_WARN) && BLE_MESH_LOG_LEVEL_CHECK(NET_BUF, WARN)) BLE_MESH_PRINT_W(MESH_TRACE_TAG, fmt, ## args);} while(0) +#define NET_BUF_INFO(fmt, args...) do {if ((NET_BUF_LOG_LEVEL >= LOG_LEVEL_INFO) && BLE_MESH_LOG_LEVEL_CHECK(NET_BUF, INFO)) BLE_MESH_PRINT_I(MESH_TRACE_TAG, fmt, ## args);} while(0) +#define NET_BUF_DBG(fmt, args...) do {if ((NET_BUF_LOG_LEVEL >= LOG_LEVEL_DEBUG) && BLE_MESH_LOG_LEVEL_CHECK(NET_BUF, DEBUG)) BLE_MESH_PRINT_D(MESH_TRACE_TAG, fmt, ## args);} while(0) +#define NET_BUF_ASSERT(cond) __ASSERT_NO_MSG(cond) +#else +#define NET_BUF_ERR(fmt, args...) +#define NET_BUF_WARN(fmt, args...) +#define NET_BUF_INFO(fmt, args...) +#define NET_BUF_DBG(fmt, args...) +#define NET_BUF_ASSERT(cond) +#endif + +#if defined(CONFIG_BLE_MESH_NET_BUF_SIMPLE_LOG) && (!CONFIG_BLE_MESH_NO_LOG) +#define NET_BUF_SIMPLE_ERR(fmt, args...) do {if ((NET_BUF_LOG_LEVEL >= LOG_LEVEL_ERROR) && BLE_MESH_LOG_LEVEL_CHECK(NET_BUF, ERROR)) BLE_MESH_PRINT_E(MESH_TRACE_TAG, fmt, ## args);} while(0) +#define NET_BUF_SIMPLE_WARN(fmt, args...) do {if ((NET_BUF_LOG_LEVEL >= LOG_LEVEL_WARN) && BLE_MESH_LOG_LEVEL_CHECK(NET_BUF, WARN)) BLE_MESH_PRINT_W(MESH_TRACE_TAG, fmt, ## args);} while(0) +#define NET_BUF_SIMPLE_INFO(fmt, args...) do {if ((NET_BUF_LOG_LEVEL >= LOG_LEVEL_INFO) && BLE_MESH_LOG_LEVEL_CHECK(NET_BUF, INFO)) BLE_MESH_PRINT_I(MESH_TRACE_TAG, fmt, ## args);} while(0) +#define NET_BUF_SIMPLE_DBG(fmt, args...) do {if ((NET_BUF_LOG_LEVEL >= LOG_LEVEL_DEBUG) && BLE_MESH_LOG_LEVEL_CHECK(NET_BUF, DEBUG)) BLE_MESH_PRINT_D(MESH_TRACE_TAG, fmt, ## args);} while(0) +#define NET_BUF_SIMPLE_ASSERT(cond) __ASSERT_NO_MSG(cond) +#else +#define NET_BUF_SIMPLE_ERR(fmt, args...) +#define NET_BUF_SIMPLE_WARN(fmt, args...) +#define NET_BUF_SIMPLE_INFO(fmt, args...) +#define NET_BUF_SIMPLE_DBG(fmt, args...) +#define NET_BUF_SIMPLE_ASSERT(cond) +#endif + +#endif /* _BLE_MESH_TRACE_H_ */ diff --git a/components/bt/ble_mesh/mesh_core/include/mesh_types.h b/components/bt/ble_mesh/mesh_core/include/mesh_types.h new file mode 100644 index 0000000000..66df1ec75e --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/include/mesh_types.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2017 Linaro Limited + * Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _BLE_MESH_TYPES_H_ +#define _BLE_MESH_TYPES_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef signed char s8_t; +typedef signed short s16_t; +typedef signed int s32_t; +typedef signed long long s64_t; + +typedef unsigned char u8_t; +typedef unsigned short u16_t; +typedef unsigned int u32_t; +typedef unsigned long long u64_t; + +typedef int bt_mesh_atomic_t; + +#ifndef bool +#define bool int8_t +#endif + +#ifndef false +#define false 0 +#endif + +#ifndef true +#define true 1 +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _BLE_MESH_TYPES_H_ */ diff --git a/components/bt/ble_mesh/mesh_core/include/mesh_util.h b/components/bt/ble_mesh/mesh_core/include/mesh_util.h new file mode 100644 index 0000000000..8258e2c691 --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/include/mesh_util.h @@ -0,0 +1,440 @@ +/* + * Copyright (c) 2011-2014, Wind River Systems, Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief Misc utilities + * + * Misc utilities usable by the kernel and application code. + */ + +#ifndef _BLE_MESH_UTIL_H_ +#define _BLE_MESH_UTIL_H_ + +#include +#include "mesh_types.h" +#include "mesh_trace.h" +#include "soc/soc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Helper to pass a int as a pointer or vice-versa. + * Those are available for 32 bits architectures: + */ +#define POINTER_TO_UINT(x) ((u32_t) (x)) +#define UINT_TO_POINTER(x) ((void *) (x)) +#define POINTER_TO_INT(x) ((s32_t) (x)) +#define INT_TO_POINTER(x) ((void *) (x)) + +/* Evaluates to 0 if cond is true-ish; compile error otherwise */ +#define ZERO_OR_COMPILE_ERROR(cond) ((int) sizeof(char[1 - 2 * !(cond)]) - 1) + +/* Evaluates to 0 if array is an array; compile error if not array (e.g. + * pointer) + */ +#define IS_ARRAY(array) \ + ZERO_OR_COMPILE_ERROR( \ + !__builtin_types_compatible_p(__typeof__(array), \ + __typeof__(&(array)[0]))) + +/* Evaluates to number of elements in an array; compile error if not + * an array (e.g. pointer) + */ +#define ARRAY_SIZE(array) \ + ((unsigned long) (IS_ARRAY(array) + \ + (sizeof(array) / sizeof((array)[0])))) + +/* Evaluates to 1 if ptr is part of array, 0 otherwise; compile error if + * "array" argument is not an array (e.g. "ptr" and "array" mixed up) + */ +#define PART_OF_ARRAY(array, ptr) \ + ((ptr) && ((ptr) >= &array[0] && (ptr) < &array[ARRAY_SIZE(array)])) + +#define CONTAINER_OF(ptr, type, field) \ + ((type *)(((char *)(ptr)) - offsetof(type, field))) + +/* round "x" up/down to next multiple of "align" (which must be a power of 2) */ +#define ROUND_UP(x, align) \ + (((unsigned long)(x) + ((unsigned long)align - 1)) & \ + ~((unsigned long)align - 1)) +#define ROUND_DOWN(x, align) ((unsigned long)(x) & ~((unsigned long)align - 1)) + +#define ceiling_fraction(numerator, divider) \ + (((numerator) + ((divider) - 1)) / (divider)) + +/* Internal helpers only used by the sys_* APIs further below */ +#ifndef __bswap_16 +#define __bswap_16(x) ((u16_t) ((((x) >> 8) & 0xff) | (((x) & 0xff) << 8))) +#endif + +#ifndef __bswap_32 +#define __bswap_32(x) ((u32_t) ((((x) >> 24) & 0xff) | \ + (((x) >> 8) & 0xff00) | \ + (((x) & 0xff00) << 8) | \ + (((x) & 0xff) << 24))) +#endif + +#ifndef __bswap_64 +#define __bswap_64(x) ((u64_t) ((((x) >> 56) & 0xff) | \ + (((x) >> 40) & 0xff00) | \ + (((x) >> 24) & 0xff0000) | \ + (((x) >> 8) & 0xff000000) | \ + (((x) & 0xff000000) << 8) | \ + (((x) & 0xff0000) << 24) | \ + (((x) & 0xff00) << 40) | \ + (((x) & 0xff) << 56))) +#endif + +#define sys_le16_to_cpu(val) (val) +#define sys_cpu_to_le16(val) (val) +#define sys_be16_to_cpu(val) __bswap_16(val) +#define sys_cpu_to_be16(val) __bswap_16(val) +#define sys_le32_to_cpu(val) (val) +#define sys_cpu_to_le32(val) (val) +#define sys_le64_to_cpu(val) (val) +#define sys_cpu_to_le64(val) (val) +#define sys_be32_to_cpu(val) __bswap_32(val) +#define sys_cpu_to_be32(val) __bswap_32(val) +#define sys_be64_to_cpu(val) __bswap_64(val) +#define sys_cpu_to_be64(val) __bswap_64(val) + +#ifndef MAX +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#endif + +#ifndef MIN +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +#ifndef BIT +#define BIT(n) (1UL << (n)) +#endif + +#ifndef BIT_MASK +#define BIT_MASK(n) (BIT(n) - 1) +#endif + +/** + * @brief Check for macro definition in compiler-visible expressions + * + * This trick was pioneered in Linux as the config_enabled() macro. + * The madness has the effect of taking a macro value that may be + * defined to "1" (e.g. CONFIG_MYFEATURE), or may not be defined at + * all and turning it into a literal expression that can be used at + * "runtime". That is, it works similarly to + * "defined(CONFIG_MYFEATURE)" does except that it is an expansion + * that can exist in a standard expression and be seen by the compiler + * and optimizer. Thus much ifdef usage can be replaced with cleaner + * expressions like: + * + * if (IS_ENABLED(CONFIG_MYFEATURE)) + * myfeature_enable(); + * + * INTERNAL + * First pass just to expand any existing macros, we need the macro + * value to be e.g. a literal "1" at expansion time in the next macro, + * not "(1)", etc... Standard recursive expansion does not work. + */ +#define IS_ENABLED(config_macro) _IS_ENABLED1(config_macro) + +/* Now stick on a "_XXXX" prefix, it will now be "_XXXX1" if config_macro + * is "1", or just "_XXXX" if it's undefined. + * ENABLED: _IS_ENABLED2(_XXXX1) + * DISABLED _IS_ENABLED2(_XXXX) + */ +#define _IS_ENABLED1(config_macro) _IS_ENABLED2(_XXXX##config_macro) + +/* Here's the core trick, we map "_XXXX1" to "_YYYY," (i.e. a string + * with a trailing comma), so it has the effect of making this a + * two-argument tuple to the preprocessor only in the case where the + * value is defined to "1" + * ENABLED: _YYYY, <--- note comma! + * DISABLED: _XXXX + */ +#define _XXXX1 _YYYY, + +/* Then we append an extra argument to fool the gcc preprocessor into + * accepting it as a varargs macro. + * arg1 arg2 arg3 + * ENABLED: _IS_ENABLED3(_YYYY, 1, 0) + * DISABLED _IS_ENABLED3(_XXXX 1, 0) + */ +#define _IS_ENABLED2(one_or_two_args) _IS_ENABLED3(one_or_two_args 1, 0) + +/* And our second argument is thus now cooked to be 1 in the case + * where the value is defined to 1, and 0 if not: + */ +#define _IS_ENABLED3(ignore_this, val, ...) val + +/* ESP Toolchain doesn't support section */ +#define ___in_section(a, b, c) +#define __in_section(a, b, c) ___in_section(a, b, c) + +#define __in_section_unique(seg) ___in_section(seg, __FILE__, __COUNTER__) + +#define popcount(x) __builtin_popcount(x) + +/** + * + * @brief find most significant bit set in a 32-bit word + * + * This routine finds the first bit set starting from the most significant bit + * in the argument passed in and returns the index of that bit. Bits are + * numbered starting at 1 from the least significant bit. A return value of + * zero indicates that the value passed is zero. + * + * @return most significant bit set, 0 if @a op is 0 + */ + +#if defined(__GNUC__) +static inline unsigned int find_msb_set(u32_t op) +{ + if (!op) { + return 0; + } + return 32 - __builtin_clz(op); +} +#endif + +/** + * + * @brief find least significant bit set in a 32-bit word + * + * This routine finds the first bit set starting from the least significant bit + * in the argument passed in and returns the index of that bit. Bits are + * numbered starting at 1 from the least significant bit. A return value of + * zero indicates that the value passed is zero. + * + * @return least significant bit set, 0 if @a op is 0 + */ + +#if defined(__GNUC__) +static inline unsigned int find_lsb_set(u32_t op) +{ + return __builtin_ffs(op); +} +#endif + +/** + * @brief Put a 16-bit integer as big-endian to arbitrary location. + * + * Put a 16-bit integer, originally in host endianness, to a + * potentially unaligned memory location in big-endian format. + * + * @param val 16-bit integer in host endianness. + * @param dst Destination memory address to store the result. + */ +static inline void sys_put_be16(u16_t val, u8_t dst[2]) +{ + dst[0] = val >> 8; + dst[1] = val; +} + +/** + * @brief Put a 32-bit integer as big-endian to arbitrary location. + * + * Put a 32-bit integer, originally in host endianness, to a + * potentially unaligned memory location in big-endian format. + * + * @param val 32-bit integer in host endianness. + * @param dst Destination memory address to store the result. + */ +static inline void sys_put_be32(u32_t val, u8_t dst[4]) +{ + sys_put_be16(val >> 16, dst); + sys_put_be16(val, &dst[2]); +} + +/** + * @brief Put a 16-bit integer as little-endian to arbitrary location. + * + * Put a 16-bit integer, originally in host endianness, to a + * potentially unaligned memory location in little-endian format. + * + * @param val 16-bit integer in host endianness. + * @param dst Destination memory address to store the result. + */ +static inline void sys_put_le16(u16_t val, u8_t dst[2]) +{ + dst[0] = val; + dst[1] = val >> 8; +} + +/** + * @brief Put a 32-bit integer as little-endian to arbitrary location. + * + * Put a 32-bit integer, originally in host endianness, to a + * potentially unaligned memory location in little-endian format. + * + * @param val 32-bit integer in host endianness. + * @param dst Destination memory address to store the result. + */ +static inline void sys_put_le32(u32_t val, u8_t dst[4]) +{ + sys_put_le16(val, dst); + sys_put_le16(val >> 16, &dst[2]); +} + +/** + * @brief Put a 64-bit integer as little-endian to arbitrary location. + * + * Put a 64-bit integer, originally in host endianness, to a + * potentially unaligned memory location in little-endian format. + * + * @param val 64-bit integer in host endianness. + * @param dst Destination memory address to store the result. + */ +static inline void sys_put_le64(u64_t val, u8_t dst[8]) +{ + sys_put_le32(val, dst); + sys_put_le32(val >> 32, &dst[4]); +} + +/** + * @brief Get a 16-bit integer stored in big-endian format. + * + * Get a 16-bit integer, stored in big-endian format in a potentially + * unaligned memory location, and convert it to the host endianness. + * + * @param src Location of the big-endian 16-bit integer to get. + * + * @return 16-bit integer in host endianness. + */ +static inline u16_t sys_get_be16(const u8_t src[2]) +{ + return ((u16_t)src[0] << 8) | src[1]; +} + +/** + * @brief Get a 32-bit integer stored in big-endian format. + * + * Get a 32-bit integer, stored in big-endian format in a potentially + * unaligned memory location, and convert it to the host endianness. + * + * @param src Location of the big-endian 32-bit integer to get. + * + * @return 32-bit integer in host endianness. + */ +static inline u32_t sys_get_be32(const u8_t src[4]) +{ + return ((u32_t)sys_get_be16(&src[0]) << 16) | sys_get_be16(&src[2]); +} + +/** + * @brief Get a 16-bit integer stored in little-endian format. + * + * Get a 16-bit integer, stored in little-endian format in a potentially + * unaligned memory location, and convert it to the host endianness. + * + * @param src Location of the little-endian 16-bit integer to get. + * + * @return 16-bit integer in host endianness. + */ +static inline u16_t sys_get_le16(const u8_t src[2]) +{ + return ((u16_t)src[1] << 8) | src[0]; +} + +/** + * @brief Get a 32-bit integer stored in little-endian format. + * + * Get a 32-bit integer, stored in little-endian format in a potentially + * unaligned memory location, and convert it to the host endianness. + * + * @param src Location of the little-endian 32-bit integer to get. + * + * @return 32-bit integer in host endianness. + */ +static inline u32_t sys_get_le32(const u8_t src[4]) +{ + return ((u32_t)sys_get_le16(&src[2]) << 16) | sys_get_le16(&src[0]); +} + +/** + * @brief Get a 64-bit integer stored in little-endian format. + * + * Get a 64-bit integer, stored in little-endian format in a potentially + * unaligned memory location, and convert it to the host endianness. + * + * @param src Location of the little-endian 64-bit integer to get. + * + * @return 64-bit integer in host endianness. + */ +static inline u64_t sys_get_le64(const u8_t src[8]) +{ + return ((u64_t)sys_get_le32(&src[4]) << 32) | sys_get_le32(&src[0]); +} + +const char *bt_hex(const void *buf, size_t len); + +void mem_rcopy(u8_t *dst, u8_t const *src, u16_t len); + +void _set(void *to, uint8_t val, unsigned int len); + +unsigned int _copy(uint8_t *to, unsigned int to_len, + const uint8_t *from, unsigned int from_len); + +void _set(void *to, uint8_t val, unsigned int len); + +uint8_t _double_byte(uint8_t a); + +int _compare(const uint8_t *a, const uint8_t *b, size_t size); + +/** + * @brief Swap one buffer content into another + * + * Copy the content of src buffer into dst buffer in reversed order, + * i.e.: src[n] will be put in dst[end-n] + * Where n is an index and 'end' the last index in both arrays. + * The 2 memory pointers must be pointing to different areas, and have + * a minimum size of given length. + * + * @param dst A valid pointer on a memory area where to copy the data in + * @param src A valid pointer on a memory area where to copy the data from + * @param length Size of both dst and src memory areas + */ +static inline void sys_memcpy_swap(void *dst, const void *src, size_t length) +{ + __ASSERT(((src < dst && (src + length) <= dst) || + (src > dst && (dst + length) <= src)), + "Source and destination buffers must not overlap"); + + src += length - 1; + + for (; length > 0; length--) { + *((u8_t *)dst++) = *((u8_t *)src--); + } +} + +/** + * @brief Swap buffer content + * + * In-place memory swap, where final content will be reversed. + * I.e.: buf[n] will be put in buf[end-n] + * Where n is an index and 'end' the last index of buf. + * + * @param buf A valid pointer on a memory area to swap + * @param length Size of buf memory area + */ +static inline void sys_mem_swap(void *buf, size_t length) +{ + size_t i; + + for (i = 0; i < (length / 2); i++) { + u8_t tmp = ((u8_t *)buf)[i]; + + ((u8_t *)buf)[i] = ((u8_t *)buf)[length - 1 - i]; + ((u8_t *)buf)[length - 1 - i] = tmp; + } +} + +#ifdef __cplusplus +} +#endif + +#endif /* _BLE_MESH_UTIL_H_ */ diff --git a/components/bt/ble_mesh/mesh_core/include/mesh_uuid.h b/components/bt/ble_mesh/mesh_core/include/mesh_uuid.h new file mode 100644 index 0000000000..de03df5c32 --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/include/mesh_uuid.h @@ -0,0 +1,530 @@ +/** @file + * @brief Bluetooth UUID handling + */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef _BLE_MESH_UUID_H_ +#define _BLE_MESH_UUID_H_ + +/** + * @brief UUIDs + * @defgroup bt_uuid UUIDs + * @ingroup bluetooth + * @{ + */ + +#include "mesh_util.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Bluetooth UUID types */ +enum { + BLE_MESH_UUID_TYPE_16, + BLE_MESH_UUID_TYPE_32, + BLE_MESH_UUID_TYPE_128, +}; + +/** @brief This is a 'tentative' type and should be used as a pointer only */ +struct bt_mesh_uuid { + u8_t type; +}; + +struct bt_mesh_uuid_16 { + struct bt_mesh_uuid uuid; + u16_t val; +}; + +struct bt_mesh_uuid_32 { + struct bt_mesh_uuid uuid; + u32_t val; +}; + +struct bt_mesh_uuid_128 { + struct bt_mesh_uuid uuid; + u8_t val[16]; +}; + +#define BLE_MESH_UUID_INIT_16(value) \ +{ \ + .uuid.type = BLE_MESH_UUID_TYPE_16, \ + .val = (value), \ +} + +#define BLE_MESH_UUID_INIT_32(value) \ +{ \ + .uuid.type = BLE_MESH_UUID_TYPE_32, \ + .val = (value), \ +} + +#define BLE_MESH_UUID_INIT_128(value...) \ +{ \ + .uuid.type = BLE_MESH_UUID_TYPE_128, \ + .val = { value }, \ +} + +#define BLE_MESH_UUID_DECLARE_16(value) \ + ((struct bt_mesh_uuid *) (&(struct bt_mesh_uuid_16) BLE_MESH_UUID_INIT_16(value))) + +#define BLE_MESH_UUID_DECLARE_32(value) \ + ((struct bt_mesh_uuid *) (&(struct bt_mesh_uuid_32) BLE_MESH_UUID_INIT_32(value))) + +#define BLE_MESH_UUID_DECLARE_128(value...) \ + ((struct bt_mesh_uuid *) (&(struct bt_mesh_uuid_128) BLE_MESH_UUID_INIT_128(value))) + +#define BLE_MESH_UUID_16(__u) CONTAINER_OF(__u, struct bt_mesh_uuid_16, uuid) +#define BLE_MESH_UUID_32(__u) CONTAINER_OF(__u, struct bt_mesh_uuid_32, uuid) +#define BLE_MESH_UUID_128(__u) CONTAINER_OF(__u, struct bt_mesh_uuid_128, uuid) + +/** @def BLE_MESH_UUID_GAP + * @brief Generic Access + */ +#define BLE_MESH_UUID_GAP BLE_MESH_UUID_DECLARE_16(0x1800) +#define BLE_MESH_UUID_GAP_VAL 0x1800 +/** @def BLE_MESH_UUID_GATT + * @brief Generic Attribute + */ +#define BLE_MESH_UUID_GATT BLE_MESH_UUID_DECLARE_16(0x1801) +#define BLE_MESH_UUID_GATT_VAL 0x1801 +/** @def BLE_MESH_UUID_CTS + * @brief Current Time Service + */ +#define BLE_MESH_UUID_CTS BLE_MESH_UUID_DECLARE_16(0x1805) +#define BLE_MESH_UUID_CTS_VAL 0x1805 +/** @def BLE_MESH_UUID_DIS + * @brief Device Information Service + */ +#define BLE_MESH_UUID_DIS BLE_MESH_UUID_DECLARE_16(0x180a) +#define BLE_MESH_UUID_DIS_VAL 0x180a +/** @def BLE_MESH_UUID_HRS + * @brief Heart Rate Service + */ +#define BLE_MESH_UUID_HRS BLE_MESH_UUID_DECLARE_16(0x180d) +#define BLE_MESH_UUID_HRS_VAL 0x180d +/** @def BLE_MESH_UUID_BAS + * @brief Battery Service + */ +#define BLE_MESH_UUID_BAS BLE_MESH_UUID_DECLARE_16(0x180f) +#define BLE_MESH_UUID_BAS_VAL 0x180f +/** @def BLE_MESH_UUID_HIDS + * @brief HID Service + */ +#define BLE_MESH_UUID_HIDS BLE_MESH_UUID_DECLARE_16(0x1812) +#define BLE_MESH_UUID_HIDS_VAL 0x1812 +/** @def BLE_MESH_UUID_CSC + * @brief Cycling Speed and Cadence Service + */ +#define BLE_MESH_UUID_CSC BLE_MESH_UUID_DECLARE_16(0x1816) +#define BLE_MESH_UUID_CSC_VAL 0x1816 +/** @def BLE_MESH_UUID_ESS + * @brief Environmental Sensing Service + */ +#define BLE_MESH_UUID_ESS BLE_MESH_UUID_DECLARE_16(0x181a) +#define BLE_MESH_UUID_ESS_VAL 0x181a +/** @def BLE_MESH_UUID_IPSS + * @brief IP Support Service + */ +#define BLE_MESH_UUID_IPSS BLE_MESH_UUID_DECLARE_16(0x1820) +#define BLE_MESH_UUID_IPSS_VAL 0x1820 +/** @def BLE_MESH_UUID_MESH_PROV + * @brief Mesh Provisioning Service + */ +#define BLE_MESH_UUID_MESH_PROV BLE_MESH_UUID_DECLARE_16(0x1827) +#define BLE_MESH_UUID_MESH_PROV_VAL 0x1827 +/** @def BLE_MESH_UUID_MESH_PROXY + * @brief Mesh Proxy Service + */ +#define BLE_MESH_UUID_MESH_PROXY BLE_MESH_UUID_DECLARE_16(0x1828) +#define BLE_MESH_UUID_MESH_PROXY_VAL 0x1828 +/** @def BLE_MESH_UUID_GATT_PRIMARY + * @brief GATT Primary Service + */ +#define BLE_MESH_UUID_GATT_PRIMARY BLE_MESH_UUID_DECLARE_16(0x2800) +#define BLE_MESH_UUID_GATT_PRIMARY_VAL 0x2800 +/** @def BLE_MESH_UUID_GATT_SECONDARY + * @brief GATT Secondary Service + */ +#define BLE_MESH_UUID_GATT_SECONDARY BLE_MESH_UUID_DECLARE_16(0x2801) +#define BLE_MESH_UUID_GATT_SECONDARY_VAL 0x2801 +/** @def BLE_MESH_UUID_GATT_INCLUDE + * @brief GATT Include Service + */ +#define BLE_MESH_UUID_GATT_INCLUDE BLE_MESH_UUID_DECLARE_16(0x2802) +#define BLE_MESH_UUID_GATT_INCLUDE_VAL 0x2802 +/** @def BLE_MESH_UUID_GATT_CHRC + * @brief GATT Characteristic + */ +#define BLE_MESH_UUID_GATT_CHRC BLE_MESH_UUID_DECLARE_16(0x2803) +#define BLE_MESH_UUID_GATT_CHRC_VAL 0x2803 +/** @def BLE_MESH_UUID_GATT_CEP + * @brief GATT Characteristic Extended Properties + */ +#define BLE_MESH_UUID_GATT_CEP BLE_MESH_UUID_DECLARE_16(0x2900) +#define BLE_MESH_UUID_GATT_CEP_VAL 0x2900 +/** @def BLE_MESH_UUID_GATT_CUD + * @brief GATT Characteristic User Description + */ +#define BLE_MESH_UUID_GATT_CUD BLE_MESH_UUID_DECLARE_16(0x2901) +#define BLE_MESH_UUID_GATT_CUD_VAL 0x2901 +/** @def BLE_MESH_UUID_GATT_CCC + * @brief GATT Client Characteristic Configuration + */ +#define BLE_MESH_UUID_GATT_CCC BLE_MESH_UUID_DECLARE_16(0x2902) +#define BLE_MESH_UUID_GATT_CCC_VAL 0x2902 +/** @def BLE_MESH_UUID_GATT_SCC + * @brief GATT Server Characteristic Configuration + */ +#define BLE_MESH_UUID_GATT_SCC BLE_MESH_UUID_DECLARE_16(0x2903) +#define BLE_MESH_UUID_GATT_SCC_VAL 0x2903 +/** @def BLE_MESH_UUID_GATT_CPF + * @brief GATT Characteristic Presentation Format + */ +#define BLE_MESH_UUID_GATT_CPF BLE_MESH_UUID_DECLARE_16(0x2904) +#define BLE_MESH_UUID_GATT_CPF_VAL 0x2904 +/** @def BLE_MESH_UUID_VALID_RANGE + * @brief Valid Range Descriptor + */ +#define BLE_MESH_UUID_VALID_RANGE BLE_MESH_UUID_DECLARE_16(0x2906) +#define BLE_MESH_UUID_VALID_RANGE_VAL 0x2906 +/** @def BLE_MESH_UUID_HIDS_EXT_REPORT + * @brief HID External Report Descriptor + */ +#define BLE_MESH_UUID_HIDS_EXT_REPORT BLE_MESH_UUID_DECLARE_16(0x2907) +#define BLE_MESH_UUID_HIDS_EXT_REPORT_VAL 0x2907 +/** @def BLE_MESH_UUID_HIDS_REPORT_REF + * @brief HID Report Reference Descriptor + */ +#define BLE_MESH_UUID_HIDS_REPORT_REF BLE_MESH_UUID_DECLARE_16(0x2908) +#define BLE_MESH_UUID_HIDS_REPORT_REF_VAL 0x2908 +/** @def BLE_MESH_UUID_ES_CONFIGURATION + * @brief Environmental Sensing Configuration Descriptor + */ +#define BLE_MESH_UUID_ES_CONFIGURATION BLE_MESH_UUID_DECLARE_16(0x290b) +#define BLE_MESH_UUID_ES_CONFIGURATION_VAL 0x290b +/** @def BLE_MESH_UUID_ES_MEASUREMENT + * @brief Environmental Sensing Measurement Descriptor + */ +#define BLE_MESH_UUID_ES_MEASUREMENT BLE_MESH_UUID_DECLARE_16(0x290c) +#define BLE_MESH_UUID_ES_MEASUREMENT_VAL 0x290c +/** @def BLE_MESH_UUID_ES_TRIGGER_SETTING + * @brief Environmental Sensing Trigger Setting Descriptor + */ +#define BLE_MESH_UUID_ES_TRIGGER_SETTING BLE_MESH_UUID_DECLARE_16(0x290d) +#define BLE_MESH_UUID_ES_TRIGGER_SETTING_VAL 0x290d +/** @def BLE_MESH_UUID_GAP_DEVICE_NAME + * @brief GAP Characteristic Device Name + */ +#define BLE_MESH_UUID_GAP_DEVICE_NAME BLE_MESH_UUID_DECLARE_16(0x2a00) +#define BLE_MESH_UUID_GAP_DEVICE_NAME_VAL 0x2a00 +/** @def BLE_MESH_UUID_GAP_APPEARANCE + * @brief GAP Characteristic Appearance + */ +#define BLE_MESH_UUID_GAP_APPEARANCE BLE_MESH_UUID_DECLARE_16(0x2a01) +#define BLE_MESH_UUID_GAP_APPEARANCE_VAL 0x2a01 +/** @def BLE_MESH_UUID_GAP_PPCP + * @brief GAP Characteristic Peripheral Preferred Connection Parameters + */ +#define BLE_MESH_UUID_GAP_PPCP BLE_MESH_UUID_DECLARE_16(0x2a04) +#define BLE_MESH_UUID_GAP_PPCP_VAL 0x2a04 +/** @def BLE_MESH_UUID_GATT_SC + * @brief GATT Characteristic Service Changed + */ +#define BLE_MESH_UUID_GATT_SC BLE_MESH_UUID_DECLARE_16(0x2a05) +#define BLE_MESH_UUID_GATT_SC_VAL 0x2a05 +/** @def BLE_MESH_UUID_BAS_BATTERY_LEVEL + * @brief BAS Characteristic Battery Level + */ +#define BLE_MESH_UUID_BAS_BATTERY_LEVEL BLE_MESH_UUID_DECLARE_16(0x2a19) +#define BLE_MESH_UUID_BAS_BATTERY_LEVEL_VAL 0x2a19 +/** @def BLE_MESH_UUID_DIS_SYSTEM_ID + * @brief DIS Characteristic System ID + */ +#define BLE_MESH_UUID_DIS_SYSTEM_ID BLE_MESH_UUID_DECLARE_16(0x2a23) +#define BLE_MESH_UUID_DIS_SYSTEM_ID_VAL 0x2a23 +/** @def BLE_MESH_UUID_DIS_MODEL_NUMBER + * @brief DIS Characteristic Model Number String + */ +#define BLE_MESH_UUID_DIS_MODEL_NUMBER BLE_MESH_UUID_DECLARE_16(0x2a24) +#define BLE_MESH_UUID_DIS_MODEL_NUMBER_VAL 0x2a24 +/** @def BLE_MESH_UUID_DIS_SERIAL_NUMBER + * @brief DIS Characteristic Serial Number String + */ +#define BLE_MESH_UUID_DIS_SERIAL_NUMBER BLE_MESH_UUID_DECLARE_16(0x2a25) +#define BLE_MESH_UUID_DIS_SERIAL_NUMBER_VAL 0x2a25 +/** @def BLE_MESH_UUID_DIS_FIRMWARE_REVISION + * @brief DIS Characteristic Firmware Revision String + */ +#define BLE_MESH_UUID_DIS_FIRMWARE_REVISION BLE_MESH_UUID_DECLARE_16(0x2a26) +#define BLE_MESH_UUID_DIS_FIRMWARE_REVISION_VAL 0x2a26 +/** @def BLE_MESH_UUID_DIS_HARDWARE_REVISION + * @brief DIS Characteristic Hardware Revision String + */ +#define BLE_MESH_UUID_DIS_HARDWARE_REVISION BLE_MESH_UUID_DECLARE_16(0x2a27) +#define BLE_MESH_UUID_DIS_HARDWARE_REVISION_VAL 0x2a27 +/** @def BLE_MESH_UUID_DIS_SOFTWARE_REVISION + * @brief DIS Characteristic Software Revision String + */ +#define BLE_MESH_UUID_DIS_SOFTWARE_REVISION BLE_MESH_UUID_DECLARE_16(0x2a28) +#define BLE_MESH_UUID_DIS_SOFTWARE_REVISION_VAL 0x2a28 +/** @def BLE_MESH_UUID_DIS_MANUFACTURER_NAME + * @brief DIS Characteristic Manufacturer Name String + */ +#define BLE_MESH_UUID_DIS_MANUFACTURER_NAME BLE_MESH_UUID_DECLARE_16(0x2a29) +#define BLE_MESH_UUID_DIS_MANUFACTURER_NAME_VAL 0x2a29 +/** @def BLE_MESH_UUID_DIS_PNP_ID + * @brief DIS Characteristic PnP ID + */ +#define BLE_MESH_UUID_DIS_PNP_ID BLE_MESH_UUID_DECLARE_16(0x2a50) +#define BLE_MESH_UUID_DIS_PNP_ID_VAL 0x2a50 +/** @def BLE_MESH_UUID_CTS_CURRENT_TIME + * @brief CTS Characteristic Current Time + */ +#define BLE_MESH_UUID_CTS_CURRENT_TIME BLE_MESH_UUID_DECLARE_16(0x2a2b) +#define BLE_MESH_UUID_CTS_CURRENT_TIME_VAL 0x2a2b +/** @def BLE_MESH_UUID_MAGN_DECLINATION + * @brief Magnetic Declination Characteristic + */ +#define BLE_MESH_UUID_MAGN_DECLINATION BLE_MESH_UUID_DECLARE_16(0x2a2c) +#define BLE_MESH_UUID_MAGN_DECLINATION_VAL 0x2a2c +/** @def BLE_MESH_UUID_HRS_MEASUREMENT + * @brief HRS Characteristic Measurement Interval + */ +#define BLE_MESH_UUID_HRS_MEASUREMENT BLE_MESH_UUID_DECLARE_16(0x2a37) +#define BLE_MESH_UUID_HRS_MEASUREMENT_VAL 0x2a37 +/** @def BLE_MESH_UUID_HRS_BODY_SENSOR + * @brief HRS Characteristic Body Sensor Location + */ +#define BLE_MESH_UUID_HRS_BODY_SENSOR BLE_MESH_UUID_DECLARE_16(0x2a38) +#define BLE_MESH_UUID_HRS_BODY_SENSOR_VAL 0x2a38 +/** @def BLE_MESH_UUID_HRS_CONTROL_POINT + * @brief HRS Characteristic Control Point + */ +#define BLE_MESH_UUID_HRS_CONTROL_POINT BLE_MESH_UUID_DECLARE_16(0x2a39) +#define BLE_MESH_UUID_HRS_CONTROL_POINT_VAL 0x2a39 +/** @def BLE_MESH_UUID_HIDS_INFO + * @brief HID Information Characteristic + */ +#define BLE_MESH_UUID_HIDS_INFO BLE_MESH_UUID_DECLARE_16(0x2a4a) +#define BLE_MESH_UUID_HIDS_INFO_VAL 0x2a4a +/** @def BLE_MESH_UUID_HIDS_REPORT_MAP + * @brief HID Report Map Characteristic + */ +#define BLE_MESH_UUID_HIDS_REPORT_MAP BLE_MESH_UUID_DECLARE_16(0x2a4b) +#define BLE_MESH_UUID_HIDS_REPORT_MAP_VAL 0x2a4b +/** @def BLE_MESH_UUID_HIDS_CTRL_POINT + * @brief HID Control Point Characteristic + */ +#define BLE_MESH_UUID_HIDS_CTRL_POINT BLE_MESH_UUID_DECLARE_16(0x2a4c) +#define BLE_MESH_UUID_HIDS_CTRL_POINT_VAL 0x2a4c +/** @def BLE_MESH_UUID_HIDS_REPORT + * @brief HID Report Characteristic + */ +#define BLE_MESH_UUID_HIDS_REPORT BLE_MESH_UUID_DECLARE_16(0x2a4d) +#define BLE_MESH_UUID_HIDS_REPORT_VAL 0x2a4d +/** @def BLE_MESH_UUID_CSC_MEASUREMENT + * @brief CSC Measurement Characteristic + */ +#define BLE_MESH_UUID_CSC_MEASUREMENT BLE_MESH_UUID_DECLARE_16(0x2a5b) +#define BLE_MESH_UUID_CSC_MEASUREMENT_VAL 0x2a5b +/** @def BLE_MESH_UUID_CSC_FEATURE + * @brief CSC Feature Characteristic + */ +#define BLE_MESH_UUID_CSC_FEATURE BLE_MESH_UUID_DECLARE_16(0x2a5c) +#define BLE_MESH_UUID_CSC_FEATURE_VAL 0x2a5c +/** @def BLE_MESH_UUID_SENSOR_LOCATION + * @brief Sensor Location Characteristic + */ +#define BLE_MESH_UUID_SENSOR_LOCATION BLE_MESH_UUID_DECLARE_16(0x2a5d) +#define BLE_MESH_UUID_SENSOR_LOCATION_VAL 0x2a5d +/** @def BLE_MESH_UUID_SC_CONTROL_POINT + * @brief SC Control Point Characteristic + */ +#define BLE_MESH_UUID_SC_CONTROL_POINT BLE_MESH_UUID_DECLARE_16(0x2a55) +#define BLE_MESH_UUID_SC_CONTROL_POINT_VAl 0x2a55 +/** @def BLE_MESH_UUID_ELEVATION + * @brief Elevation Characteristic + */ +#define BLE_MESH_UUID_ELEVATION BLE_MESH_UUID_DECLARE_16(0x2a6c) +#define BLE_MESH_UUID_ELEVATION_VAL 0x2a6c +/** @def BLE_MESH_UUID_PRESSURE + * @brief Pressure Characteristic + */ +#define BLE_MESH_UUID_PRESSURE BLE_MESH_UUID_DECLARE_16(0x2a6d) +#define BLE_MESH_UUID_PRESSURE_VAL 0x2a6d +/** @def BLE_MESH_UUID_TEMPERATURE + * @brief Temperature Characteristic + */ +#define BLE_MESH_UUID_TEMPERATURE BLE_MESH_UUID_DECLARE_16(0x2a6e) +#define BLE_MESH_UUID_TEMPERATURE_VAL 0x2a6e +/** @def BLE_MESH_UUID_HUMIDITY + * @brief Humidity Characteristic + */ +#define BLE_MESH_UUID_HUMIDITY BLE_MESH_UUID_DECLARE_16(0x2a6f) +#define BLE_MESH_UUID_HUMIDITY_VAL 0x2a6f +/** @def BLE_MESH_UUID_TRUE_WIND_SPEED + * @brief True Wind Speed Characteristic + */ +#define BLE_MESH_UUID_TRUE_WIND_SPEED BLE_MESH_UUID_DECLARE_16(0x2a70) +#define BLE_MESH_UUID_TRUE_WIND_SPEED_VAL 0x2a70 +/** @def BLE_MESH_UUID_TRUE_WIND_DIR + * @brief True Wind Direction Characteristic + */ +#define BLE_MESH_UUID_TRUE_WIND_DIR BLE_MESH_UUID_DECLARE_16(0x2a71) +#define BLE_MESH_UUID_TRUE_WIND_DIR_VAL 0x2a71 +/** @def BLE_MESH_UUID_APPARENT_WIND_SPEED + * @brief Apparent Wind Speed Characteristic + */ +#define BLE_MESH_UUID_APPARENT_WIND_SPEED BLE_MESH_UUID_DECLARE_16(0x2a72) +#define BLE_MESH_UUID_APPARENT_WIND_SPEED_VAL 0x2a72 +/** @def BLE_MESH_UUID_APPARENT_WIND_DIR + * @brief Apparent Wind Direction Characteristic + */ +#define BLE_MESH_UUID_APPARENT_WIND_DIR BLE_MESH_UUID_DECLARE_16(0x2a73) +#define BLE_MESH_UUID_APPARENT_WIND_DIR_VAL 0x2a73 +/** @def BLE_MESH_UUID_GUST_FACTOR + * @brief Gust Factor Characteristic + */ +#define BLE_MESH_UUID_GUST_FACTOR BLE_MESH_UUID_DECLARE_16(0x2a74) +#define BLE_MESH_UUID_GUST_FACTOR_VAL 0x2a74 +/** @def BLE_MESH_UUID_POLLEN_CONCENTRATION + * @brief Pollen Concentration Characteristic + */ +#define BLE_MESH_UUID_POLLEN_CONCENTRATION BLE_MESH_UUID_DECLARE_16(0x2a75) +#define BLE_MESH_UUID_POLLEN_CONCENTRATION_VAL 0x2a75 +/** @def BLE_MESH_UUID_UV_INDEX + * @brief UV Index Characteristic + */ +#define BLE_MESH_UUID_UV_INDEX BLE_MESH_UUID_DECLARE_16(0x2a76) +#define BLE_MESH_UUID_UV_INDEX_VAL 0x2a76 +/** @def BLE_MESH_UUID_IRRADIANCE + * @brief Irradiance Characteristic + */ +#define BLE_MESH_UUID_IRRADIANCE BLE_MESH_UUID_DECLARE_16(0x2a77) +#define BLE_MESH_UUID_IRRADIANCE_VAL 0x2a77 +/** @def BLE_MESH_UUID_RAINFALL + * @brief Rainfall Characteristic + */ +#define BLE_MESH_UUID_RAINFALL BLE_MESH_UUID_DECLARE_16(0x2a78) +#define BLE_MESH_UUID_RAINFALL_VAL 0x2a78 +/** @def BLE_MESH_UUID_WIND_CHILL + * @brief Wind Chill Characteristic + */ +#define BLE_MESH_UUID_WIND_CHILL BLE_MESH_UUID_DECLARE_16(0x2a79) +#define BLE_MESH_UUID_WIND_CHILL_VAL 0x2a79 +/** @def BLE_MESH_UUID_HEAT_INDEX + * @brief Heat Index Characteristic + */ +#define BLE_MESH_UUID_HEAT_INDEX BLE_MESH_UUID_DECLARE_16(0x2a7a) +#define BLE_MESH_UUID_HEAT_INDEX_VAL 0x2a7a +/** @def BLE_MESH_UUID_DEW_POINT + * @brief Dew Point Characteristic + */ +#define BLE_MESH_UUID_DEW_POINT BLE_MESH_UUID_DECLARE_16(0x2a7b) +#define BLE_MESH_UUID_DEW_POINT_VAL 0x2a7b +/** @def BLE_MESH_UUID_DESC_VALUE_CHANGED + * @brief Descriptor Value Changed Characteristic + */ +#define BLE_MESH_UUID_DESC_VALUE_CHANGED BLE_MESH_UUID_DECLARE_16(0x2a7d) +#define BLE_MESH_UUID_DESC_VALUE_CHANGED_VAL 0x2a7d +/** @def BLE_MESH_UUID_MAGN_FLUX_DENSITY_2D + * @brief Magnetic Flux Density - 2D Characteristic + */ +#define BLE_MESH_UUID_MAGN_FLUX_DENSITY_2D BLE_MESH_UUID_DECLARE_16(0x2aa0) +#define BLE_MESH_UUID_MAGN_FLUX_DENSITY_2D_VAL 0x2aa0 +/** @def BLE_MESH_UUID_MAGN_FLUX_DENSITY_3D + * @brief Magnetic Flux Density - 3D Characteristic + */ +#define BLE_MESH_UUID_MAGN_FLUX_DENSITY_3D BLE_MESH_UUID_DECLARE_16(0x2aa1) +#define BLE_MESH_UUID_MAGN_FLUX_DENSITY_3D_VAL 0x2aa1 +/** @def BLE_MESH_UUID_BAR_PRESSURE_TREND + * @brief Barometric Pressure Trend Characteristic + */ +#define BLE_MESH_UUID_BAR_PRESSURE_TREND BLE_MESH_UUID_DECLARE_16(0x2aa3) +#define BLE_MESH_UUID_BAR_PRESSURE_TREND_VAL 0x2aa3 +/** @def BLE_MESH_UUID_MESH_PROV_DATA_IN + * @brief Mesh Provisioning Data In + */ +#define BLE_MESH_UUID_MESH_PROV_DATA_IN BLE_MESH_UUID_DECLARE_16(0x2adb) +#define BLE_MESH_UUID_MESH_PROV_DATA_IN_VAL 0x2adb +/** @def BLE_MESH_UUID_MESH_PROV_DATA_OUT + * @brief Mesh Provisioning Data Out + */ +#define BLE_MESH_UUID_MESH_PROV_DATA_OUT BLE_MESH_UUID_DECLARE_16(0x2adc) +#define BLE_MESH_UUID_MESH_PROV_DATA_OUT_VAL 0x2adc +/** @def BLE_MESH_UUID_MESH_PROXY_DATA_IN + * @brief Mesh Proxy Data In + */ +#define BLE_MESH_UUID_MESH_PROXY_DATA_IN BLE_MESH_UUID_DECLARE_16(0x2add) +#define BLE_MESH_UUID_MESH_PROXY_DATA_IN_VAL 0x2add +/** @def BLE_MESH_UUID_MESH_PROXY_DATA_OUT + * @brief Mesh Proxy Data Out + */ +#define BLE_MESH_UUID_MESH_PROXY_DATA_OUT BLE_MESH_UUID_DECLARE_16(0x2ade) +#define BLE_MESH_UUID_MESH_PROXY_DATA_OUT_VAL 0x2ade + +/* + * Protocol UUIDs + */ +#define BLE_MESH_UUID_SDP BLE_MESH_UUID_DECLARE_16(0x0001) +#define BLE_MESH_UUID_SDP_VAL 0x0001 +#define BLE_MESH_UUID_UDP BLE_MESH_UUID_DECLARE_16(0x0002) +#define BLE_MESH_UUID_UDP_VAL 0x0002 +#define BLE_MESH_UUID_RFCOMM BLE_MESH_UUID_DECLARE_16(0x0003) +#define BLE_MESH_UUID_RFCOMM_VAL 0x0003 +#define BLE_MESH_UUID_TCP BLE_MESH_UUID_DECLARE_16(0x0004) +#define BLE_MESH_UUID_TCP_VAL 0x0004 +#define BLE_MESH_UUID_TCS_BIN BLE_MESH_UUID_DECLARE_16(0x0005) +#define BLE_MESH_UUID_TCS_BIN_VAL 0x0005 +#define BLE_MESH_UUID_TCS_AT BLE_MESH_UUID_DECLARE_16(0x0006) +#define BLE_MESH_UUID_TCS_AT_VAL 0x0006 +#define BLE_MESH_UUID_ATT BLE_MESH_UUID_DECLARE_16(0x0007) +#define BLE_MESH_UUID_ATT_VAL 0x0007 +#define BLE_MESH_UUID_OBEX BLE_MESH_UUID_DECLARE_16(0x0008) +#define BLE_MESH_UUID_OBEX_VAL 0x0008 +#define BLE_MESH_UUID_IP BLE_MESH_UUID_DECLARE_16(0x0009) +#define BLE_MESH_UUID_IP_VAL 0x0009 +#define BLE_MESH_UUID_FTP BLE_MESH_UUID_DECLARE_16(0x000a) +#define BLE_MESH_UUID_FTP_VAL 0x000a +#define BLE_MESH_UUID_HTTP BLE_MESH_UUID_DECLARE_16(0x000c) +#define BLE_MESH_UUID_HTTP_VAL 0x000c +#define BLE_MESH_UUID_BNEP BLE_MESH_UUID_DECLARE_16(0x000f) +#define BLE_MESH_UUID_BNEP_VAL 0x000f +#define BLE_MESH_UUID_UPNP BLE_MESH_UUID_DECLARE_16(0x0010) +#define BLE_MESH_UUID_UPNP_VAL 0x0010 +#define BLE_MESH_UUID_HIDP BLE_MESH_UUID_DECLARE_16(0x0011) +#define BLE_MESH_UUID_HIDP_VAL 0x0011 +#define BLE_MESH_UUID_HCRP_CTRL BLE_MESH_UUID_DECLARE_16(0x0012) +#define BLE_MESH_UUID_HCRP_CTRL_VAL 0x0012 +#define BLE_MESH_UUID_HCRP_DATA BLE_MESH_UUID_DECLARE_16(0x0014) +#define BLE_MESH_UUID_HCRP_DATA_VAL 0x0014 +#define BLE_MESH_UUID_HCRP_NOTE BLE_MESH_UUID_DECLARE_16(0x0016) +#define BLE_MESH_UUID_HCRP_NOTE_VAL 0x0016 +#define BLE_MESH_UUID_AVCTP BLE_MESH_UUID_DECLARE_16(0x0017) +#define BLE_MESH_UUID_AVCTP_VAL 0x0017 +#define BLE_MESH_UUID_AVDTP BLE_MESH_UUID_DECLARE_16(0x0019) +#define BLE_MESH_UUID_AVDTP_VAL 0x0019 +#define BLE_MESH_UUID_CMTP BLE_MESH_UUID_DECLARE_16(0x001b) +#define BLE_MESH_UUID_CMTP_VAL 0x001b +#define BLE_MESH_UUID_UDI BLE_MESH_UUID_DECLARE_16(0x001d) +#define BLE_MESH_UUID_UDI_VAL 0x001d +#define BLE_MESH_UUID_MCAP_CTRL BLE_MESH_UUID_DECLARE_16(0x001e) +#define BLE_MESH_UUID_MCAP_CTRL_VAL 0x001e +#define BLE_MESH_UUID_MCAP_DATA BLE_MESH_UUID_DECLARE_16(0x001f) +#define BLE_MESH_UUID_MCAP_DATA_VAL 0x001f +#define BLE_MESH_UUID_L2CAP BLE_MESH_UUID_DECLARE_16(0x0100) +#define BLE_MESH_UUID_L2CAP_VAL 0x0100 + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* _BLE_MESH_UUID_H_ */ diff --git a/components/bt/ble_mesh/mesh_core/lpn.c b/components/bt/ble_mesh/mesh_core/lpn.c new file mode 100644 index 0000000000..8adf2a24bb --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/lpn.c @@ -0,0 +1,1057 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include "sdkconfig.h" +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BLE_MESH_DEBUG_LOW_POWER) + +#include "mesh_buf.h" +#include "mesh_kernel.h" +#include "mesh_trace.h" +#include "mesh_main.h" + +#include "crypto.h" +#include "adv.h" +#include "mesh.h" +#include "net.h" +#include "transport.h" +#include "access.h" +#include "beacon.h" +#include "foundation.h" +#include "lpn.h" + +#ifdef CONFIG_BLE_MESH_LOW_POWER + +#if defined(CONFIG_BLE_MESH_LPN_AUTO) +#define LPN_AUTO_TIMEOUT K_SECONDS(CONFIG_BLE_MESH_LPN_AUTO_TIMEOUT) +#else +#define LPN_AUTO_TIMEOUT 0 +#endif + +#define LPN_RECV_DELAY CONFIG_BLE_MESH_LPN_RECV_DELAY +#define SCAN_LATENCY MIN(CONFIG_BLE_MESH_LPN_SCAN_LATENCY, \ + LPN_RECV_DELAY) + +#define FRIEND_REQ_RETRY_TIMEOUT K_SECONDS(CONFIG_BLE_MESH_LPN_RETRY_TIMEOUT) + +#define FRIEND_REQ_WAIT K_MSEC(100) +#define FRIEND_REQ_SCAN K_SECONDS(1) +#define FRIEND_REQ_TIMEOUT (FRIEND_REQ_WAIT + FRIEND_REQ_SCAN) + +#define POLL_RETRY_TIMEOUT K_MSEC(100) + +#define REQ_RETRY_DURATION(lpn) (4 * (LPN_RECV_DELAY + (lpn)->adv_duration + \ + (lpn)->recv_win + POLL_RETRY_TIMEOUT)) + +#define POLL_TIMEOUT_INIT (CONFIG_BLE_MESH_LPN_INIT_POLL_TIMEOUT * 100) +#define POLL_TIMEOUT_MAX(lpn) ((CONFIG_BLE_MESH_LPN_POLL_TIMEOUT * 100) - \ + REQ_RETRY_DURATION(lpn)) + +/* Update 4 to 20 for BQB test case MESH/NODE/FRND/LPM/BI-02-C */ +#define REQ_ATTEMPTS(lpn) (POLL_TIMEOUT_MAX(lpn) < K_SECONDS(3) ? 2 : 4) + +#define CLEAR_ATTEMPTS 2 + +#define LPN_CRITERIA ((CONFIG_BLE_MESH_LPN_MIN_QUEUE_SIZE) | \ + (CONFIG_BLE_MESH_LPN_RSSI_FACTOR << 3) | \ + (CONFIG_BLE_MESH_LPN_RECV_WIN_FACTOR << 5)) + +#define POLL_TO(to) { (u8_t)((to) >> 16), (u8_t)((to) >> 8), (u8_t)(to) } +#define LPN_POLL_TO POLL_TO(CONFIG_BLE_MESH_LPN_POLL_TIMEOUT) + +/* 2 transmissions, 20ms interval */ +#define POLL_XMIT BLE_MESH_TRANSMIT(1, 20) + +static void (*lpn_cb)(u16_t friend_addr, bool established); + +#if defined(CONFIG_BLE_MESH_DEBUG_LOW_POWER) +static const char *state2str(int state) +{ + switch (state) { + case BLE_MESH_LPN_DISABLED: + return "disabled"; + case BLE_MESH_LPN_CLEAR: + return "clear"; + case BLE_MESH_LPN_TIMER: + return "timer"; + case BLE_MESH_LPN_ENABLED: + return "enabled"; + case BLE_MESH_LPN_REQ_WAIT: + return "req wait"; + case BLE_MESH_LPN_WAIT_OFFER: + return "wait offer"; + case BLE_MESH_LPN_ESTABLISHED: + return "established"; + case BLE_MESH_LPN_RECV_DELAY: + return "recv delay"; + case BLE_MESH_LPN_WAIT_UPDATE: + return "wait update"; + default: + return "(unknown)"; + } +} +#endif /* CONFIG_BLE_MESH_DEBUG_LOW_POWER */ + +static inline void lpn_set_state(int state) +{ +#if defined(CONFIG_BLE_MESH_DEBUG_LOW_POWER) + BT_DBG("%s -> %s", state2str(bt_mesh.lpn.state), state2str(state)); +#endif + bt_mesh.lpn.state = state; +} + +static inline void group_zero(bt_mesh_atomic_t *target) +{ +#if CONFIG_BLE_MESH_LPN_GROUPS > 32 + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.lpn.added); i++) { + bt_mesh_atomic_set(&target[i], 0); + } +#else + bt_mesh_atomic_set(target, 0); +#endif +} + +static inline void group_set(bt_mesh_atomic_t *target, bt_mesh_atomic_t *source) +{ +#if CONFIG_BLE_MESH_LPN_GROUPS > 32 + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.lpn.added); i++) { + (void)bt_mesh_atomic_or(&target[i], bt_mesh_atomic_get(&source[i])); + } +#else + (void)bt_mesh_atomic_or(target, bt_mesh_atomic_get(source)); +#endif +} + +static inline void group_clear(bt_mesh_atomic_t *target, bt_mesh_atomic_t *source) +{ +#if CONFIG_BLE_MESH_LPN_GROUPS > 32 + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.lpn.added); i++) { + (void)bt_mesh_atomic_and(&target[i], ~bt_mesh_atomic_get(&source[i])); + } +#else + (void)bt_mesh_atomic_and(target, ~bt_mesh_atomic_get(source)); +#endif +} + +static void clear_friendship(bool force, bool disable); + +static void friend_clear_sent(int err, void *user_data) +{ + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + + /* We're switching away from Low Power behavior, so permanently + * enable scanning. + */ + bt_mesh_scan_enable(); + + lpn->req_attempts++; + + if (err) { + BT_ERR("%s, Sending Friend Request failed (err %d)", __func__, err); + lpn_set_state(BLE_MESH_LPN_ENABLED); + clear_friendship(false, lpn->disable); + return; + } + + lpn_set_state(BLE_MESH_LPN_CLEAR); + k_delayed_work_submit(&lpn->timer, FRIEND_REQ_TIMEOUT); +} + +static const struct bt_mesh_send_cb clear_sent_cb = { + .end = friend_clear_sent, +}; + +static int send_friend_clear(void) +{ + struct bt_mesh_msg_ctx ctx = { + .net_idx = bt_mesh.sub[0].net_idx, + .app_idx = BLE_MESH_KEY_UNUSED, + .addr = bt_mesh.lpn.frnd, + .send_ttl = 0, + }; + struct bt_mesh_net_tx tx = { + .sub = &bt_mesh.sub[0], + .ctx = &ctx, + .src = bt_mesh_primary_addr(), + .xmit = bt_mesh_net_transmit_get(), + }; + struct bt_mesh_ctl_friend_clear req = { + .lpn_addr = sys_cpu_to_be16(tx.src), + .lpn_counter = sys_cpu_to_be16(bt_mesh.lpn.counter), + }; + + BT_DBG("%s", __func__); + + return bt_mesh_ctl_send(&tx, TRANS_CTL_OP_FRIEND_CLEAR, &req, + sizeof(req), NULL, &clear_sent_cb, NULL); +} + +static void clear_friendship(bool force, bool disable) +{ + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + + BT_DBG("force %u disable %u", force, disable); + + if (!force && lpn->established && !lpn->clear_success && + lpn->req_attempts < CLEAR_ATTEMPTS) { + send_friend_clear(); + lpn->disable = disable; + return; + } + + bt_mesh_rx_reset(); + + k_delayed_work_cancel(&lpn->timer); + + friend_cred_del(bt_mesh.sub[0].net_idx, lpn->frnd); + + if (lpn->clear_success) { + lpn->old_friend = BLE_MESH_ADDR_UNASSIGNED; + } else { + lpn->old_friend = lpn->frnd; + } + + if (lpn_cb && lpn->frnd != BLE_MESH_ADDR_UNASSIGNED) { + lpn_cb(lpn->frnd, false); + } + + lpn->frnd = BLE_MESH_ADDR_UNASSIGNED; + lpn->fsn = 0U; + lpn->req_attempts = 0U; + lpn->recv_win = 0U; + lpn->queue_size = 0U; + lpn->disable = 0U; + lpn->sent_req = 0U; + lpn->established = 0U; + lpn->clear_success = 0U; + + group_zero(lpn->added); + group_zero(lpn->pending); + group_zero(lpn->to_remove); + + /* Set this to 1 to force group subscription when the next + * Friendship is created, in case lpn->groups doesn't get + * modified meanwhile. + */ + lpn->groups_changed = 1U; + + if (disable) { + lpn_set_state(BLE_MESH_LPN_DISABLED); + return; + } + + lpn_set_state(BLE_MESH_LPN_ENABLED); + k_delayed_work_submit(&lpn->timer, FRIEND_REQ_RETRY_TIMEOUT); +} + +static void friend_req_sent(u16_t duration, int err, void *user_data) +{ + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + + if (err) { + BT_ERR("%s, Sending Friend Request failed (err %d)", __func__, err); + return; + } + + lpn->adv_duration = duration; + + if (IS_ENABLED(CONFIG_BLE_MESH_LPN_ESTABLISHMENT)) { + k_delayed_work_submit(&lpn->timer, FRIEND_REQ_WAIT); + lpn_set_state(BLE_MESH_LPN_REQ_WAIT); + } else { + k_delayed_work_submit(&lpn->timer, + duration + FRIEND_REQ_TIMEOUT); + lpn_set_state(BLE_MESH_LPN_WAIT_OFFER); + } +} + +static const struct bt_mesh_send_cb friend_req_sent_cb = { + .start = friend_req_sent, +}; + +static int send_friend_req(struct bt_mesh_lpn *lpn) +{ + const struct bt_mesh_comp *comp = bt_mesh_comp_get(); + struct bt_mesh_msg_ctx ctx = { + .net_idx = bt_mesh.sub[0].net_idx, + .app_idx = BLE_MESH_KEY_UNUSED, + .addr = BLE_MESH_ADDR_FRIENDS, + .send_ttl = 0, + }; + struct bt_mesh_net_tx tx = { + .sub = &bt_mesh.sub[0], + .ctx = &ctx, + .src = bt_mesh_primary_addr(), + .xmit = POLL_XMIT, + }; + struct bt_mesh_ctl_friend_req req = { + .criteria = LPN_CRITERIA, + .recv_delay = LPN_RECV_DELAY, + .poll_to = LPN_POLL_TO, + .prev_addr = lpn->old_friend, + .num_elem = comp->elem_count, + .lpn_counter = sys_cpu_to_be16(lpn->counter), + }; + + BT_DBG("%s", __func__); + + return bt_mesh_ctl_send(&tx, TRANS_CTL_OP_FRIEND_REQ, &req, + sizeof(req), NULL, &friend_req_sent_cb, NULL); +} + +static void req_sent(u16_t duration, int err, void *user_data) +{ + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + +#if defined(CONFIG_BLE_MESH_DEBUG_LOW_POWER) + BT_DBG("req 0x%02x duration %u err %d state %s", + lpn->sent_req, duration, err, state2str(lpn->state)); +#endif + + if (err) { + BT_ERR("%s, Sending request failed (err %d)", __func__, err); + lpn->sent_req = 0U; + group_zero(lpn->pending); + return; + } + + lpn->req_attempts++; + lpn->adv_duration = duration; + + if (lpn->established || IS_ENABLED(CONFIG_BLE_MESH_LPN_ESTABLISHMENT)) { + lpn_set_state(BLE_MESH_LPN_RECV_DELAY); + /* We start scanning a bit early to elimitate risk of missing + * response data due to HCI and other latencies. + */ + k_delayed_work_submit(&lpn->timer, + LPN_RECV_DELAY - SCAN_LATENCY); + } else { + lpn_set_state(BLE_MESH_LPN_OFFER_RECV); + k_delayed_work_submit(&lpn->timer, + LPN_RECV_DELAY + duration + + lpn->recv_win); + } +} + +static const struct bt_mesh_send_cb req_sent_cb = { + .start = req_sent, +}; + +static int send_friend_poll(void) +{ + struct bt_mesh_msg_ctx ctx = { + .net_idx = bt_mesh.sub[0].net_idx, + .app_idx = BLE_MESH_KEY_UNUSED, + .addr = bt_mesh.lpn.frnd, + .send_ttl = 0, + }; + struct bt_mesh_net_tx tx = { + .sub = &bt_mesh.sub[0], + .ctx = &ctx, + .src = bt_mesh_primary_addr(), + .xmit = POLL_XMIT, + .friend_cred = true, + }; + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + u8_t fsn = lpn->fsn; + int err; + + BT_DBG("lpn->sent_req 0x%02x", lpn->sent_req); + + if (lpn->sent_req) { + if (lpn->sent_req != TRANS_CTL_OP_FRIEND_POLL) { + lpn->pending_poll = 1U; + } + + return 0; + } + + err = bt_mesh_ctl_send(&tx, TRANS_CTL_OP_FRIEND_POLL, &fsn, 1, + NULL, &req_sent_cb, NULL); + if (err == 0) { + lpn->pending_poll = 0U; + lpn->sent_req = TRANS_CTL_OP_FRIEND_POLL; + } + + return err; +} + +void bt_mesh_lpn_disable(bool force) +{ + if (bt_mesh.lpn.state == BLE_MESH_LPN_DISABLED) { + return; + } + + clear_friendship(force, true); +} + +int bt_mesh_lpn_set(bool enable) +{ + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + + if (enable) { + if (lpn->state != BLE_MESH_LPN_DISABLED) { + return 0; + } + } else { + if (lpn->state == BLE_MESH_LPN_DISABLED) { + return 0; + } + } + + if (!bt_mesh_is_provisioned()) { + if (enable) { + lpn_set_state(BLE_MESH_LPN_ENABLED); + } else { + lpn_set_state(BLE_MESH_LPN_DISABLED); + } + + return 0; + } + + if (enable) { + lpn_set_state(BLE_MESH_LPN_ENABLED); + + if (IS_ENABLED(CONFIG_BLE_MESH_LPN_ESTABLISHMENT)) { + bt_mesh_scan_disable(); + } + + send_friend_req(lpn); + } else { + if (IS_ENABLED(CONFIG_BLE_MESH_LPN_AUTO) && + lpn->state == BLE_MESH_LPN_TIMER) { + k_delayed_work_cancel(&lpn->timer); + lpn_set_state(BLE_MESH_LPN_DISABLED); + } else { + bt_mesh_lpn_disable(false); + } + } + + return 0; +} + +static void friend_response_received(struct bt_mesh_lpn *lpn) +{ + BT_DBG("lpn->sent_req 0x%02x", lpn->sent_req); + + if (lpn->sent_req == TRANS_CTL_OP_FRIEND_POLL) { + lpn->fsn++; + } + + k_delayed_work_cancel(&lpn->timer); + bt_mesh_scan_disable(); + lpn_set_state(BLE_MESH_LPN_ESTABLISHED); + lpn->req_attempts = 0U; + lpn->sent_req = 0U; +} + +void bt_mesh_lpn_msg_received(struct bt_mesh_net_rx *rx) +{ + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + + if (lpn->state == BLE_MESH_LPN_TIMER) { + BT_DBG("Restarting establishment timer"); + k_delayed_work_submit(&lpn->timer, LPN_AUTO_TIMEOUT); + return; + } + + if (lpn->sent_req != TRANS_CTL_OP_FRIEND_POLL) { + BT_WARN("Unexpected message withouth a preceding Poll"); + return; + } + + friend_response_received(lpn); + + BT_DBG("Requesting more messages from Friend"); + + send_friend_poll(); +} + +int bt_mesh_lpn_friend_offer(struct bt_mesh_net_rx *rx, + struct net_buf_simple *buf) +{ + struct bt_mesh_ctl_friend_offer *msg = (void *)buf->data; + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + struct bt_mesh_subnet *sub = rx->sub; + struct friend_cred *cred; + u16_t frnd_counter; + int err; + + if (buf->len < sizeof(*msg)) { + BT_WARN("Too short Friend Offer"); + return -EINVAL; + } + + if (lpn->state != BLE_MESH_LPN_WAIT_OFFER) { + BT_WARN("Ignoring unexpected Friend Offer"); + return 0; + } + + if (!msg->recv_win) { + BT_WARN("Prohibited ReceiveWindow value"); + return -EINVAL; + } + + frnd_counter = sys_be16_to_cpu(msg->frnd_counter); + + BT_DBG("recv_win %u queue_size %u sub_list_size %u rssi %d counter %u", + msg->recv_win, msg->queue_size, msg->sub_list_size, msg->rssi, + frnd_counter); + + lpn->frnd = rx->ctx.addr; + + cred = friend_cred_create(sub, lpn->frnd, lpn->counter, frnd_counter); + if (!cred) { + lpn->frnd = BLE_MESH_ADDR_UNASSIGNED; + return -ENOMEM; + } + + /* TODO: Add offer acceptance criteria check */ + + k_delayed_work_cancel(&lpn->timer); + + lpn->recv_win = msg->recv_win; + lpn->queue_size = msg->queue_size; + + err = send_friend_poll(); + if (err) { + friend_cred_clear(cred); + lpn->frnd = BLE_MESH_ADDR_UNASSIGNED; + lpn->recv_win = 0U; + lpn->queue_size = 0U; + return err; + } + + lpn->counter++; + + return 0; +} + +int bt_mesh_lpn_friend_clear_cfm(struct bt_mesh_net_rx *rx, + struct net_buf_simple *buf) +{ + struct bt_mesh_ctl_friend_clear_confirm *msg = (void *)buf->data; + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + u16_t addr, counter; + + if (buf->len < sizeof(*msg)) { + BT_WARN("Too short Friend Clear Confirm"); + return -EINVAL; + } + + if (lpn->state != BLE_MESH_LPN_CLEAR) { + BT_WARN("Ignoring unexpected Friend Clear Confirm"); + return 0; + } + + addr = sys_be16_to_cpu(msg->lpn_addr); + counter = sys_be16_to_cpu(msg->lpn_counter); + + BT_DBG("LPNAddress 0x%04x LPNCounter 0x%04x", addr, counter); + + if (addr != bt_mesh_primary_addr() || counter != lpn->counter) { + BT_WARN("Invalid parameters in Friend Clear Confirm"); + return 0; + } + + lpn->clear_success = 1U; + clear_friendship(false, lpn->disable); + + return 0; +} + +static void lpn_group_add(u16_t group) +{ + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + u16_t *free_slot = NULL; + int i; + + for (i = 0; i < ARRAY_SIZE(lpn->groups); i++) { + if (lpn->groups[i] == group) { + bt_mesh_atomic_clear_bit(lpn->to_remove, i); + return; + } + + if (!free_slot && lpn->groups[i] == BLE_MESH_ADDR_UNASSIGNED) { + free_slot = &lpn->groups[i]; + } + } + + if (!free_slot) { + BT_WARN("Friend Subscription List exceeded!"); + return; + } + + *free_slot = group; + lpn->groups_changed = 1U; +} + +static void lpn_group_del(u16_t group) +{ + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + int i; + + for (i = 0; i < ARRAY_SIZE(lpn->groups); i++) { + if (lpn->groups[i] == group) { + if (bt_mesh_atomic_test_bit(lpn->added, i) || + bt_mesh_atomic_test_bit(lpn->pending, i)) { + bt_mesh_atomic_set_bit(lpn->to_remove, i); + lpn->groups_changed = 1U; + } else { + lpn->groups[i] = BLE_MESH_ADDR_UNASSIGNED; + } + } + } +} + +static inline int group_popcount(bt_mesh_atomic_t *target) +{ +#if CONFIG_BLE_MESH_LPN_GROUPS > 32 + int i, count = 0; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.lpn.added); i++) { + count += popcount(bt_mesh_atomic_get(&target[i])); + } +#else + return popcount(bt_mesh_atomic_get(target)); +#endif +} + +static bool sub_update(u8_t op) +{ + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + int added_count = group_popcount(lpn->added); + struct bt_mesh_msg_ctx ctx = { + .net_idx = bt_mesh.sub[0].net_idx, + .app_idx = BLE_MESH_KEY_UNUSED, + .addr = lpn->frnd, + .send_ttl = 0, + }; + struct bt_mesh_net_tx tx = { + .sub = &bt_mesh.sub[0], + .ctx = &ctx, + .src = bt_mesh_primary_addr(), + .xmit = POLL_XMIT, + .friend_cred = true, + }; + struct bt_mesh_ctl_friend_sub req; + size_t i, g; + + BT_DBG("op 0x%02x sent_req 0x%02x", op, lpn->sent_req); + + if (lpn->sent_req) { + return false; + } + + for (i = 0, g = 0; i < ARRAY_SIZE(lpn->groups); i++) { + if (lpn->groups[i] == BLE_MESH_ADDR_UNASSIGNED) { + continue; + } + + if (op == TRANS_CTL_OP_FRIEND_SUB_ADD) { + if (bt_mesh_atomic_test_bit(lpn->added, i)) { + continue; + } + } else { + if (!bt_mesh_atomic_test_bit(lpn->to_remove, i)) { + continue; + } + } + + if (added_count + g >= lpn->queue_size) { + BT_WARN("%s, Friend Queue Size exceeded", __func__); + break; + } + + req.addr_list[g++] = sys_cpu_to_be16(lpn->groups[i]); + bt_mesh_atomic_set_bit(lpn->pending, i); + + if (g == ARRAY_SIZE(req.addr_list)) { + break; + } + } + + if (g == 0) { + group_zero(lpn->pending); + return false; + } + + req.xact = lpn->xact_next++; + + if (bt_mesh_ctl_send(&tx, op, &req, 1 + g * 2, NULL, + &req_sent_cb, NULL) < 0) { + group_zero(lpn->pending); + return false; + } + + lpn->xact_pending = req.xact; + lpn->sent_req = op; + return true; +} + +static void update_timeout(struct bt_mesh_lpn *lpn) +{ + if (lpn->established) { + BT_WARN("No response from Friend during ReceiveWindow"); + bt_mesh_scan_disable(); + lpn_set_state(BLE_MESH_LPN_ESTABLISHED); + k_delayed_work_submit(&lpn->timer, POLL_RETRY_TIMEOUT); + } else { + if (IS_ENABLED(CONFIG_BLE_MESH_LPN_ESTABLISHMENT)) { + bt_mesh_scan_disable(); + } + + if (lpn->req_attempts < 6) { + BT_WARN("Retrying first Friend Poll"); + lpn->sent_req = 0U; + if (send_friend_poll() == 0) { + return; + } + } + + BT_ERR("Timed out waiting for first Friend Update"); + clear_friendship(false, false); + } +} + +static void lpn_timeout(struct k_work *work) +{ + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + +#if defined(CONFIG_BLE_MESH_DEBUG_LOW_POWER) + BT_DBG("state: %s", state2str(lpn->state)); +#endif + + switch (lpn->state) { + case BLE_MESH_LPN_DISABLED: + break; + case BLE_MESH_LPN_CLEAR: + clear_friendship(false, bt_mesh.lpn.disable); + break; + case BLE_MESH_LPN_TIMER: + BT_DBG("Starting to look for Friend nodes"); + lpn_set_state(BLE_MESH_LPN_ENABLED); + if (IS_ENABLED(CONFIG_BLE_MESH_LPN_ESTABLISHMENT)) { + bt_mesh_scan_disable(); + } + /* fall through */ + case BLE_MESH_LPN_ENABLED: + send_friend_req(lpn); + break; + case BLE_MESH_LPN_REQ_WAIT: + bt_mesh_scan_enable(); + k_delayed_work_submit(&lpn->timer, + lpn->adv_duration + FRIEND_REQ_SCAN); + lpn_set_state(BLE_MESH_LPN_WAIT_OFFER); + break; + case BLE_MESH_LPN_WAIT_OFFER: + BT_WARN("No acceptable Friend Offers received"); + if (IS_ENABLED(CONFIG_BLE_MESH_LPN_ESTABLISHMENT)) { + bt_mesh_scan_disable(); + } + lpn->counter++; + lpn_set_state(BLE_MESH_LPN_ENABLED); + k_delayed_work_submit(&lpn->timer, FRIEND_REQ_RETRY_TIMEOUT); + break; + case BLE_MESH_LPN_OFFER_RECV: + BT_WARN("No Friend Update received after the first Friend Poll"); + lpn->sent_req = 0U; + send_friend_poll(); + break; + case BLE_MESH_LPN_ESTABLISHED: + if (lpn->req_attempts < REQ_ATTEMPTS(lpn)) { + u8_t req = lpn->sent_req; + + lpn->sent_req = 0U; + + if (!req || req == TRANS_CTL_OP_FRIEND_POLL) { + send_friend_poll(); + } else { + sub_update(req); + } + + break; + } + + BT_ERR("No response from Friend after %u retries", + lpn->req_attempts); + lpn->req_attempts = 0U; + clear_friendship(false, false); + break; + case BLE_MESH_LPN_RECV_DELAY: + k_delayed_work_submit(&lpn->timer, + lpn->adv_duration + SCAN_LATENCY + + lpn->recv_win); + bt_mesh_scan_enable(); + lpn_set_state(BLE_MESH_LPN_WAIT_UPDATE); + break; + case BLE_MESH_LPN_WAIT_UPDATE: + update_timeout(lpn); + break; + default: + __ASSERT(0, "Unhandled LPN state"); + break; + } +} + +void bt_mesh_lpn_group_add(u16_t group) +{ + BT_DBG("group 0x%04x", group); + + lpn_group_add(group); + + if (!bt_mesh_lpn_established() || bt_mesh.lpn.sent_req) { + return; + } + + sub_update(TRANS_CTL_OP_FRIEND_SUB_ADD); +} + +void bt_mesh_lpn_group_del(u16_t *groups, size_t group_count) +{ + int i; + + for (i = 0; i < group_count; i++) { + if (groups[i] != BLE_MESH_ADDR_UNASSIGNED) { + BT_DBG("group 0x%04x", groups[i]); + lpn_group_del(groups[i]); + } + } + + if (!bt_mesh_lpn_established() || bt_mesh.lpn.sent_req) { + return; + } + + sub_update(TRANS_CTL_OP_FRIEND_SUB_REM); +} + +static s32_t poll_timeout(struct bt_mesh_lpn *lpn) +{ + /* If we're waiting for segment acks keep polling at high freq */ + if (bt_mesh_tx_in_progress()) { + return MIN(POLL_TIMEOUT_MAX(lpn), K_SECONDS(1)); + } + + if (lpn->poll_timeout < POLL_TIMEOUT_MAX(lpn)) { + lpn->poll_timeout *= 2; + lpn->poll_timeout = MIN(lpn->poll_timeout, + POLL_TIMEOUT_MAX(lpn)); + } + + BT_DBG("Poll Timeout is %ums", lpn->poll_timeout); + + return lpn->poll_timeout; +} + +int bt_mesh_lpn_friend_sub_cfm(struct bt_mesh_net_rx *rx, + struct net_buf_simple *buf) +{ + struct bt_mesh_ctl_friend_sub_confirm *msg = (void *)buf->data; + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + + if (buf->len < sizeof(*msg)) { + BT_WARN("Too short Friend Subscription Confirm"); + return -EINVAL; + } + + BT_DBG("xact 0x%02x", msg->xact); + + if (!lpn->sent_req) { + BT_WARN("No pending subscription list message"); + return 0; + } + + if (msg->xact != lpn->xact_pending) { + BT_WARN("Transaction mismatch (0x%02x != 0x%02x)", + msg->xact, lpn->xact_pending); + return 0; + } + + if (lpn->sent_req == TRANS_CTL_OP_FRIEND_SUB_ADD) { + group_set(lpn->added, lpn->pending); + group_zero(lpn->pending); + } else if (lpn->sent_req == TRANS_CTL_OP_FRIEND_SUB_REM) { + int i; + + group_clear(lpn->added, lpn->pending); + + for (i = 0; i < ARRAY_SIZE(lpn->groups); i++) { + if (bt_mesh_atomic_test_and_clear_bit(lpn->pending, i) && + bt_mesh_atomic_test_and_clear_bit(lpn->to_remove, i)) { + lpn->groups[i] = BLE_MESH_ADDR_UNASSIGNED; + } + } + } else { + BT_WARN("Unexpected Friend Subscription Confirm"); + return 0; + } + + friend_response_received(lpn); + + if (lpn->groups_changed) { + sub_update(TRANS_CTL_OP_FRIEND_SUB_ADD); + sub_update(TRANS_CTL_OP_FRIEND_SUB_REM); + + if (!lpn->sent_req) { + lpn->groups_changed = 0U; + } + } + + if (lpn->pending_poll) { + send_friend_poll(); + } + + if (!lpn->sent_req) { + k_delayed_work_submit(&lpn->timer, poll_timeout(lpn)); + } + + return 0; +} + +int bt_mesh_lpn_friend_update(struct bt_mesh_net_rx *rx, + struct net_buf_simple *buf) +{ + struct bt_mesh_ctl_friend_update *msg = (void *)buf->data; + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + struct bt_mesh_subnet *sub = rx->sub; + u32_t iv_index; + + if (buf->len < sizeof(*msg)) { + BT_WARN("Too short Friend Update"); + return -EINVAL; + } + + if (lpn->sent_req != TRANS_CTL_OP_FRIEND_POLL) { + BT_WARN("Unexpected friend update"); + return 0; + } + + if (sub->kr_phase == BLE_MESH_KR_PHASE_2 && !rx->new_key) { + BT_WARN("Ignoring Phase 2 KR Update secured using old key"); + return 0; + } + + if (bt_mesh_atomic_test_bit(bt_mesh.flags, BLE_MESH_IVU_INITIATOR) && + (bt_mesh_atomic_test_bit(bt_mesh.flags, BLE_MESH_IVU_IN_PROGRESS) == + BLE_MESH_IV_UPDATE(msg->flags))) { + bt_mesh_beacon_ivu_initiator(false); + } + + if (!lpn->established) { + /* This is normally checked on the transport layer, however + * in this state we're also still accepting master + * credentials so we need to ensure the right ones (Friend + * Credentials) were used for this message. + */ + if (!rx->friend_cred) { + BT_WARN("Friend Update with wrong credentials"); + return -EINVAL; + } + + lpn->established = 1U; + + BT_INFO("Friendship established with 0x%04x", lpn->frnd); + + if (lpn_cb) { + lpn_cb(lpn->frnd, true); + } + + /* Set initial poll timeout */ + lpn->poll_timeout = MIN(POLL_TIMEOUT_MAX(lpn), + POLL_TIMEOUT_INIT); + } + + friend_response_received(lpn); + + iv_index = sys_be32_to_cpu(msg->iv_index); + + BT_DBG("flags 0x%02x iv_index 0x%08x md %u", msg->flags, iv_index, + msg->md); + + if (bt_mesh_kr_update(sub, BLE_MESH_KEY_REFRESH(msg->flags), + rx->new_key)) { + bt_mesh_net_beacon_update(sub); + } + + bt_mesh_net_iv_update(iv_index, BLE_MESH_IV_UPDATE(msg->flags)); + + if (lpn->groups_changed) { + sub_update(TRANS_CTL_OP_FRIEND_SUB_ADD); + sub_update(TRANS_CTL_OP_FRIEND_SUB_REM); + + if (!lpn->sent_req) { + lpn->groups_changed = 0U; + } + } + + if (msg->md) { + BT_DBG("Requesting for more messages"); + send_friend_poll(); + } + + if (!lpn->sent_req) { + k_delayed_work_submit(&lpn->timer, poll_timeout(lpn)); + } + + return 0; +} + +int bt_mesh_lpn_poll(void) +{ + if (!bt_mesh.lpn.established) { + return -EAGAIN; + } + + BT_DBG("Requesting more messages"); + + return send_friend_poll(); +} + +void bt_mesh_lpn_set_cb(void (*cb)(u16_t friend_addr, bool established)) +{ + lpn_cb = cb; +} + +int bt_mesh_lpn_init(void) +{ + struct bt_mesh_lpn *lpn = &bt_mesh.lpn; + + BT_DBG("%s", __func__); + + k_delayed_work_init(&lpn->timer, lpn_timeout); + + if (lpn->state == BLE_MESH_LPN_ENABLED) { + if (IS_ENABLED(CONFIG_BLE_MESH_LPN_ESTABLISHMENT)) { + bt_mesh_scan_disable(); + } else { + bt_mesh_scan_enable(); + } + + send_friend_req(lpn); + } else { + bt_mesh_scan_enable(); + + if (IS_ENABLED(CONFIG_BLE_MESH_LPN_AUTO)) { + BT_DBG("Waiting %u ms for messages", LPN_AUTO_TIMEOUT); + lpn_set_state(BLE_MESH_LPN_TIMER); + k_delayed_work_submit(&lpn->timer, LPN_AUTO_TIMEOUT); + } + } + + return 0; +} + +#endif /* CONFIG_BLE_MESH_LOW_POWER */ diff --git a/components/bt/ble_mesh/mesh_core/lpn.h b/components/bt/ble_mesh/mesh_core/lpn.h new file mode 100644 index 0000000000..ad870e99e1 --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/lpn.h @@ -0,0 +1,67 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _LPN_H_ +#define _LPN_H_ + +int bt_mesh_lpn_friend_update(struct bt_mesh_net_rx *rx, + struct net_buf_simple *buf); +int bt_mesh_lpn_friend_offer(struct bt_mesh_net_rx *rx, + struct net_buf_simple *buf); +int bt_mesh_lpn_friend_clear_cfm(struct bt_mesh_net_rx *rx, + struct net_buf_simple *buf); +int bt_mesh_lpn_friend_sub_cfm(struct bt_mesh_net_rx *rx, + struct net_buf_simple *buf); + +static inline bool bt_mesh_lpn_established(void) +{ +#if defined(CONFIG_BLE_MESH_LOW_POWER) + return bt_mesh.lpn.established; +#else + return false; +#endif +} + +static inline bool bt_mesh_lpn_match(u16_t addr) +{ +#if defined(CONFIG_BLE_MESH_LOW_POWER) + if (bt_mesh_lpn_established()) { + return (addr == bt_mesh.lpn.frnd); + } +#endif + return false; +} + +static inline bool bt_mesh_lpn_waiting_update(void) +{ +#if defined(CONFIG_BLE_MESH_LOW_POWER) + return (bt_mesh.lpn.state == BLE_MESH_LPN_WAIT_UPDATE); +#else + return false; +#endif +} + +static inline bool bt_mesh_lpn_timer(void) +{ +#if defined(CONFIG_BLE_MESH_LPN_AUTO) + return (bt_mesh.lpn.state == BLE_MESH_LPN_TIMER); +#else + return false; +#endif +} + +void bt_mesh_lpn_msg_received(struct bt_mesh_net_rx *rx); + +void bt_mesh_lpn_group_add(u16_t group); +void bt_mesh_lpn_group_del(u16_t *groups, size_t group_count); + +void bt_mesh_lpn_disable(bool force); + +int bt_mesh_lpn_init(void); + +#endif /* _LPN_H_ */ diff --git a/components/bt/ble_mesh/mesh_core/mesh.h b/components/bt/ble_mesh/mesh_core/mesh.h new file mode 100644 index 0000000000..c536184aa9 --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/mesh.h @@ -0,0 +1,22 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _MESH_H_ +#define _MESH_H_ + +#define BLE_MESH_KEY_PRIMARY 0x0000 +#define BLE_MESH_KEY_ANY 0xffff + +#define BLE_MESH_ADDR_IS_UNICAST(addr) ((addr) && (addr) < 0x8000) +#define BLE_MESH_ADDR_IS_GROUP(addr) ((addr) >= 0xc000 && (addr) <= 0xff00) +#define BLE_MESH_ADDR_IS_VIRTUAL(addr) ((addr) >= 0x8000 && (addr) < 0xc000) +#define BLE_MESH_ADDR_IS_RFU(addr) ((addr) >= 0xff00 && (addr) <= 0xfffb) + +struct bt_mesh_net; + +#endif /* _MESH_H_ */ diff --git a/components/bt/ble_mesh/mesh_core/mesh_aes_encrypt.c b/components/bt/ble_mesh/mesh_core/mesh_aes_encrypt.c new file mode 100644 index 0000000000..2d1842cc4b --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/mesh_aes_encrypt.c @@ -0,0 +1,409 @@ +/* aes_encrypt.c - TinyCrypt implementation of AES encryption procedure */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "mesh_aes_encrypt.h" +#include "mesh_util.h" +#include "sdkconfig.h" + +/* max number of calls until change the key (2^48).*/ +const static uint64_t MAX_CALLS = ((uint64_t)1 << 48); + +/* + * gf_wrap -- In our implementation, GF(2^128) is represented as a 16 byte + * array with byte 0 the most significant and byte 15 the least significant. + * High bit carry reduction is based on the primitive polynomial + * + * X^128 + X^7 + X^2 + X + 1, + * + * which leads to the reduction formula X^128 = X^7 + X^2 + X + 1. Indeed, + * since 0 = (X^128 + X^7 + X^2 + 1) mod (X^128 + X^7 + X^2 + X + 1) and since + * addition of polynomials with coefficients in Z/Z(2) is just XOR, we can + * add X^128 to both sides to get + * + * X^128 = (X^7 + X^2 + X + 1) mod (X^128 + X^7 + X^2 + X + 1) + * + * and the coefficients of the polynomial on the right hand side form the + * string 1000 0111 = 0x87, which is the value of gf_wrap. + * + * This gets used in the following way. Doubling in GF(2^128) is just a left + * shift by 1 bit, except when the most significant bit is 1. In the latter + * case, the relation X^128 = X^7 + X^2 + X + 1 says that the high order bit + * that overflows beyond 128 bits can be replaced by addition of + * X^7 + X^2 + X + 1 <--> 0x87 to the low order 128 bits. Since addition + * in GF(2^128) is represented by XOR, we therefore only have to XOR 0x87 + * into the low order byte after a left shift when the starting high order + * bit is 1. + */ +const unsigned char gf_wrap = 0x87; + +static const uint8_t sbox[256] = { + 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, + 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, + 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, + 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, + 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, + 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, + 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, + 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, + 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, + 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, + 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, + 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, + 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, + 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, + 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, + 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, + 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, + 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, + 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, + 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, + 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, + 0xb0, 0x54, 0xbb, 0x16 +}; + +static inline unsigned int rotword(unsigned int a) +{ + return (((a) >> 24) | ((a) << 8)); +} + +#define subbyte(a, o) (sbox[((a) >> (o))&0xff] << (o)) +#define subword(a) (subbyte(a, 24)|subbyte(a, 16)|subbyte(a, 8)|subbyte(a, 0)) + +int tc_aes128_set_encrypt_key(TCAesKeySched_t s, const uint8_t *k) +{ + const unsigned int rconst[11] = { + 0x00000000, 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, + 0x20000000, 0x40000000, 0x80000000, 0x1b000000, 0x36000000 + }; + unsigned int i; + unsigned int t; + + if (s == (TCAesKeySched_t) 0) { + return TC_CRYPTO_FAIL; + } else if (k == (const uint8_t *) 0) { + return TC_CRYPTO_FAIL; + } + + for (i = 0; i < Nk; ++i) { + s->words[i] = (k[Nb * i] << 24) | (k[Nb * i + 1] << 16) | + (k[Nb * i + 2] << 8) | (k[Nb * i + 3]); + } + + for (; i < (Nb * (Nr + 1)); ++i) { + t = s->words[i - 1]; + if ((i % Nk) == 0) { + t = subword(rotword(t)) ^ rconst[i / Nk]; + } + s->words[i] = s->words[i - Nk] ^ t; + } + + return TC_CRYPTO_SUCCESS; +} + +static inline void add_round_key(uint8_t *s, const unsigned int *k) +{ + s[0] ^= (uint8_t)(k[0] >> 24); s[1] ^= (uint8_t)(k[0] >> 16); + s[2] ^= (uint8_t)(k[0] >> 8); s[3] ^= (uint8_t)(k[0]); + s[4] ^= (uint8_t)(k[1] >> 24); s[5] ^= (uint8_t)(k[1] >> 16); + s[6] ^= (uint8_t)(k[1] >> 8); s[7] ^= (uint8_t)(k[1]); + s[8] ^= (uint8_t)(k[2] >> 24); s[9] ^= (uint8_t)(k[2] >> 16); + s[10] ^= (uint8_t)(k[2] >> 8); s[11] ^= (uint8_t)(k[2]); + s[12] ^= (uint8_t)(k[3] >> 24); s[13] ^= (uint8_t)(k[3] >> 16); + s[14] ^= (uint8_t)(k[3] >> 8); s[15] ^= (uint8_t)(k[3]); +} + +static inline void sub_bytes(uint8_t *s) +{ + unsigned int i; + + for (i = 0; i < (Nb * Nk); ++i) { + s[i] = sbox[s[i]]; + } +} + +#define triple(a)(_double_byte(a)^(a)) + +static inline void mult_row_column(uint8_t *out, const uint8_t *in) +{ + out[0] = _double_byte(in[0]) ^ triple(in[1]) ^ in[2] ^ in[3]; + out[1] = in[0] ^ _double_byte(in[1]) ^ triple(in[2]) ^ in[3]; + out[2] = in[0] ^ in[1] ^ _double_byte(in[2]) ^ triple(in[3]); + out[3] = triple(in[0]) ^ in[1] ^ in[2] ^ _double_byte(in[3]); +} + +static inline void mix_columns(uint8_t *s) +{ + uint8_t t[Nb * Nk]; + + mult_row_column(t, s); + mult_row_column(&t[Nb], s + Nb); + mult_row_column(&t[2 * Nb], s + (2 * Nb)); + mult_row_column(&t[3 * Nb], s + (3 * Nb)); + (void) _copy(s, sizeof(t), t, sizeof(t)); +} + +/* + * This shift_rows also implements the matrix flip required for mix_columns, but + * performs it here to reduce the number of memory operations. + */ +static inline void shift_rows(uint8_t *s) +{ + uint8_t t[Nb * Nk]; + + t[0] = s[0]; t[1] = s[5]; t[2] = s[10]; t[3] = s[15]; + t[4] = s[4]; t[5] = s[9]; t[6] = s[14]; t[7] = s[3]; + t[8] = s[8]; t[9] = s[13]; t[10] = s[2]; t[11] = s[7]; + t[12] = s[12]; t[13] = s[1]; t[14] = s[6]; t[15] = s[11]; + (void) _copy(s, sizeof(t), t, sizeof(t)); +} + +int tc_aes_encrypt(uint8_t *out, const uint8_t *in, const TCAesKeySched_t s) +{ + uint8_t state[Nk * Nb]; + unsigned int i; + + if (out == (uint8_t *) 0) { + return TC_CRYPTO_FAIL; + } else if (in == (const uint8_t *) 0) { + return TC_CRYPTO_FAIL; + } else if (s == (TCAesKeySched_t) 0) { + return TC_CRYPTO_FAIL; + } + + (void)_copy(state, sizeof(state), in, sizeof(state)); + add_round_key(state, s->words); + + for (i = 0; i < (Nr - 1); ++i) { + sub_bytes(state); + shift_rows(state); + mix_columns(state); + add_round_key(state, s->words + Nb * (i + 1)); + } + + sub_bytes(state); + shift_rows(state); + add_round_key(state, s->words + Nb * (i + 1)); + + (void)_copy(out, sizeof(state), state, sizeof(state)); + + /* zeroing out the state buffer */ + _set(state, TC_ZERO_BYTE, sizeof(state)); + + return TC_CRYPTO_SUCCESS; +} + +int tc_cmac_setup(TCCmacState_t s, const uint8_t *key, TCAesKeySched_t sched) +{ + + /* input sanity check: */ + if (s == (TCCmacState_t) 0 || + key == (const uint8_t *) 0) { + return TC_CRYPTO_FAIL; + } + + /* put s into a known state */ + _set(s, 0, sizeof(*s)); + s->sched = sched; + + /* configure the encryption key used by the underlying block cipher */ + tc_aes128_set_encrypt_key(s->sched, key); + + /* compute s->K1 and s->K2 from s->iv using s->keyid */ + _set(s->iv, 0, TC_AES_BLOCK_SIZE); + tc_aes_encrypt(s->iv, s->iv, s->sched); + gf_double (s->K1, s->iv); + gf_double (s->K2, s->K1); + + /* reset s->iv to 0 in case someone wants to compute now */ + tc_cmac_init(s); + + return TC_CRYPTO_SUCCESS; +} + +/* + * assumes: out != NULL and points to a GF(2^n) value to receive the + * doubled value; + * in != NULL and points to a 16 byte GF(2^n) value + * to double; + * the in and out buffers do not overlap. + * effects: doubles the GF(2^n) value pointed to by "in" and places + * the result in the GF(2^n) value pointed to by "out." + */ +void gf_double(uint8_t *out, uint8_t *in) +{ + + /* start with low order byte */ + uint8_t *x = in + (TC_AES_BLOCK_SIZE - 1); + + /* if msb == 1, we need to add the gf_wrap value, otherwise add 0 */ + uint8_t carry = (in[0] >> 7) ? gf_wrap : 0; + + out += (TC_AES_BLOCK_SIZE - 1); + for (;;) { + *out-- = (*x << 1) ^ carry; + if (x == in) { + break; + } + carry = *x-- >> 7; + } +} + +int tc_cmac_init(TCCmacState_t s) +{ + /* input sanity check: */ + if (s == (TCCmacState_t) 0) { + return TC_CRYPTO_FAIL; + } + + /* CMAC starts with an all zero initialization vector */ + _set(s->iv, 0, TC_AES_BLOCK_SIZE); + + /* and the leftover buffer is empty */ + _set(s->leftover, 0, TC_AES_BLOCK_SIZE); + s->leftover_offset = 0; + + /* Set countdown to max number of calls allowed before re-keying: */ + s->countdown = MAX_CALLS; + + return TC_CRYPTO_SUCCESS; +} + +int tc_cmac_update(TCCmacState_t s, const uint8_t *data, size_t data_length) +{ + unsigned int i; + + /* input sanity check: */ + if (s == (TCCmacState_t) 0) { + return TC_CRYPTO_FAIL; + } + if (data_length == 0) { + return TC_CRYPTO_SUCCESS; + } + if (data == (const uint8_t *) 0) { + return TC_CRYPTO_FAIL; + } + + if (s->countdown == 0) { + return TC_CRYPTO_FAIL; + } + + s->countdown--; + + if (s->leftover_offset > 0) { + /* last data added to s didn't end on a TC_AES_BLOCK_SIZE byte boundary */ + size_t remaining_space = TC_AES_BLOCK_SIZE - s->leftover_offset; + + if (data_length < remaining_space) { + /* still not enough data to encrypt this time either */ + _copy(&s->leftover[s->leftover_offset], data_length, data, data_length); + s->leftover_offset += data_length; + return TC_CRYPTO_SUCCESS; + } + /* leftover block is now full; encrypt it first */ + _copy(&s->leftover[s->leftover_offset], + remaining_space, + data, + remaining_space); + data_length -= remaining_space; + data += remaining_space; + s->leftover_offset = 0; + + for (i = 0; i < TC_AES_BLOCK_SIZE; ++i) { + s->iv[i] ^= s->leftover[i]; + } + tc_aes_encrypt(s->iv, s->iv, s->sched); + } + + /* CBC encrypt each (except the last) of the data blocks */ + while (data_length > TC_AES_BLOCK_SIZE) { + for (i = 0; i < TC_AES_BLOCK_SIZE; ++i) { + s->iv[i] ^= data[i]; + } + tc_aes_encrypt(s->iv, s->iv, s->sched); + data += TC_AES_BLOCK_SIZE; + data_length -= TC_AES_BLOCK_SIZE; + } + + if (data_length > 0) { + /* save leftover data for next time */ + _copy(s->leftover, data_length, data, data_length); + s->leftover_offset = data_length; + } + + return TC_CRYPTO_SUCCESS; +} + +int tc_cmac_final(uint8_t *tag, TCCmacState_t s) +{ + uint8_t *k; + unsigned int i; + + /* input sanity check: */ + if (tag == (uint8_t *) 0 || + s == (TCCmacState_t) 0) { + return TC_CRYPTO_FAIL; + } + + if (s->leftover_offset == TC_AES_BLOCK_SIZE) { + /* the last message block is a full-sized block */ + k = (uint8_t *) s->K1; + } else { + /* the final message block is not a full-sized block */ + size_t remaining = TC_AES_BLOCK_SIZE - s->leftover_offset; + + _set(&s->leftover[s->leftover_offset], 0, remaining); + s->leftover[s->leftover_offset] = TC_CMAC_PADDING; + k = (uint8_t *) s->K2; + } + for (i = 0; i < TC_AES_BLOCK_SIZE; ++i) { + s->iv[i] ^= s->leftover[i] ^ k[i]; + } + + tc_aes_encrypt(tag, s->iv, s->sched); + + /* erasing state: */ + tc_cmac_erase(s); + + return TC_CRYPTO_SUCCESS; +} + +int tc_cmac_erase(TCCmacState_t s) +{ + if (s == (TCCmacState_t) 0) { + return TC_CRYPTO_FAIL; + } + + /* destroy the current state */ + _set(s, 0, sizeof(*s)); + + return TC_CRYPTO_SUCCESS; +} diff --git a/components/bt/ble_mesh/mesh_core/mesh_atomic.c b/components/bt/ble_mesh/mesh_core/mesh_atomic.c new file mode 100644 index 0000000000..ce73638053 --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/mesh_atomic.c @@ -0,0 +1,179 @@ +/** + * @brief Atomically set a bit. + * + * Atomically set bit number @a bit of @a target. + * The target may be a single atomic variable or an array of them. + * + * @param target Address of atomic variable or array. + * @param bit Bit number (starting from 0). + * + * @return N/A + */ + +/* + * Copyright (c) 2016 Intel Corporation + * Copyright (c) 2011-2014 Wind River Systems, Inc. + * Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "mesh_atomic.h" +#include "mesh_kernel.h" +#include "sdkconfig.h" + +#ifndef CONFIG_ATOMIC_OPERATIONS_BUILTIN + +/** +* +* @brief Atomic get primitive +* +* @param target memory location to read from +* +* This routine provides the atomic get primitive to atomically read +* a value from . It simply does an ordinary load. Note that +* is expected to be aligned to a 4-byte boundary. +* +* @return The value read from +*/ +bt_mesh_atomic_val_t bt_mesh_atomic_get(const bt_mesh_atomic_t *target) +{ + return *target; +} + +/** + * + * @brief Atomic get-and-set primitive + * + * This routine provides the atomic set operator. The is atomically + * written at and the previous value at is returned. + * + * @param target the memory location to write to + * @param value the value to write + * + * @return The previous value from + */ +bt_mesh_atomic_val_t bt_mesh_atomic_set(bt_mesh_atomic_t *target, bt_mesh_atomic_val_t value) +{ + unsigned int key; + bt_mesh_atomic_val_t ret; + + key = bt_mesh_irq_lock(); + + ret = *target; + *target = value; + + bt_mesh_irq_unlock(key); + + return ret; +} + +/** + * + * @brief Atomic bitwise inclusive OR primitive + * + * This routine provides the atomic bitwise inclusive OR operator. The + * is atomically bitwise OR'ed with the value at , placing the result + * at , and the previous value at is returned. + * + * @param target the memory location to be modified + * @param value the value to OR + * + * @return The previous value from + */ +bt_mesh_atomic_val_t bt_mesh_atomic_or(bt_mesh_atomic_t *target, bt_mesh_atomic_val_t value) +{ + unsigned int key; + bt_mesh_atomic_val_t ret; + + key = bt_mesh_irq_lock(); + + ret = *target; + *target |= value; + + bt_mesh_irq_unlock(key); + + return ret; +} + +/** + * + * @brief Atomic bitwise AND primitive + * + * This routine provides the atomic bitwise AND operator. The is + * atomically bitwise AND'ed with the value at , placing the result + * at , and the previous value at is returned. + * + * @param target the memory location to be modified + * @param value the value to AND + * + * @return The previous value from + */ +bt_mesh_atomic_val_t bt_mesh_atomic_and(bt_mesh_atomic_t *target, bt_mesh_atomic_val_t value) +{ + unsigned int key; + bt_mesh_atomic_val_t ret; + + key = bt_mesh_irq_lock(); + + ret = *target; + *target &= value; + + bt_mesh_irq_unlock(key); + + return ret; +} + +/** + * + * @brief Atomic decrement primitive + * + * @param target memory location to decrement + * + * This routine provides the atomic decrement operator. The value at + * is atomically decremented by 1, and the old value from is returned. + * + * @return The value from prior to the decrement + */ +bt_mesh_atomic_val_t bt_mesh_atomic_dec(bt_mesh_atomic_t *target) +{ + unsigned int key; + bt_mesh_atomic_val_t ret; + + key = bt_mesh_irq_lock(); + + ret = *target; + (*target)--; + + bt_mesh_irq_unlock(key); + + return ret; +} + +/** + * + * @brief Atomic increment primitive + * + * @param target memory location to increment + * + * This routine provides the atomic increment operator. The value at + * is atomically incremented by 1, and the old value from is returned. + * + * @return The value from before the increment + */ +bt_mesh_atomic_val_t bt_mesh_atomic_inc(bt_mesh_atomic_t *target) +{ + unsigned int key; + bt_mesh_atomic_val_t ret; + + key = bt_mesh_irq_lock(); + + ret = *target; + (*target)++; + + bt_mesh_irq_unlock(key); + + return ret; +} + +#endif /* #ifndef CONFIG_ATOMIC_OPERATIONS_BUILTIN */ diff --git a/components/bt/ble_mesh/mesh_core/mesh_bearer_adapt.c b/components/bt/ble_mesh/mesh_core/mesh_bearer_adapt.c new file mode 100644 index 0000000000..67bea67e03 --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/mesh_bearer_adapt.c @@ -0,0 +1,1858 @@ +/* + * Copyright (c) 2017 Nordic Semiconductor ASA + * Copyright (c) 2015-2016 Intel Corporation + * Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "sdkconfig.h" + +#include "bta/bta_api.h" +#include "bta/bta_gatt_api.h" +#include "bta/bta_gatt_common.h" +#include "bta_gattc_int.h" +#include "stack/btm_ble_api.h" +#include "p_256_ecc_pp.h" +#include "stack/hcimsgs.h" +#include "osi/future.h" +#include "osi/allocator.h" + +#include "mbedtls/aes.h" + +#include "mesh_hci.h" +#include "mesh_aes_encrypt.h" +#include "mesh_bearer_adapt.h" +#include "mesh_trace.h" +#include "mesh_buf.h" +#include "mesh_atomic.h" + +#include "provisioner_prov.h" +#include "mesh_common.h" + +#define BLE_MESH_BTM_CHECK_STATUS(func) do { \ + tBTM_STATUS __status = (func); \ + if ((__status != BTM_SUCCESS) && (__status != BTM_CMD_STARTED)) { \ + BT_ERR("%s, Invalid status %d", __func__, __status); \ + return -1; \ + } \ + } while(0); + +#define BLE_MESH_GATT_GET_CONN_ID(conn_id) (((u16_t)(conn_id)) >> 8) +#define BLE_MESH_GATT_CREATE_CONN_ID(gatt_if, conn_id) ((u16_t)((((u8_t)(conn_id)) << 8) | ((u8_t)(gatt_if)))) + +/* We don't need to manage the BLE_MESH_DEV_ADVERTISING flags in the version of bluedriod, + * it will manage it in the BTM layer. + */ +#define BLE_MESH_DEV 0 + +/* P-256 Variables */ +static u8_t bt_mesh_public_key[64]; +static BT_OCTET32 bt_mesh_private_key = { + 0x3f, 0x49, 0xf6, 0xd4, 0xa3, 0xc5, 0x5f, 0x38, + 0x74, 0xc9, 0xb3, 0xe3, 0xd2, 0x10, 0x3f, 0x50, + 0x4a, 0xff, 0x60, 0x7b, 0xeb, 0x40, 0xb7, 0x99, + 0x58, 0x99, 0xb8, 0xa6, 0xcd, 0x3c, 0x1a, 0xbd +}; + +/* Scan related functions */ +static bt_mesh_scan_cb_t *bt_mesh_scan_dev_found_cb; +static void bt_mesh_scan_result_callback(tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH *p_data); + +#if defined(CONFIG_BLE_MESH_NODE) && CONFIG_BLE_MESH_NODE +/* the gatt database list to save the attribute table */ +static sys_slist_t bt_mesh_gatts_db; + +/* Static Variables */ +static struct bt_mesh_conn bt_mesh_gatts_conn[BLE_MESH_MAX_CONN]; +static struct bt_mesh_conn_cb *bt_mesh_gatts_conn_cb; +static tBTA_GATTS_IF bt_mesh_gatts_if; +static BD_ADDR bt_mesh_gatts_addr; +static u16_t svc_handle, char_handle; +static future_t *future_mesh; + +/* Static Functions */ +static struct bt_mesh_gatt_attr *bt_mesh_gatts_find_attr_by_handle(u16_t handle); +#endif /* defined(CONFIG_BLE_MESH_NODE) && CONFIG_BLE_MESH_NODE */ + +#if defined(CONFIG_BLE_MESH_PROVISIONER) && CONFIG_BLE_MESH_PROVISIONER +#define BLE_MESH_GATTC_APP_UUID_BYTE 0x97 +static struct gattc_prov_info { + /* Service to be found depends on the type of adv pkt received */ + struct bt_mesh_conn conn; + BD_ADDR addr; + u8_t addr_type; + u16_t service_uuid; + u16_t mtu; + bool wr_desc_done; /* Indicate if write char descriptor event is received */ + u16_t start_handle; /* Service attribute start handle */ + u16_t end_handle; /* Service attribute end handle */ + u16_t data_in_handle; /* Data In Characteristic attribute handle */ + u16_t data_out_handle; /* Data Out Characteristic attribute handle */ + u16_t ccc_handle; /* Data Out Characteristic CCC attribute handle */ +} bt_mesh_gattc_info[BLE_MESH_MAX_CONN]; +static struct bt_mesh_prov_conn_cb *bt_mesh_gattc_conn_cb; +static tBTA_GATTC_IF bt_mesh_gattc_if; +#endif /* defined(CONFIG_BLE_MESH_PROVISIONER) && CONFIG_BLE_MESH_PROVISIONER */ + +static void bt_mesh_scan_results_change_2_bta(tBTM_INQ_RESULTS *p_inq, u8_t *p_eir, + tBTA_DM_SEARCH_CBACK *p_scan_cback) +{ + tBTM_INQ_INFO *p_inq_info; + tBTA_DM_SEARCH result; + + bdcpy(result.inq_res.bd_addr, p_inq->remote_bd_addr); + result.inq_res.rssi = p_inq->rssi; + result.inq_res.ble_addr_type = p_inq->ble_addr_type; + result.inq_res.inq_result_type = p_inq->inq_result_type; + result.inq_res.device_type = p_inq->device_type; + result.inq_res.flag = p_inq->flag; + result.inq_res.adv_data_len = p_inq->adv_data_len; + result.inq_res.scan_rsp_len = p_inq->scan_rsp_len; + memcpy(result.inq_res.dev_class, p_inq->dev_class, sizeof(DEV_CLASS)); + result.inq_res.ble_evt_type = p_inq->ble_evt_type; + + /* application will parse EIR to find out remote device name */ + result.inq_res.p_eir = p_eir; + + if ((p_inq_info = BTM_InqDbRead(p_inq->remote_bd_addr)) != NULL) { + /* initialize remt_name_not_required to FALSE so that we get the name by default */ + result.inq_res.remt_name_not_required = FALSE; + } + + if (p_scan_cback) { + p_scan_cback(BTA_DM_INQ_RES_EVT, &result); + } + + if (p_inq_info) { + /* application indicates if it knows the remote name, inside the callback + copy that to the inquiry data base*/ + if (result.inq_res.remt_name_not_required) { + p_inq_info->appl_knows_rem_name = TRUE; + } + } +} + +static void bt_mesh_scan_results_cb(tBTM_INQ_RESULTS *p_inq, u8_t *p_eir) +{ + bt_mesh_scan_results_change_2_bta(p_inq, p_eir, bt_mesh_scan_result_callback); +} + +static bool valid_adv_param(const struct bt_mesh_adv_param *param) +{ + if (!(param->options & BLE_MESH_ADV_OPT_CONNECTABLE)) { +#if BLE_MESH_DEV + if (bt_mesh_dev.hci_version < BLE_MESH_HCI_VERSION_5_0 && + param->interval_min < 0x00a0) { + return false; + } +#endif + } + + if (param->interval_min > param->interval_max || + param->interval_min < 0x0020 || param->interval_max > 0x4000) { + return false; + } + + return true; +} + +static int set_adv_data(u16_t hci_op, const struct bt_mesh_adv_data *ad, size_t ad_len) +{ + struct bt_mesh_hci_cp_set_adv_data param = {0}; + int i; + + if (ad == NULL || ad_len == 0) { + return 0; + } + + for (i = 0; i < ad_len; i++) { + /* Check if ad fit in the remaining buffer */ + if (param.len + ad[i].data_len + 2 > 31) { + return -EINVAL; + } + + param.data[param.len++] = ad[i].data_len + 1; + param.data[param.len++] = ad[i].type; + + memcpy(¶m.data[param.len], ad[i].data, ad[i].data_len); + param.len += ad[i].data_len; + } + + /* Set adv data and scan rsp data. */ + if (hci_op == BLE_MESH_HCI_OP_SET_ADV_DATA) { + BLE_MESH_BTM_CHECK_STATUS(BTM_BleWriteAdvDataRaw(param.data, param.len)); + } else if (hci_op == BLE_MESH_HCI_OP_SET_SCAN_RSP_DATA) { + BLE_MESH_BTM_CHECK_STATUS(BTM_BleWriteScanRspRaw(param.data, param.len)); + } + + return 0; +} + +static void start_adv_completed_cb(u8_t status) +{ +#if BLE_MESH_DEV + if (!status) { + bt_mesh_atomic_set_bit(bt_mesh_dev.flags, BLE_MESH_DEV_ADVERTISING); + } +#endif +} + +static bool valid_scan_param(const struct bt_mesh_scan_param *param) +{ + if (param->type != BLE_MESH_SCAN_PASSIVE && + param->type != BLE_MESH_SCAN_ACTIVE) { + return false; + } + + if (param->filter_dup != BLE_MESH_SCAN_FILTER_DUP_DISABLE && + param->filter_dup != BLE_MESH_SCAN_FILTER_DUP_ENABLE) { + return false; + } + + if (param->interval < 0x0004 || param->interval > 0x4000) { + return false; + } + + if (param->window < 0x0004 || param->window > 0x4000) { + return false; + } + + if (param->window > param->interval) { + return false; + } + + return true; +} + +static int start_le_scan(u8_t scan_type, u16_t interval, u16_t window, u8_t filter_dup) +{ + UINT8 scan_fil_policy = BLE_MESH_SP_ADV_ALL; /* No whitelist for BLE Mesh */ + UINT8 addr_type_own = BLE_MESH_ADDR_PUBLIC; /* Currently only support Public Address */ + tGATT_IF client_if = 0xFF; /* Default GATT interface id */ + + BLE_MESH_BTM_CHECK_STATUS( + BTM_BleSetScanFilterParams(client_if, interval, window, scan_type, addr_type_own, + filter_dup, scan_fil_policy, NULL)); + + /* BLE Mesh scan permanently, so no duration of scan here */ + BLE_MESH_BTM_CHECK_STATUS(BTM_BleScan(true, 0, bt_mesh_scan_results_cb, NULL, NULL)); + +#if BLE_MESH_DEV + if (scan_type == BLE_MESH_SCAN_ACTIVE) { + bt_mesh_atomic_set_bit(bt_mesh_dev.flags, BLE_MESH_DEV_ACTIVE_SCAN); + } else { + bt_mesh_atomic_clear_bit(bt_mesh_dev.flags, BLE_MESH_DEV_ACTIVE_SCAN); + } +#endif + + return 0; +} + +static void bt_mesh_scan_result_callback(tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH *p_data) +{ + bt_mesh_addr_t addr = {0}; + UINT8 adv_type; + UINT8 rssi; + + BT_DBG("%s, event = %d", __func__, event); + + if (event == BTA_DM_INQ_RES_EVT) { + /* TODO: How to process scan response here? */ + addr.type = p_data->inq_res.ble_addr_type; + memcpy(addr.val, p_data->inq_res.bd_addr, BLE_MESH_ADDR_LEN); + rssi = p_data->inq_res.rssi; + adv_type = p_data->inq_res.ble_evt_type; + + /* scan rsp len: p_data->inq_res.scan_rsp_len */ + struct net_buf_simple *buf = bt_mesh_alloc_buf(p_data->inq_res.adv_data_len); + if (!buf) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + net_buf_simple_add_mem(buf, p_data->inq_res.p_eir, p_data->inq_res.adv_data_len); + + if (bt_mesh_scan_dev_found_cb != NULL) { + bt_mesh_scan_dev_found_cb(&addr, rssi, adv_type, buf); + } + osi_free(buf); + } else if (event == BTA_DM_INQ_CMPL_EVT) { + BT_INFO("%s, Scan completed, number of scan response %d", __func__, p_data->inq_cmpl.num_resps); + } else { + BT_WARN("%s, Unexpected event 0x%x", __func__, event); + } +} + +/* APIs functions */ +int bt_le_adv_start(const struct bt_mesh_adv_param *param, + const struct bt_mesh_adv_data *ad, size_t ad_len, + const struct bt_mesh_adv_data *sd, size_t sd_len) +{ + tBTA_START_ADV_CMPL_CBACK *p_start_adv_cb; + tBTM_BLE_ADV_CHNL_MAP channel_map; + tBLE_ADDR_TYPE addr_type_own; + tBLE_BD_ADDR p_dir_bda = {0}; + tBTM_BLE_AFP adv_fil_pol; + UINT8 adv_type; + int err; + +#if BLE_MESH_DEV + if (bt_mesh_atomic_test_bit(bt_mesh_dev.flags, BLE_MESH_DEV_ADVERTISING)) { + return -EALREADY; + } +#endif + + if (!valid_adv_param(param)) { + BT_ERR("%s, Invalid adv parameters", __func__); + return -EINVAL; + } + + err = set_adv_data(BLE_MESH_HCI_OP_SET_ADV_DATA, ad, ad_len); + if (err) { + BT_ERR("%s, Failed to set adv data", __func__); + return err; + } + + /* + * We need to set SCAN_RSP when enabling advertising type that allows + * for Scan Requests. + * + * If sd was not provided but we enable connectable undirected + * advertising sd needs to be cleared from values set by previous calls. + * Clearing sd is done by calling set_adv_data() with NULL data and zero len. + * So following condition check is unusual but correct. + */ + if (sd && (param->options & BLE_MESH_ADV_OPT_CONNECTABLE)) { + err = set_adv_data(BLE_MESH_HCI_OP_SET_SCAN_RSP_DATA, sd, sd_len); + if (err) { + BT_ERR("%s, Failed to set scan rsp data", __func__); + return err; + } + } + + if (param->options & BLE_MESH_ADV_OPT_CONNECTABLE) { + adv_type = BLE_MESH_ADV_IND; + } else if (sd != NULL) { + adv_type = BLE_MESH_ADV_SCAN_IND; + } else { + adv_type = BLE_MESH_ADV_NONCONN_IND; + } + addr_type_own = BLE_MESH_ADDR_PUBLIC; /* Currently only support Public Address */ + channel_map = BLE_MESH_ADV_CHNL_37 | BLE_MESH_ADV_CHNL_38 | BLE_MESH_ADV_CHNL_39; + adv_fil_pol = BLE_MESH_AP_SCAN_CONN_ALL; + p_start_adv_cb = start_adv_completed_cb; + + /* Check if we can start adv using BTM_BleSetAdvParamsStartAdvCheck */ + BLE_MESH_BTM_CHECK_STATUS( + BTM_BleSetAdvParamsAll(param->interval_min, param->interval_max, adv_type, + addr_type_own, &p_dir_bda, + channel_map, adv_fil_pol, p_start_adv_cb)); + BLE_MESH_BTM_CHECK_STATUS(BTM_BleStartAdv()); + +#if BLE_MESH_DEV + bt_mesh_atomic_set_bit(bt_mesh_dev.flags, BLE_MESH_DEV_ADVERTISING); + + if (!(param->options & BLE_MESH_ADV_OPT_ONE_TIME)) { + bt_mesh_atomic_set_bit(bt_mesh_dev.flags, BLE_MESH_DEV_KEEP_ADVERTISING); + } +#endif + + return 0; +} + +int bt_le_adv_stop(void) +{ +#if BLE_MESH_DEV + bt_mesh_atomic_clear_bit(bt_mesh_dev.flags, BLE_MESH_DEV_KEEP_ADVERTISING); + if (!bt_mesh_atomic_test_bit(bt_mesh_dev.flags, BLE_MESH_DEV_ADVERTISING)) { + return 0; + } +#endif + + BLE_MESH_BTM_CHECK_STATUS(BTM_BleBroadcast(false, NULL)); + +#if BLE_MESH_DEV + bt_mesh_atomic_clear_bit(bt_mesh_dev.flags, BLE_MESH_DEV_ADVERTISING); +#endif + + return 0; +} + +int bt_le_scan_start(const struct bt_mesh_scan_param *param, bt_mesh_scan_cb_t cb) +{ + int err; + +#if BLE_MESH_DEV + if (bt_mesh_atomic_test_bit(bt_mesh_dev.flags, BLE_MESH_DEV_SCANNING)) { + return -EALREADY; + } +#endif + + if (!valid_scan_param(param)) { + return -EINVAL; + } + +#if BLE_MESH_DEV + if (param->filter_dup) { + bt_mesh_atomic_set_bit(bt_mesh_dev.flags, BLE_MESH_DEV_SCAN_FILTER_DUP); + } else { + bt_mesh_atomic_clear_bit(bt_mesh_dev.flags, BLE_MESH_DEV_SCAN_FILTER_DUP); + } +#endif + + err = start_le_scan(param->type, param->interval, param->window, param->filter_dup); + if (err) { + return err; + } + +#if BLE_MESH_DEV + bt_mesh_atomic_set_bit(bt_mesh_dev.flags, BLE_MESH_DEV_SCANNING); +#endif + + bt_mesh_scan_dev_found_cb = cb; + return err; +} + +int bt_le_scan_stop(void) +{ +#if BLE_MESH_DEV + if (bt_mesh_atomic_test_bit(bt_mesh_dev.flags, BLE_MESH_DEV_SCANNING)) { + bt_mesh_atomic_clear_bit(bt_mesh_dev.flags, BLE_MESH_DEV_SCANNING); + BLE_MESH_BTM_CHECK_STATUS(BTM_BleScan(false, 0, NULL, NULL, NULL)); + } +#else + BLE_MESH_BTM_CHECK_STATUS(BTM_BleScan(false, 0, NULL, NULL, NULL)); +#endif + + bt_mesh_scan_dev_found_cb = NULL; + return 0; +} + +#if defined(CONFIG_BLE_MESH_NODE) && CONFIG_BLE_MESH_NODE +static void bt_mesh_bta_gatts_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data) +{ + switch (event) { + case BTA_GATTS_REG_EVT: + if (p_data->reg_oper.status == BTA_GATT_OK) { + bt_mesh_gatts_if = p_data->reg_oper.server_if; + } + break; + case BTA_GATTS_READ_EVT: { + struct bt_mesh_gatt_attr *attr = bt_mesh_gatts_find_attr_by_handle(p_data->req_data.p_data->read_req.handle); + u8_t index = BLE_MESH_GATT_GET_CONN_ID(p_data->req_data.conn_id); + tBTA_GATTS_RSP rsp; + u8_t buf[100] = {0}; + u16_t len = 0; + + BT_DBG("%s, read: handle = %d", __func__, p_data->req_data.p_data->read_req.handle); + + if (attr != NULL && attr->read != NULL) { + if ((len = attr->read(&bt_mesh_gatts_conn[index], attr, buf, 100, + p_data->req_data.p_data->read_req.offset)) > 0) { + rsp.attr_value.handle = p_data->req_data.p_data->read_req.handle; + rsp.attr_value.len = len; + memcpy(&rsp.attr_value.value[0], buf, len); + BTA_GATTS_SendRsp(p_data->req_data.conn_id, p_data->req_data.trans_id, + p_data->req_data.status, &rsp); + BT_DBG("%s, Send gatts read response, handle = %x", __func__, attr->handle); + } else { + BT_WARN("%s, BLE Mesh gatts read failed", __func__); + } + } + break; + } + case BTA_GATTS_WRITE_EVT: { + struct bt_mesh_gatt_attr *attr = bt_mesh_gatts_find_attr_by_handle(p_data->req_data.p_data->write_req.handle); + u8_t index = BLE_MESH_GATT_GET_CONN_ID(p_data->req_data.conn_id); + u16_t len = 0; + + BT_DBG("%s, write: handle = %d, len = %d, data = %s", __func__, p_data->req_data.p_data->write_req.handle, + p_data->req_data.p_data->write_req.len, + bt_hex(p_data->req_data.p_data->write_req.value, p_data->req_data.p_data->write_req.len)); + + if (attr != NULL && attr->write != NULL) { + if ((len = attr->write(&bt_mesh_gatts_conn[index], attr, + p_data->req_data.p_data->write_req.value, + p_data->req_data.p_data->write_req.len, + p_data->req_data.p_data->write_req.offset, 0)) > 0) { + if (p_data->req_data.p_data->write_req.need_rsp) { + BTA_GATTS_SendRsp(p_data->req_data.conn_id, p_data->req_data.trans_id, + p_data->req_data.status, NULL); + BT_DBG("%s, send mesh write rsp, handle = %x", __func__, attr->handle); + } + } + } + break; + } + case BTA_GATTS_EXEC_WRITE_EVT: + break; + case BTA_GATTS_MTU_EVT: + break; + case BTA_GATTS_CONF_EVT: + break; + case BTA_GATTS_CREATE_EVT: + svc_handle = p_data->create.service_id; + BT_DBG("%s, svc_handle = %d, future_mesh = %p", __func__, svc_handle, future_mesh); + if (future_mesh != NULL) { + future_ready(future_mesh, FUTURE_SUCCESS); + } + break; + case BTA_GATTS_ADD_INCL_SRVC_EVT: + svc_handle = p_data->add_result.attr_id; + if (future_mesh != NULL) { + future_ready(future_mesh, FUTURE_SUCCESS); + } + break; + case BTA_GATTS_ADD_CHAR_EVT: + char_handle = p_data->add_result.attr_id; + if (future_mesh != NULL) { + future_ready(future_mesh, FUTURE_SUCCESS); + } + break; + case BTA_GATTS_ADD_CHAR_DESCR_EVT: + char_handle = p_data->add_result.attr_id; + if (future_mesh != NULL) { + future_ready(future_mesh, FUTURE_SUCCESS); + } + break; + case BTA_GATTS_DELELTE_EVT: + break; + case BTA_GATTS_START_EVT: + break; + case BTA_GATTS_STOP_EVT: + break; + case BTA_GATTS_CONNECT_EVT: +#if BLE_MESH_DEV + /* When connection is created, advertising will be stopped automatically. */ + bt_mesh_atomic_test_and_clear_bit(bt_mesh_dev.flags, BLE_MESH_DEV_ADVERTISING); +#endif + if (bt_mesh_gatts_conn_cb != NULL && bt_mesh_gatts_conn_cb->connected != NULL) { + u8_t index = BLE_MESH_GATT_GET_CONN_ID(p_data->conn.conn_id); + if (index < BLE_MESH_MAX_CONN) { + bt_mesh_gatts_conn[index].handle = BLE_MESH_GATT_GET_CONN_ID(p_data->conn.conn_id); + (bt_mesh_gatts_conn_cb->connected)(&bt_mesh_gatts_conn[index], 0); + } + memcpy(bt_mesh_gatts_addr, p_data->conn.remote_bda, BLE_MESH_ADDR_LEN); + /* This is for EspBleMesh Android app. When it tries to connect with the + * device at the first time and it fails due to some reason. And after + * the second connection, the device needs to send GATT service change + * indication to the phone manually to notify it dicovering service again. + */ + BTA_GATTS_SendServiceChangeIndication(bt_mesh_gatts_if, bt_mesh_gatts_addr); + } + break; + case BTA_GATTS_DISCONNECT_EVT: +#if BLE_MESH_DEV + bt_mesh_atomic_test_and_clear_bit(bt_mesh_dev.flags, BLE_MESH_DEV_ADVERTISING); +#endif + if (bt_mesh_gatts_conn_cb != NULL && bt_mesh_gatts_conn_cb->disconnected != NULL) { + u8_t index = BLE_MESH_GATT_GET_CONN_ID(p_data->conn.conn_id); + if (index < BLE_MESH_MAX_CONN) { + bt_mesh_gatts_conn[index].handle = BLE_MESH_GATT_GET_CONN_ID(p_data->conn.conn_id); + (bt_mesh_gatts_conn_cb->disconnected)(&bt_mesh_gatts_conn[index], p_data->conn.reason); + } + memset(bt_mesh_gatts_addr, 0x0, BLE_MESH_ADDR_LEN); + } + break; + case BTA_GATTS_CLOSE_EVT: + break; + default: + break; + } +} + +void bt_mesh_gatts_conn_cb_register(struct bt_mesh_conn_cb *cb) +{ + bt_mesh_gatts_conn_cb = cb; +} + +static struct bt_mesh_gatt_attr *bt_mesh_gatts_find_attr_by_handle(u16_t handle) +{ + struct bt_mesh_gatt_service *svc = NULL; + struct bt_mesh_gatt_attr *attr = NULL; + + SYS_SLIST_FOR_EACH_CONTAINER(&bt_mesh_gatts_db, svc, node) { + int i; + + for (i = 0; i < svc->attr_count; i++) { + attr = &svc->attrs[i]; + /* Check the attrs handle is equal to the handle or not */ + if (attr->handle == handle) { + return attr; + } + } + } + + return NULL; +} + +static void bt_mesh_gatts_foreach_attr(u16_t start_handle, u16_t end_handle, + bt_mesh_gatt_attr_func_t func, void *user_data) +{ + struct bt_mesh_gatt_service *svc = NULL; + + SYS_SLIST_FOR_EACH_CONTAINER(&bt_mesh_gatts_db, svc, node) { + int i; + + for (i = 0; i < svc->attr_count; i++) { + struct bt_mesh_gatt_attr *attr = &svc->attrs[i]; + + /* Check if attribute handle is within range */ + if (attr->handle < start_handle || + attr->handle > end_handle) { + continue; + } + + if (func(attr, user_data) == BLE_MESH_GATT_ITER_STOP) { + return; + } + } + } +} + +static u8_t find_next(const struct bt_mesh_gatt_attr *attr, void *user_data) +{ + struct bt_mesh_gatt_attr **next = user_data; + + *next = (struct bt_mesh_gatt_attr *)attr; + + return BLE_MESH_GATT_ITER_STOP; +} + +static struct bt_mesh_gatt_attr *bt_mesh_gatts_attr_next(const struct bt_mesh_gatt_attr *attr) +{ + struct bt_mesh_gatt_attr *next = NULL; + + bt_mesh_gatts_foreach_attr(attr->handle + 1, attr->handle + 1, find_next, &next); + + return next; +} + +ssize_t bt_mesh_gatts_attr_read(struct bt_mesh_conn *conn, const struct bt_mesh_gatt_attr *attr, + void *buf, u16_t buf_len, u16_t offset, + const void *value, u16_t value_len) +{ + u16_t len; + + if (offset > value_len) { + return BLE_MESH_GATT_ERR(BLE_MESH_ATT_ERR_INVALID_OFFSET); + } + + len = MIN(buf_len, value_len - offset); + + BT_DBG("handle 0x%04x offset %u length %u", attr->handle, offset, len); + + memcpy(buf, value + offset, len); + + return len; +} + +struct gatts_incl { + u16_t start_handle; + u16_t end_handle; + u16_t uuid16; +} __packed; + +ssize_t bt_mesh_gatts_attr_read_included(struct bt_mesh_conn *conn, + const struct bt_mesh_gatt_attr *attr, + void *buf, u16_t len, u16_t offset) +{ + struct bt_mesh_gatt_attr *incl = attr->user_data; + struct bt_mesh_uuid *uuid = incl->user_data; + struct gatts_incl pdu = {0}; + u8_t value_len; + + /* First attr points to the start handle */ + pdu.start_handle = sys_cpu_to_le16(incl->handle); + value_len = sizeof(pdu.start_handle) + sizeof(pdu.end_handle); + + /* + * Core 4.2, Vol 3, Part G, 3.2, + * The Service UUID shall only be present when the UUID is a 16-bit Bluetooth UUID. + */ + if (uuid->type == BLE_MESH_UUID_TYPE_16) { + pdu.uuid16 = sys_cpu_to_le16(BLE_MESH_UUID_16(uuid)->val); + value_len += sizeof(pdu.uuid16); + } + + return bt_mesh_gatts_attr_read(conn, attr, buf, len, offset, &pdu, value_len); +} + +ssize_t bt_mesh_gatts_attr_read_service(struct bt_mesh_conn *conn, + const struct bt_mesh_gatt_attr *attr, + void *buf, u16_t len, u16_t offset) +{ + struct bt_mesh_uuid *uuid = attr->user_data; + + if (uuid->type == BLE_MESH_UUID_TYPE_16) { + u16_t uuid16 = sys_cpu_to_le16(BLE_MESH_UUID_16(uuid)->val); + + return bt_mesh_gatts_attr_read(conn, attr, buf, len, offset, &uuid16, 2); + } + + return bt_mesh_gatts_attr_read(conn, attr, buf, len, offset, + BLE_MESH_UUID_128(uuid)->val, 16); +} + +struct gatts_chrc { + u8_t properties; + u16_t value_handle; + union { + u16_t uuid16; + u8_t uuid[16]; + }; +} __packed; + +ssize_t bt_mesh_gatts_attr_read_chrc(struct bt_mesh_conn *conn, + const struct bt_mesh_gatt_attr *attr, void *buf, + u16_t len, u16_t offset) +{ + struct bt_mesh_gatt_char *chrc = attr->user_data; + const struct bt_mesh_gatt_attr *next = NULL; + struct gatts_chrc pdu = {0}; + u8_t value_len; + + pdu.properties = chrc->properties; + /* BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part G] page 534: + * 3.3.2 Characteristic Value Declaration + * The Characteristic Value declaration contains the value of the + * characteristic. It is the first Attribute after the characteristic + * declaration. All characteristic definitions shall have a + * Characteristic Value declaration. + */ + next = bt_mesh_gatts_attr_next(attr); + if (!next) { + BT_WARN("%s, No value for characteristic at 0x%04x", __func__, attr->handle); + pdu.value_handle = 0x0000; + } else { + pdu.value_handle = sys_cpu_to_le16(next->handle); + } + value_len = sizeof(pdu.properties) + sizeof(pdu.value_handle); + + if (chrc->uuid->type == BLE_MESH_UUID_TYPE_16) { + pdu.uuid16 = sys_cpu_to_le16(BLE_MESH_UUID_16(chrc->uuid)->val); + value_len += 2; + } else { + memcpy(pdu.uuid, BLE_MESH_UUID_128(chrc->uuid)->val, 16); + value_len += 16; + } + + return bt_mesh_gatts_attr_read(conn, attr, buf, len, offset, &pdu, value_len); +} + +static void bta_uuid_to_bt_mesh_uuid(tBT_UUID *bta_uuid, const struct bt_mesh_uuid *uuid) +{ + assert(uuid != NULL && bta_uuid != NULL); + + if (uuid->type == BLE_MESH_UUID_TYPE_16) { + bta_uuid->len = LEN_UUID_16; + bta_uuid->uu.uuid16 = BLE_MESH_UUID_16(uuid)->val; + } else if (uuid->type == BLE_MESH_UUID_TYPE_32) { + bta_uuid->len = LEN_UUID_32; + bta_uuid->uu.uuid32 = BLE_MESH_UUID_32(uuid)->val; + } else if (uuid->type == BLE_MESH_UUID_TYPE_128) { + bta_uuid->len = LEN_UUID_128; + memcpy(bta_uuid->uu.uuid128, BLE_MESH_UUID_128(uuid)->val, LEN_UUID_128); + } else { + BT_ERR("%s, Invalid mesh uuid type = %d", __func__, uuid->type); + } + + return; +} + +static int gatts_register(struct bt_mesh_gatt_service *svc) +{ + struct bt_mesh_gatt_service *last; + u16_t handle; + + if (sys_slist_is_empty(&bt_mesh_gatts_db)) { + handle = 0; + goto populate; + } + + last = SYS_SLIST_PEEK_TAIL_CONTAINER(&bt_mesh_gatts_db, last, node); + handle = last->attrs[last->attr_count - 1].handle; + BT_DBG("%s, handle = %d", __func__, handle); + +populate: + sys_slist_append(&bt_mesh_gatts_db, &svc->node); + return 0; +} + +static tBTA_GATT_PERM bt_mesh_perm_to_bta_perm(u8_t perm) +{ + tBTA_GATT_PERM bta_perm = 0; + + if ((perm & BLE_MESH_GATT_PERM_READ) == BLE_MESH_GATT_PERM_READ) { + bta_perm |= BTA_GATT_PERM_READ; + } + + if ((perm & BLE_MESH_GATT_PERM_WRITE) == BLE_MESH_GATT_PERM_WRITE) { + bta_perm |= BTA_GATT_PERM_WRITE; + } + + if ((perm & BLE_MESH_GATT_PERM_READ_ENCRYPT) == BLE_MESH_GATT_PERM_READ_ENCRYPT) { + bta_perm |= BTA_GATT_PERM_READ_ENCRYPTED; + } + + if ((perm & BLE_MESH_GATT_PERM_WRITE_ENCRYPT) == BLE_MESH_GATT_PERM_WRITE_ENCRYPT) { + bta_perm |= BTA_GATT_PERM_WRITE_ENCRYPTED; + } + + if ((perm & BLE_MESH_GATT_PERM_READ_AUTHEN) == BLE_MESH_GATT_PERM_READ_AUTHEN) { + bta_perm |= BTA_GATT_PERM_READ_ENC_MITM; + } + + if ((perm & BLE_MESH_GATT_PERM_WRITE_AUTHEN) == BLE_MESH_GATT_PERM_WRITE_AUTHEN) { + bta_perm |= BTA_GATT_PERM_WRITE_ENC_MITM; + } + + return bta_perm; +} + +int bt_mesh_gatts_service_register(struct bt_mesh_gatt_service *svc) +{ + tBT_UUID bta_uuid = {0}; + + assert(svc != NULL); + + for (int i = 0; i < svc->attr_count; i++) { + if (svc->attrs[i].uuid->type == BLE_MESH_UUID_TYPE_16) { + switch (BLE_MESH_UUID_16(svc->attrs[i].uuid)->val) { + case BLE_MESH_UUID_GATT_PRIMARY_VAL: { + future_mesh = future_new(); + bta_uuid_to_bt_mesh_uuid(&bta_uuid, (struct bt_mesh_uuid *)svc->attrs[i].user_data); + BTA_GATTS_CreateService(bt_mesh_gatts_if, + &bta_uuid, 0, svc->attr_count, true); + if (future_await(future_mesh) == FUTURE_FAIL) { + BT_ERR("%s, Failed to add primary service", __func__); + return ESP_FAIL; + } + svc->attrs[i].handle = svc_handle; + BT_DBG("Add primary service: svc_uuid = %x, perm = %d, svc_handle = %d", bta_uuid.uu.uuid16, svc->attrs[i].perm, svc_handle); + break; + } + case BLE_MESH_UUID_GATT_SECONDARY_VAL: { + future_mesh = future_new(); + bta_uuid_to_bt_mesh_uuid(&bta_uuid, (struct bt_mesh_uuid *)svc->attrs[i].user_data); + BTA_GATTS_CreateService(bt_mesh_gatts_if, + &bta_uuid, 0, svc->attr_count, false); + if (future_await(future_mesh) == FUTURE_FAIL) { + BT_ERR("%s, Failed to add secondary service", __func__); + return ESP_FAIL; + } + svc->attrs[i].handle = svc_handle; + BT_DBG("Add secondary service: svc_uuid = %x, perm = %d, svc_handle = %d", bta_uuid.uu.uuid16, svc->attrs[i].perm, svc_handle); + break; + } + case BLE_MESH_UUID_GATT_INCLUDE_VAL: { + break; + } + case BLE_MESH_UUID_GATT_CHRC_VAL: { + future_mesh = future_new(); + struct bt_mesh_gatt_char *gatts_chrc = (struct bt_mesh_gatt_char *)svc->attrs[i].user_data; + bta_uuid_to_bt_mesh_uuid(&bta_uuid, gatts_chrc->uuid); + BTA_GATTS_AddCharacteristic(svc_handle, &bta_uuid, bt_mesh_perm_to_bta_perm(svc->attrs[i + 1].perm), gatts_chrc->properties, NULL, NULL); + if (future_await(future_mesh) == FUTURE_FAIL) { + BT_ERR("%s, Failed to add characristic", __func__); + return ESP_FAIL; + } + /* All the characristic should have two handle: the declaration handle and the value handle */ + svc->attrs[i].handle = char_handle - 1; + svc->attrs[i + 1].handle = char_handle; + BT_DBG("Add characteristic: char_uuid = %x, char_handle = %d, perm = %d, char_pro = %d", BLE_MESH_UUID_16(gatts_chrc->uuid)->val, char_handle, svc->attrs[i + 1].perm, gatts_chrc->properties); + break; + } + case BLE_MESH_UUID_GATT_CEP_VAL: + case BLE_MESH_UUID_GATT_CUD_VAL: + case BLE_MESH_UUID_GATT_CCC_VAL: + case BLE_MESH_UUID_GATT_SCC_VAL: + case BLE_MESH_UUID_GATT_CPF_VAL: + case BLE_MESH_UUID_VALID_RANGE_VAL: + case BLE_MESH_UUID_HIDS_EXT_REPORT_VAL: + case BLE_MESH_UUID_HIDS_REPORT_REF_VAL: + case BLE_MESH_UUID_ES_CONFIGURATION_VAL: + case BLE_MESH_UUID_ES_MEASUREMENT_VAL: + case BLE_MESH_UUID_ES_TRIGGER_SETTING_VAL: { + future_mesh = future_new(); + bta_uuid_to_bt_mesh_uuid(&bta_uuid, svc->attrs[i].uuid); + BTA_GATTS_AddCharDescriptor(svc_handle, bt_mesh_perm_to_bta_perm(svc->attrs[i].perm), &bta_uuid, NULL, NULL); + if (future_await(future_mesh) == FUTURE_FAIL) { + BT_ERR("%s, Failed to add descriptor", __func__); + return ESP_FAIL; + } + svc->attrs[i].handle = char_handle; + BT_DBG("Add descriptor: descr_uuid = %x, perm= %d, descr_handle = %d", BLE_MESH_UUID_16(svc->attrs[i].uuid)->val, svc->attrs[i].perm, char_handle); + break; + } + default: + break; + } + } + } + + if (svc_handle != 0) { + svc_handle = 0; + } + + gatts_register(svc); + return 0; +} + +int bt_mesh_gatts_disconnect(struct bt_mesh_conn *conn, u8_t reason) +{ + UNUSED(reason); + u16_t conn_id = BLE_MESH_GATT_CREATE_CONN_ID(bt_mesh_gatts_if, conn->handle); + BTA_GATTS_Close(conn_id); + return 0; +} + +int bt_mesh_gatts_service_unregister(struct bt_mesh_gatt_service *svc) +{ + assert(svc != NULL); + + BTA_GATTS_DeleteService(svc->attrs[0].handle); + return 0; +} + +int bt_mesh_gatts_notify(struct bt_mesh_conn *conn, const struct bt_mesh_gatt_attr *attr, + const void *data, u16_t len) +{ + u16_t conn_id = BLE_MESH_GATT_CREATE_CONN_ID(bt_mesh_gatts_if, conn->handle); + BTA_GATTS_HandleValueIndication(conn_id, attr->handle, len, (u8_t *)data, false); + return 0; +} + +u16_t bt_mesh_gatt_get_mtu(struct bt_mesh_conn *conn) +{ + return BTA_GATT_GetLocalMTU(); +} + +/* APIs added by Espressif */ +int bt_mesh_gatts_service_stop(struct bt_mesh_gatt_service *svc) +{ + if (!svc) { + BT_ERR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + BT_DBG("Stop service:%d", svc->attrs[0].handle); + + BTA_GATTS_StopService(svc->attrs[0].handle); + return 0; +} + +int bt_mesh_gatts_service_start(struct bt_mesh_gatt_service *svc) +{ + struct bt_mesh_uuid_16 *uuid_16 = NULL; + struct bt_mesh_uuid *uuid = NULL; + + if (!svc) { + BT_ERR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + BT_DBG("Start service:%d", svc->attrs[0].handle); + + BTA_GATTS_StartService(svc->attrs[0].handle, BTA_GATT_TRANSPORT_LE); + + /* For EspBleMesh Android app, it does not disconnect after provisioning + * is done, and hence we send GATT service change indication manually + * when Mesh Proxy Service is started after provisioning. + */ + uuid = (struct bt_mesh_uuid *)svc->attrs[0].user_data; + if (uuid && uuid->type == BLE_MESH_UUID_TYPE_16) { + uuid_16 = (struct bt_mesh_uuid_16 *)uuid; + BT_DBG("%s, type 0x%02x, val 0x%04x", __func__, uuid_16->uuid.type, uuid_16->val); + if (uuid_16->val == BLE_MESH_UUID_MESH_PROXY_VAL) { + BTA_GATTS_SendServiceChangeIndication(bt_mesh_gatts_if, bt_mesh_gatts_addr); + } + } + + return 0; +} +#endif /* defined(CONFIG_BLE_MESH_NODE) && CONFIG_BLE_MESH_NODE */ + +#if defined(CONFIG_BLE_MESH_PROVISIONER) && CONFIG_BLE_MESH_PROVISIONER +void bt_mesh_gattc_conn_cb_register(struct bt_mesh_prov_conn_cb *cb) +{ + bt_mesh_gattc_conn_cb = cb; +} + +u16_t bt_mesh_gattc_get_service_uuid(struct bt_mesh_conn *conn) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh_gattc_info); i++) { + if (conn == &bt_mesh_gattc_info[i].conn) { + break; + } + } + + if (i == ARRAY_SIZE(bt_mesh_gattc_info)) { + return 0; + } + + return bt_mesh_gattc_info[i].service_uuid; +} + +/** For provisioner acting as a GATT client, it may follow the procedures + * listed below. + * 1. Create connection with the unprovisioned device + * 2. Exchange MTU size + * 3. Find Mesh Prov Service in the device's service database + * 4. Find Mesh Prov Data In/Out characteristic within the service + * 5. Get CCC of Mesh Prov Data Out Characteristic + * 6. Set the Notification bit of CCC + */ + +int bt_mesh_gattc_conn_create(const bt_mesh_addr_t *addr, u16_t service_uuid) +{ + u8_t zero[6] = {0}; + int i; + + if (!addr || !memcmp(addr->val, zero, BLE_MESH_ADDR_LEN) || + (addr->type > BLE_ADDR_RANDOM)) { + BT_ERR("%s, Invalid remote address", __func__); + return -EINVAL; + } + + if (service_uuid != BLE_MESH_UUID_MESH_PROV_VAL && + service_uuid != BLE_MESH_UUID_MESH_PROXY_VAL) { + BT_ERR("%s, Invalid service uuid 0x%04x", __func__, service_uuid); + return -EINVAL; + } + + /* Check if already creating connection with the device */ + for (i = 0; i < ARRAY_SIZE(bt_mesh_gattc_info); i++) { + if (!memcmp(bt_mesh_gattc_info[i].addr, addr->val, BLE_MESH_ADDR_LEN)) { + BT_WARN("%s, Already create connection with %s", + __func__, bt_hex(addr->val, BLE_MESH_ADDR_LEN)); + return -EALREADY; + } + } + + /* Find empty element in queue to store device info */ + for (i = 0; i < ARRAY_SIZE(bt_mesh_gattc_info); i++) { + if ((bt_mesh_gattc_info[i].conn.handle == 0xFFFF) && + (bt_mesh_gattc_info[i].service_uuid == 0x0000)) { + memcpy(bt_mesh_gattc_info[i].addr, addr->val, BLE_MESH_ADDR_LEN); + bt_mesh_gattc_info[i].addr_type = addr->type; + /* Service to be found after exhanging mtu size */ + bt_mesh_gattc_info[i].service_uuid = service_uuid; + break; + } + } + + if (i == ARRAY_SIZE(bt_mesh_gattc_info)) { + BT_WARN("%s, gattc info is full", __func__); + return -ENOMEM; + } + +#if BLE_MESH_DEV + if (bt_mesh_atomic_test_and_clear_bit(bt_mesh_dev.flags, BLE_MESH_DEV_SCANNING)) { + BLE_MESH_BTM_CHECK_STATUS(BTM_BleScan(false, 0, NULL, NULL, NULL)); + } +#else + BLE_MESH_BTM_CHECK_STATUS(BTM_BleScan(false, 0, NULL, NULL, NULL)); +#endif /* BLE_MESH_DEV */ + + BT_DBG("%s, create conn with %s", __func__, bt_hex(addr->val, BLE_MESH_ADDR_LEN)); + + /* Min_interval: 250ms + * Max_interval: 250ms + * Slave_latency: 0x0 + * Supervision_timeout: 32 sec + */ + BTA_DmSetBlePrefConnParams(bt_mesh_gattc_info[i].addr, 0xC8, 0xC8, 0x00, 0xC80); + + BTA_GATTC_Open(bt_mesh_gattc_if, bt_mesh_gattc_info[i].addr, + bt_mesh_gattc_info[i].addr_type, true, BTA_GATT_TRANSPORT_LE); + + /* Increment pbg_count */ + provisioner_pbg_count_inc(); + + return 0; +} + +void bt_mesh_gattc_exchange_mtu(u8_t index) +{ + /** Set local MTU and exchange with GATT server. + * ATT_MTU >= 69 for Mesh GATT Prov Service + * ATT_NTU >= 33 for Mesh GATT Proxy Service + */ + u16_t conn_id; + + conn_id = BLE_MESH_GATT_CREATE_CONN_ID(bt_mesh_gattc_if, bt_mesh_gattc_info[index].conn.handle); + + BTA_GATTC_ConfigureMTU(conn_id); +} + +u16_t bt_mesh_gattc_get_mtu_info(struct bt_mesh_conn *conn) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh_gattc_info); i++) { + if (conn == &bt_mesh_gattc_info[i].conn) { + return bt_mesh_gattc_info[i].mtu; + } + } + + return 0; +} + +int bt_mesh_gattc_write_no_rsp(struct bt_mesh_conn *conn, const struct bt_mesh_gatt_attr *attr, + const void *data, u16_t len) +{ + u16_t conn_id; + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh_gattc_info); i++) { + if (conn == &bt_mesh_gattc_info[i].conn) { + break; + } + } + + if (i == ARRAY_SIZE(bt_mesh_gattc_info)) { + BT_ERR("%s, Conn is not found", __func__); + /** Here we return 0 for prov_send() return value check in provisioner.c + */ + return 0; + } + + conn_id = BLE_MESH_GATT_CREATE_CONN_ID(bt_mesh_gattc_if, bt_mesh_gattc_info[i].conn.handle); + + BTA_GATTC_WriteCharValue(conn_id, bt_mesh_gattc_info[i].data_in_handle, + BTA_GATTC_TYPE_WRITE_NO_RSP, len, + (u8_t *)data, BTA_GATT_AUTH_REQ_NONE); + + return 0; +} + +void bt_mesh_gattc_disconnect(struct bt_mesh_conn *conn) +{ + /** Disconnect + * Clear proper proxy server information + * Clear proper prov_link information + * Clear proper bt_mesh_gattc_info information + * Here in adapter, we just clear proper bt_mesh_gattc_info, and + * when proxy_disconnected callback comes, the proxy server + * information and prov_link information should be cleared. + */ + u16_t conn_id; + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh_gattc_info); i++) { + if (conn == &bt_mesh_gattc_info[i].conn) { + break; + } + } + + if (i == ARRAY_SIZE(bt_mesh_gattc_info)) { + BT_ERR("%s, Conn is not found", __func__); + return; + } + + conn_id = BLE_MESH_GATT_CREATE_CONN_ID(bt_mesh_gattc_if, bt_mesh_gattc_info[i].conn.handle); + + BTA_GATTC_Close(conn_id); +} + +/** Mesh Provisioning Service: 0x1827 + * Mesh Provisioning Data In: 0x2ADB + * Mesh Provisioning Data Out: 0x2ADC + * Mesh Proxy Service: 0x1828 + * Mesh Proxy Data In: 0x2ADD + * Mesh PROXY Data Out: 0x2ADE + */ +static void bt_mesh_bta_gattc_cb(tBTA_GATTC_EVT event, tBTA_GATTC *p_data) +{ + struct bt_mesh_conn *conn = NULL; + u16_t handle = 0; + ssize_t len = 0; + int i = 0; + + switch (event) { + case BTA_GATTC_REG_EVT: + if (p_data->reg_oper.status == BTA_GATT_OK) { + u8_t uuid[16] = { [0 ... 15] = BLE_MESH_GATTC_APP_UUID_BYTE }; + + BT_DBG("BTA_GATTC_REG_EVT"); + + if (p_data->reg_oper.app_uuid.len == LEN_UUID_128 && + !memcmp(p_data->reg_oper.app_uuid.uu.uuid128, uuid, 16)) { + bt_mesh_gattc_if = p_data->reg_oper.client_if; + BT_DBG("bt_mesh_gattc_if is %d", bt_mesh_gattc_if); + } + } + break; + case BTA_GATTC_CFG_MTU_EVT: { + if (p_data->cfg_mtu.status == BTA_GATT_OK) { + BT_DBG("BTA_GATTC_CFG_MTU_EVT, cfg_mtu is %d", p_data->cfg_mtu.mtu); + + handle = BLE_MESH_GATT_GET_CONN_ID(p_data->cfg_mtu.conn_id); + + for (i = 0; i < ARRAY_SIZE(bt_mesh_gattc_info); i++) { + if (bt_mesh_gattc_info[i].conn.handle == handle) { + bt_mesh_gattc_info[i].mtu = p_data->cfg_mtu.mtu; + break; + } + } + + /** Once mtu exchanged accomplished, start to find services, and here + * need a flag to indicate which service to find(Mesh Prov Service or + * Mesh Proxy Service) + */ + if (i != ARRAY_SIZE(bt_mesh_gattc_info)) { + tBT_UUID service_uuid; + u16_t conn_id; + + conn_id = BLE_MESH_GATT_CREATE_CONN_ID(bt_mesh_gattc_if, bt_mesh_gattc_info[i].conn.handle); + service_uuid.len = sizeof(bt_mesh_gattc_info[i].service_uuid); + service_uuid.uu.uuid16 = bt_mesh_gattc_info[i].service_uuid; + + /* Search Mesh Provisioning Service or Mesh Proxy Service */ + BTA_GATTC_ServiceSearchRequest(conn_id, &service_uuid); + } + } + break; + } + case BTA_GATTC_SEARCH_RES_EVT: { + BT_DBG("BTA_GATTC_SEARCH_RES_EVT"); + + handle = BLE_MESH_GATT_GET_CONN_ID(p_data->srvc_res.conn_id); + + for (i = 0; i < ARRAY_SIZE(bt_mesh_gattc_info); i++) { + if (bt_mesh_gattc_info[i].conn.handle == handle) { + break; + } + } + + if (i != ARRAY_SIZE(bt_mesh_gattc_info)) { + if (p_data->srvc_res.service_uuid.uuid.len == 2 && + p_data->srvc_res.service_uuid.uuid.uu.uuid16 == bt_mesh_gattc_info[i].service_uuid) { + bt_mesh_gattc_info[i].start_handle = p_data->srvc_res.start_handle; + bt_mesh_gattc_info[i].end_handle = p_data->srvc_res.end_handle; + } + } + break; + } + case BTA_GATTC_SEARCH_CMPL_EVT: { + if (p_data->search_cmpl.status == BTA_GATT_OK) { + BT_DBG("BTA_GATTC_SEARCH_CMPL_EVT"); + + handle = BLE_MESH_GATT_GET_CONN_ID(p_data->search_cmpl.conn_id); + + for (i = 0; i < ARRAY_SIZE(bt_mesh_gattc_info); i++) { + if (bt_mesh_gattc_info[i].conn.handle == handle) { + break; + } + } + + if (i == ARRAY_SIZE(bt_mesh_gattc_info)) { + BT_ERR("%s, Conn handle is not found", __func__); + return; + } + + conn = &bt_mesh_gattc_info[i].conn; + + if (bt_mesh_gattc_info[i].start_handle == 0x00 || + bt_mesh_gattc_info[i].end_handle == 0x00 || + (bt_mesh_gattc_info[i].start_handle > bt_mesh_gattc_info[i].end_handle)) { + bt_mesh_gattc_disconnect(conn); + return; + } + + int count = 0; + int num = 0; + u16_t conn_id; + tBT_UUID char_uuid; + btgatt_db_element_t *result = NULL; + tBTA_GATT_STATUS status; + u16_t notify_en = BLE_MESH_GATT_CCC_NOTIFY; + tBTA_GATT_UNFMT write; + + /* Get the characteristic num within Mesh Provisioning/Proxy Service */ + conn_id = BLE_MESH_GATT_CREATE_CONN_ID(bt_mesh_gattc_if, bt_mesh_gattc_info[i].conn.handle); + BTA_GATTC_GetDBSizeByType(conn_id, BTGATT_DB_CHARACTERISTIC, bt_mesh_gattc_info[i].start_handle, + bt_mesh_gattc_info[i].end_handle, BTA_GATTC_INVALID_HANDLE, &count); + if (count != 2) { + bt_mesh_gattc_disconnect(conn); + return; + } + + /* Get Mesh Provisioning/Proxy Data In/Out Characteristic */ + for (int j = 0; j != 2; j++) { + /** First: find Mesh Provisioning/Proxy Data In Characteristic + * Second: find Mesh Provisioning/Proxy Data Out Characteristic + */ + char_uuid.len = 2; + if (bt_mesh_gattc_info[i].service_uuid == BLE_MESH_UUID_MESH_PROV_VAL) { + char_uuid.uu.uuid16 = BLE_MESH_UUID_MESH_PROV_DATA_IN_VAL + j; + } else if (bt_mesh_gattc_info[i].service_uuid == BLE_MESH_UUID_MESH_PROXY_VAL) { + char_uuid.uu.uuid16 = BLE_MESH_UUID_MESH_PROXY_DATA_IN_VAL + j; + } + + BTA_GATTC_GetCharByUUID(conn_id, bt_mesh_gattc_info[i].start_handle, + bt_mesh_gattc_info[i].end_handle, char_uuid, &result, &num); + + if (!result) { + bt_mesh_gattc_disconnect(conn); + return; + } + + if (num != 1) { + osi_free(result); + bt_mesh_gattc_disconnect(conn); + return; + } + + if (!j) { + if (!(result[0].properties & BLE_MESH_GATT_CHRC_WRITE_WITHOUT_RESP)) { + osi_free(result); + bt_mesh_gattc_disconnect(conn); + return; + } + bt_mesh_gattc_info[i].data_in_handle = result[0].attribute_handle; + } else { + if (!(result[0].properties & BLE_MESH_GATT_CHRC_NOTIFY)) { + osi_free(result); + bt_mesh_gattc_disconnect(conn); + return; + } + bt_mesh_gattc_info[i].data_out_handle = result[0].attribute_handle; + } + osi_free(result); + result = NULL; + } + + /* Register Notification fot Mesh Provisioning/Proxy Data Out Characteristic */ + status = BTA_GATTC_RegisterForNotifications(bt_mesh_gattc_if, bt_mesh_gattc_info[i].addr, + bt_mesh_gattc_info[i].data_out_handle); + if (status != BTA_GATT_OK) { + bt_mesh_gattc_disconnect(conn); + return; + } + + /** After notification is registered, get descriptor number of the + * Mesh Provisioning/Proxy Data Out Characteristic + */ + BTA_GATTC_GetDBSizeByType(conn_id, BTGATT_DB_DESCRIPTOR, bt_mesh_gattc_info[i].start_handle, + bt_mesh_gattc_info[i].end_handle, bt_mesh_gattc_info[i].data_out_handle, &num); + if (!num) { + bt_mesh_gattc_disconnect(conn); + return; + } + + /* Get CCC of Mesh Provisioning/Proxy Data Out Characteristic */ + char_uuid.len = 2; + char_uuid.uu.uuid16 = BLE_MESH_UUID_GATT_CCC_VAL; + BTA_GATTC_GetDescrByCharHandle(conn_id, bt_mesh_gattc_info[i].data_out_handle, + char_uuid, &result, &num); + + if (!result) { + bt_mesh_gattc_disconnect(conn); + return; + } + + if (num != 1) { + osi_free(result); + bt_mesh_gattc_disconnect(conn); + return; + } + + bt_mesh_gattc_info[i].ccc_handle = result[0].attribute_handle; + + /** Enable Notification of Mesh Provisioning/Proxy Data Out + * Characteristic Descriptor. + */ + write.len = sizeof(notify_en); + write.p_value = (u8_t *)¬ify_en; + BTA_GATTC_WriteCharDescr(conn_id, result[0].attribute_handle, + BTA_GATTC_TYPE_WRITE, &write, BTA_GATT_AUTH_REQ_NONE); + + osi_free(result); + result = NULL; + } + break; + } + case BTA_GATTC_READ_DESCR_EVT: + break; + case BTA_GATTC_WRITE_DESCR_EVT: { + if (p_data->write.status == BTA_GATT_OK) { + BT_DBG("BTA_GATTC_WRITE_DESCR_EVT"); + + handle = BLE_MESH_GATT_GET_CONN_ID(p_data->write.conn_id); + + for (i = 0; i < ARRAY_SIZE(bt_mesh_gattc_info); i++) { + if (bt_mesh_gattc_info[i].conn.handle == handle) { + break; + } + } + + if (i == ARRAY_SIZE(bt_mesh_gattc_info)) { + BT_ERR("%s, Conn handle is not found", __func__); + return; + } + + conn = &bt_mesh_gattc_info[i].conn; + + if (bt_mesh_gattc_info[i].ccc_handle != p_data->write.handle) { + BT_WARN("%s, gattc ccc_handle is not matched", __func__); + bt_mesh_gattc_disconnect(conn); + return; + } + + if (bt_mesh_gattc_info[i].service_uuid == BLE_MESH_UUID_MESH_PROV_VAL) { + if (bt_mesh_gattc_conn_cb != NULL && bt_mesh_gattc_conn_cb->prov_write_descr != NULL) { + len = bt_mesh_gattc_conn_cb->prov_write_descr(&bt_mesh_gattc_info[i].conn, bt_mesh_gattc_info[i].addr); + if (len < 0) { + BT_ERR("%s, prov_write_descr failed", __func__); + bt_mesh_gattc_disconnect(conn); + return; + } + bt_mesh_gattc_info[i].wr_desc_done = true; + } + } else if (bt_mesh_gattc_info[i].service_uuid == BLE_MESH_UUID_MESH_PROXY_VAL) { + if (bt_mesh_gattc_conn_cb != NULL && bt_mesh_gattc_conn_cb->proxy_write_descr != NULL) { + len = bt_mesh_gattc_conn_cb->proxy_write_descr(&bt_mesh_gattc_info[i].conn); + if (len < 0) { + BT_ERR("%s, proxy_write_descr failed", __func__); + bt_mesh_gattc_disconnect(conn); + return; + } + } + } + } + break; + } + case BTA_GATTC_NOTIF_EVT: { + BT_DBG("BTA_GATTC_NOTIF_EVT"); + + handle = BLE_MESH_GATT_GET_CONN_ID(p_data->notify.conn_id); + + for (i = 0; i < ARRAY_SIZE(bt_mesh_gattc_info); i++) { + if (bt_mesh_gattc_info[i].conn.handle == handle) { + break; + } + } + + if (i == ARRAY_SIZE(bt_mesh_gattc_info)) { + BT_ERR("%s, Conn handle is not found", __func__); + return; + } + + conn = &bt_mesh_gattc_info[i].conn; + + if (memcmp(bt_mesh_gattc_info[i].addr, p_data->notify.bda, BLE_MESH_ADDR_LEN) || + bt_mesh_gattc_info[i].data_out_handle != p_data->notify.handle || + p_data->notify.is_notify == false) { + BT_ERR("%s, Notification error", __func__); + bt_mesh_gattc_disconnect(conn); + return; + } + + if (bt_mesh_gattc_info[i].service_uuid == BLE_MESH_UUID_MESH_PROV_VAL) { + if (bt_mesh_gattc_conn_cb != NULL && bt_mesh_gattc_conn_cb->prov_notify != NULL) { + len = bt_mesh_gattc_conn_cb->prov_notify(&bt_mesh_gattc_info[i].conn, + p_data->notify.value, p_data->notify.len); + if (len < 0) { + BT_ERR("%s, prov_notify failed", __func__); + bt_mesh_gattc_disconnect(conn); + return; + } + } + } else if (bt_mesh_gattc_info[i].service_uuid == BLE_MESH_UUID_MESH_PROXY_VAL) { + if (bt_mesh_gattc_conn_cb != NULL && bt_mesh_gattc_conn_cb->proxy_notify != NULL) { + len = bt_mesh_gattc_conn_cb->proxy_notify(&bt_mesh_gattc_info[i].conn, + p_data->notify.value, p_data->notify.len); + if (len < 0) { + BT_ERR("%s, proxy_notify failed", __func__); + bt_mesh_gattc_disconnect(conn); + return; + } + } + } + break; + } + case BTA_GATTC_READ_CHAR_EVT: + break; + case BTA_GATTC_WRITE_CHAR_EVT: + break; + case BTA_GATTC_PREP_WRITE_EVT: + break; + case BTA_GATTC_EXEC_EVT: + break; + case BTA_GATTC_OPEN_EVT: { + BT_DBG("BTA_GATTC_OPEN_EVT"); + /** After current connection is established, provisioner can + * use BTA_DmBleScan() to re-enable scan. + */ + tBTM_STATUS status; +#if BLE_MESH_DEV + if (!bt_mesh_atomic_test_bit(bt_mesh_dev.flags, BLE_MESH_DEV_SCANNING)) { + status = BTM_BleScan(true, 0, bt_mesh_scan_results_cb, NULL, NULL); + if (status != BTM_SUCCESS && status != BTM_CMD_STARTED) { + BT_ERR("%s, Invalid status %d", __func__, status); + break; + } + bt_mesh_atomic_set_bit(bt_mesh_dev.flags, BLE_MESH_DEV_SCANNING); + } +#else + status = BTM_BleScan(true, 0, bt_mesh_scan_results_cb, NULL, NULL); + if (status != BTM_SUCCESS && status != BTM_CMD_STARTED) { + BT_ERR("%s, Invalid status %d", __func__, status); + break; + } +#endif /* BLE_MESH_DEV */ + break; + } + case BTA_GATTC_CLOSE_EVT: + BT_DBG("BTA_GATTC_CLOSE_EVT"); + break; + case BTA_GATTC_CONNECT_EVT: { + BT_DBG("BTA_GATTC_CONNECT_EVT"); + + if (bt_mesh_gattc_if != p_data->connect.client_if) { + BT_ERR("%s, gattc_if & connect_if don't match", __func__); + return; + } + + if (bt_mesh_gattc_conn_cb != NULL && bt_mesh_gattc_conn_cb->connected != NULL) { + for (i = 0; i < ARRAY_SIZE(bt_mesh_gattc_info); i++) { + if (!memcmp(bt_mesh_gattc_info[i].addr, p_data->connect.remote_bda, BLE_MESH_ADDR_LEN)) { + bt_mesh_gattc_info[i].conn.handle = BLE_MESH_GATT_GET_CONN_ID(p_data->connect.conn_id); + (bt_mesh_gattc_conn_cb->connected)(bt_mesh_gattc_info[i].addr, &bt_mesh_gattc_info[i].conn, i); + break; + } + } + } + break; + } + case BTA_GATTC_DISCONNECT_EVT: { + BT_DBG("BTA_GATTC_DISCONNECT_EVT"); + + if (bt_mesh_gattc_if != p_data->disconnect.client_if) { + BT_ERR("%s, gattc_if & disconnect_if don't match", __func__); + return; + } + + handle = BLE_MESH_GATT_GET_CONN_ID(p_data->disconnect.conn_id); + + if (bt_mesh_gattc_conn_cb != NULL && bt_mesh_gattc_conn_cb->disconnected != NULL) { + for (i = 0; i < ARRAY_SIZE(bt_mesh_gattc_info); i++) { + if (!memcmp(bt_mesh_gattc_info[i].addr, p_data->disconnect.remote_bda, BLE_MESH_ADDR_LEN)) { + if (bt_mesh_gattc_info[i].conn.handle == handle) { + (bt_mesh_gattc_conn_cb->disconnected)(&bt_mesh_gattc_info[i].conn, p_data->disconnect.reason); + if (!bt_mesh_gattc_info[i].wr_desc_done) { + /* Add this in case connection is established, connected event comes, but + * connection is terminated before server->filter_type is set to PROV. + */ + provisioner_clear_link_conn_info(bt_mesh_gattc_info[i].addr); + } + } else { + /* Add this in case connection is failed to be established, and here we + * need to clear some provision link info, like connecting flag, device + * uuid, address info, etc. + */ + provisioner_clear_link_conn_info(bt_mesh_gattc_info[i].addr); + } + /* Decrease prov pbg_count */ + provisioner_pbg_count_dec(); + /* Reset corresponding gattc info */ + memset(&bt_mesh_gattc_info[i], 0, sizeof(bt_mesh_gattc_info[i])); + bt_mesh_gattc_info[i].conn.handle = 0xFFFF; + bt_mesh_gattc_info[i].mtu = GATT_DEF_BLE_MTU_SIZE; + bt_mesh_gattc_info[i].wr_desc_done = false; + break; + } + } + } + break; + } + case BTA_GATTC_CONGEST_EVT: + break; + case BTA_GATTC_SRVC_CHG_EVT: + break; + default: + break; + } +} +#endif /* defined(CONFIG_BLE_MESH_PROVISIONER) && CONFIG_BLE_MESH_PROVISIONER */ + +struct bt_mesh_conn *bt_mesh_conn_ref(struct bt_mesh_conn *conn) +{ + bt_mesh_atomic_inc(&conn->ref); + + BT_DBG("handle %u ref %u", conn->handle, bt_mesh_atomic_get(&conn->ref)); + + return conn; +} + +void bt_mesh_conn_unref(struct bt_mesh_conn *conn) +{ + bt_mesh_atomic_dec(&conn->ref); + + BT_DBG("handle %u ref %u", conn->handle, bt_mesh_atomic_get(&conn->ref)); +} + +void bt_mesh_gatt_init(void) +{ + tBT_UUID app_uuid = {LEN_UUID_128, {0}}; + + BTA_GATT_SetLocalMTU(GATT_DEF_BLE_MTU_SIZE); + +#if CONFIG_BLE_MESH_NODE + /* Fill our internal UUID with a fixed pattern 0x96 for the ble mesh */ + memset(&app_uuid.uu.uuid128, 0x96, LEN_UUID_128); + BTA_GATTS_AppRegister(&app_uuid, bt_mesh_bta_gatts_cb); +#endif + +#if CONFIG_BLE_MESH_PROVISIONER + for (int i = 0; i < ARRAY_SIZE(bt_mesh_gattc_info); i++) { + bt_mesh_gattc_info[i].conn.handle = 0xFFFF; + bt_mesh_gattc_info[i].mtu = GATT_DEF_BLE_MTU_SIZE; /* Default MTU_SIZE 23 */ + bt_mesh_gattc_info[i].wr_desc_done = false; + } + memset(&app_uuid.uu.uuid128, BLE_MESH_GATTC_APP_UUID_BYTE, LEN_UUID_128); + BTA_GATTC_AppRegister(&app_uuid, bt_mesh_bta_gattc_cb); +#endif +} + +void bt_mesh_adapt_init(void) +{ + BT_DBG("%s", __func__); + /* initialization of P-256 parameters */ + p_256_init_curve(KEY_LENGTH_DWORDS_P256); +} + +int bt_mesh_rand(void *buf, size_t len) +{ + int i; + + if (buf == NULL || len == 0) { + BT_ERR("%s, Invalid parameter", __func__); + return -EAGAIN; + } + + for (i = 0; i < (int)(len / sizeof(u32_t)); i++) { + u32_t rand = esp_random(); + memcpy(buf + i * sizeof(u32_t), &rand, sizeof(u32_t)); + } + + BT_DBG("%s, rand: %s", __func__, bt_hex(buf, len)); + return 0; +} + +void bt_mesh_set_private_key(const u8_t pri_key[32]) +{ + memcpy(bt_mesh_private_key, pri_key, 32); +} + +const u8_t *bt_mesh_pub_key_get(void) +{ + Point public_key; + BT_OCTET32 pri_key; +#if 1 + if (bt_mesh_atomic_test_bit(bt_mesh_dev.flags, BLE_MESH_DEV_HAS_PUB_KEY)) { + return bt_mesh_public_key; + } +#else + /* BLE Mesh BQB test case MESH/NODE/PROV/UPD/BV-12-C requires + * different public key for each provisioning procedure. + * Note: if enabled, when Provisioner provision multiple devices + * at the same time, this may cause invalid confirmation value. + */ + if (bt_mesh_rand(bt_mesh_private_key, 32)) { + BT_ERR("%s, Unable to generate bt_mesh_private_key", __func__); + return NULL; + } +#endif + mem_rcopy(pri_key, bt_mesh_private_key, 32); + ECC_PointMult(&public_key, &(curve_p256.G), (DWORD *)pri_key, KEY_LENGTH_DWORDS_P256); + + memcpy(bt_mesh_public_key, public_key.x, BT_OCTET32_LEN); + memcpy(bt_mesh_public_key + BT_OCTET32_LEN, public_key.y, BT_OCTET32_LEN); + + bt_mesh_atomic_set_bit(bt_mesh_dev.flags, BLE_MESH_DEV_HAS_PUB_KEY); + BT_DBG("gen the bt_mesh_public_key:%s", bt_hex(bt_mesh_public_key, sizeof(bt_mesh_public_key))); + + return bt_mesh_public_key; +} + +bool bt_mesh_check_public_key(const u8_t key[64]) +{ + struct p256_pub_key { + u8_t x[32]; + u8_t y[32]; + } check = {0}; + + sys_memcpy_swap(check.x, key, 32); + sys_memcpy_swap(check.y, key + 32, 32); + + return ECC_CheckPointIsInElliCur_P256((Point *)&check); +} + +int bt_mesh_dh_key_gen(const u8_t remote_pk[64], bt_mesh_dh_key_cb_t cb, const u8_t idx) +{ + BT_OCTET32 private_key; + Point peer_publ_key; + Point new_publ_key; + BT_OCTET32 dhkey; + + BT_DBG("private key = %s", bt_hex(bt_mesh_private_key, BT_OCTET32_LEN)); + + mem_rcopy(private_key, bt_mesh_private_key, BT_OCTET32_LEN); + memcpy(peer_publ_key.x, remote_pk, BT_OCTET32_LEN); + memcpy(peer_publ_key.y, &remote_pk[BT_OCTET32_LEN], BT_OCTET32_LEN); + + BT_DBG("remote public key x = %s", bt_hex(peer_publ_key.x, BT_OCTET32_LEN)); + BT_DBG("remote public key y = %s", bt_hex(peer_publ_key.y, BT_OCTET32_LEN)); + + ECC_PointMult(&new_publ_key, &peer_publ_key, (DWORD *) private_key, KEY_LENGTH_DWORDS_P256); + + memcpy(dhkey, new_publ_key.x, BT_OCTET32_LEN); + + BT_DBG("new public key x = %s", bt_hex(new_publ_key.x, 32)); + BT_DBG("new public key y = %s", bt_hex(new_publ_key.y, 32)); + + if (cb != NULL) { + cb((const u8_t *)dhkey, idx); + } + + return 0; +} + +#if CONFIG_MBEDTLS_HARDWARE_AES +static void ecb_encrypt(u8_t const *const key_le, u8_t const *const clear_text_le, + u8_t *const cipher_text_le, u8_t *const cipher_text_be) +{ + struct bt_mesh_ecb_param ecb; + mbedtls_aes_context aes_ctx = {0}; + + aes_ctx.key_bytes = 16; + mem_rcopy(&aes_ctx.key[0], key_le, 16); + mem_rcopy(&ecb.clear_text[0], clear_text_le, sizeof(ecb.clear_text)); + mbedtls_aes_crypt_ecb(&aes_ctx, MBEDTLS_AES_ENCRYPT, &ecb.clear_text[0], &ecb.cipher_text[0]); + + if (cipher_text_le) { + mem_rcopy(cipher_text_le, &ecb.cipher_text[0], + sizeof(ecb.cipher_text)); + } + + if (cipher_text_be) { + memcpy(cipher_text_be, &ecb.cipher_text[0], + sizeof(ecb.cipher_text)); + } +} + +static void ecb_encrypt_be(u8_t const *const key_be, u8_t const *const clear_text_be, + u8_t *const cipher_text_be) +{ + struct bt_mesh_ecb_param ecb; + mbedtls_aes_context aes_ctx = {0}; + + aes_ctx.key_bytes = 16; + memcpy(&aes_ctx.key[0], key_be, 16); + memcpy(&ecb.clear_text[0], clear_text_be, sizeof(ecb.clear_text)); + mbedtls_aes_crypt_ecb(&aes_ctx, MBEDTLS_AES_ENCRYPT, &ecb.clear_text[0], &ecb.cipher_text[0]); + + memcpy(cipher_text_be, &ecb.cipher_text[0], sizeof(ecb.cipher_text)); +} +#endif /* CONFIG_MBEDTLS_HARDWARE_AES */ + +int bt_mesh_encrypt_le(const u8_t key[16], const u8_t plaintext[16], + u8_t enc_data[16]) +{ +#if CONFIG_MBEDTLS_HARDWARE_AES + BT_DBG("key %s plaintext %s", bt_hex(key, 16), bt_hex(plaintext, 16)); + + ecb_encrypt(key, plaintext, enc_data, NULL); + + BT_DBG("enc_data %s", bt_hex(enc_data, 16)); + return 0; +#else /* CONFIG_MBEDTLS_HARDWARE_AES */ + struct tc_aes_key_sched_struct s; + u8_t tmp[16]; + + BT_DBG("key %s plaintext %s", bt_hex(key, 16), bt_hex(plaintext, 16)); + + sys_memcpy_swap(tmp, key, 16); + + if (tc_aes128_set_encrypt_key(&s, tmp) == TC_CRYPTO_FAIL) { + return -EINVAL; + } + + sys_memcpy_swap(tmp, plaintext, 16); + + if (tc_aes_encrypt(enc_data, tmp, &s) == TC_CRYPTO_FAIL) { + return -EINVAL; + } + + sys_mem_swap(enc_data, 16); + + BT_DBG("enc_data %s", bt_hex(enc_data, 16)); + + return 0; +#endif /* CONFIG_MBEDTLS_HARDWARE_AES */ +} + +int bt_mesh_encrypt_be(const u8_t key[16], const u8_t plaintext[16], + u8_t enc_data[16]) +{ +#if CONFIG_MBEDTLS_HARDWARE_AES + BT_DBG("key %s plaintext %s", bt_hex(key, 16), bt_hex(plaintext, 16)); + + ecb_encrypt_be(key, plaintext, enc_data); + + BT_DBG("enc_data %s", bt_hex(enc_data, 16)); + + return 0; +#else /* CONFIG_MBEDTLS_HARDWARE_AES */ + struct tc_aes_key_sched_struct s; + + BT_DBG("key %s plaintext %s", bt_hex(key, 16), bt_hex(plaintext, 16)); + + if (tc_aes128_set_encrypt_key(&s, key) == TC_CRYPTO_FAIL) { + return -EINVAL; + } + + if (tc_aes_encrypt(enc_data, plaintext, &s) == TC_CRYPTO_FAIL) { + return -EINVAL; + } + + BT_DBG("enc_data %s", bt_hex(enc_data, 16)); + + return 0; +#endif /* CONFIG_MBEDTLS_HARDWARE_AES */ +} + +#if defined(CONFIG_BLE_MESH_USE_DUPLICATE_SCAN) +int bt_mesh_update_exceptional_list(u8_t sub_code, u8_t type, void *info) +{ + BD_ADDR value = {0}; + + if ((sub_code > BLE_MESH_EXCEP_LIST_CLEAN) || + (type > BLE_MESH_EXCEP_INFO_MESH_PROXY_ADV)) { + BT_ERR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + if (type == BLE_MESH_EXCEP_INFO_MESH_LINK_ID) { + if (!info) { + BT_ERR("%s, NULL Provisioning Link ID", __func__); + return -EINVAL; + } + memcpy(value, info, sizeof(u32_t)); + } + + BT_DBG("%s, %s type 0x%x", __func__, sub_code ? "Remove" : "Add", type); + + /* The parameter "device_info" can't be NULL in the API */ + BLE_MESH_BTM_CHECK_STATUS(BTM_UpdateBleDuplicateExceptionalList(sub_code, type, value, NULL)); + + return 0; +} +#endif diff --git a/components/bt/ble_mesh/mesh_core/mesh_buf.c b/components/bt/ble_mesh/mesh_core/mesh_buf.c new file mode 100644 index 0000000000..d6e0cd710e --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/mesh_buf.c @@ -0,0 +1,453 @@ +/* + * Copyright (c) 2015 Intel Corporation + * Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "sdkconfig.h" + +#include "mesh_buf.h" +#include "mesh_trace.h" + +int net_buf_id(struct net_buf *buf) +{ + struct net_buf_pool *pool = buf->pool; + + return buf - pool->__bufs; +} + +static inline struct net_buf *pool_get_uninit(struct net_buf_pool *pool, + u16_t uninit_count) +{ + struct net_buf *buf; + + buf = &pool->__bufs[pool->buf_count - uninit_count]; + + buf->pool = pool; + + return buf; +} + +void *net_buf_simple_add(struct net_buf_simple *buf, size_t len) +{ + u8_t *tail = net_buf_simple_tail(buf); + + NET_BUF_SIMPLE_DBG("buf %p len %u", buf, len); + + NET_BUF_SIMPLE_ASSERT(net_buf_simple_tailroom(buf) >= len); + + buf->len += len; + return tail; +} + +void *net_buf_simple_add_mem(struct net_buf_simple *buf, const void *mem, + size_t len) +{ + NET_BUF_SIMPLE_DBG("buf %p len %u", buf, len); + + return memcpy(net_buf_simple_add(buf, len), mem, len); +} + +u8_t *net_buf_simple_add_u8(struct net_buf_simple *buf, u8_t val) +{ + u8_t *u8; + + NET_BUF_SIMPLE_DBG("buf %p val 0x%02x", buf, val); + + u8 = net_buf_simple_add(buf, 1); + *u8 = val; + + return u8; +} + +void net_buf_simple_add_le16(struct net_buf_simple *buf, u16_t val) +{ + NET_BUF_SIMPLE_DBG("buf %p val %u", buf, val); + + val = sys_cpu_to_le16(val); + memcpy(net_buf_simple_add(buf, sizeof(val)), &val, sizeof(val)); +} + +void net_buf_simple_add_be16(struct net_buf_simple *buf, u16_t val) +{ + NET_BUF_SIMPLE_DBG("buf %p val %u", buf, val); + + val = sys_cpu_to_be16(val); + memcpy(net_buf_simple_add(buf, sizeof(val)), &val, sizeof(val)); +} + +void net_buf_simple_add_le32(struct net_buf_simple *buf, u32_t val) +{ + NET_BUF_SIMPLE_DBG("buf %p val %u", buf, val); + + val = sys_cpu_to_le32(val); + memcpy(net_buf_simple_add(buf, sizeof(val)), &val, sizeof(val)); +} + +void net_buf_simple_add_be32(struct net_buf_simple *buf, u32_t val) +{ + NET_BUF_SIMPLE_DBG("buf %p val %u", buf, val); + + val = sys_cpu_to_be32(val); + memcpy(net_buf_simple_add(buf, sizeof(val)), &val, sizeof(val)); +} + +void *net_buf_simple_push(struct net_buf_simple *buf, size_t len) +{ + NET_BUF_SIMPLE_DBG("buf %p len %u", buf, len); + + NET_BUF_SIMPLE_ASSERT(net_buf_simple_headroom(buf) >= len); + + buf->data -= len; + buf->len += len; + return buf->data; +} + +void net_buf_simple_push_le16(struct net_buf_simple *buf, u16_t val) +{ + NET_BUF_SIMPLE_DBG("buf %p val %u", buf, val); + + val = sys_cpu_to_le16(val); + memcpy(net_buf_simple_push(buf, sizeof(val)), &val, sizeof(val)); +} + +void net_buf_simple_push_be16(struct net_buf_simple *buf, u16_t val) +{ + NET_BUF_SIMPLE_DBG("buf %p val %u", buf, val); + + val = sys_cpu_to_be16(val); + memcpy(net_buf_simple_push(buf, sizeof(val)), &val, sizeof(val)); +} + +void net_buf_simple_push_u8(struct net_buf_simple *buf, u8_t val) +{ + u8_t *data = net_buf_simple_push(buf, 1); + + *data = val; +} + +void *net_buf_simple_pull(struct net_buf_simple *buf, size_t len) +{ + NET_BUF_SIMPLE_DBG("buf %p len %u", buf, len); + + NET_BUF_SIMPLE_ASSERT(buf->len >= len); + + buf->len -= len; + return buf->data += len; +} + +void *net_buf_simple_pull_mem(struct net_buf_simple *buf, size_t len) +{ + void *data = buf->data; + + NET_BUF_SIMPLE_DBG("buf %p len %zu", buf, len); + + NET_BUF_SIMPLE_ASSERT(buf->len >= len); + + buf->len -= len; + buf->data += len; + + return data; +} + +u8_t net_buf_simple_pull_u8(struct net_buf_simple *buf) +{ + u8_t val; + + val = buf->data[0]; + net_buf_simple_pull(buf, 1); + + return val; +} + +u16_t net_buf_simple_pull_le16(struct net_buf_simple *buf) +{ + u16_t val; + + val = UNALIGNED_GET((u16_t *)buf->data); + net_buf_simple_pull(buf, sizeof(val)); + + return sys_le16_to_cpu(val); +} + +u16_t net_buf_simple_pull_be16(struct net_buf_simple *buf) +{ + u16_t val; + + val = UNALIGNED_GET((u16_t *)buf->data); + net_buf_simple_pull(buf, sizeof(val)); + + return sys_be16_to_cpu(val); +} + +u32_t net_buf_simple_pull_le32(struct net_buf_simple *buf) +{ + u32_t val; + + val = UNALIGNED_GET((u32_t *)buf->data); + net_buf_simple_pull(buf, sizeof(val)); + + return sys_le32_to_cpu(val); +} + +u32_t net_buf_simple_pull_be32(struct net_buf_simple *buf) +{ + u32_t val; + + val = UNALIGNED_GET((u32_t *)buf->data); + net_buf_simple_pull(buf, sizeof(val)); + + return sys_be32_to_cpu(val); +} + +size_t net_buf_simple_headroom(struct net_buf_simple *buf) +{ + return buf->data - buf->__buf; +} + +size_t net_buf_simple_tailroom(struct net_buf_simple *buf) +{ + return buf->size - net_buf_simple_headroom(buf) - buf->len; +} + +void net_buf_reset(struct net_buf *buf) +{ + NET_BUF_ASSERT(buf->flags == 0); + NET_BUF_ASSERT(buf->frags == NULL); + + net_buf_simple_reset(&buf->b); +} + +void net_buf_simple_reserve(struct net_buf_simple *buf, size_t reserve) +{ + NET_BUF_ASSERT(buf); + NET_BUF_ASSERT(buf->len == 0U); + NET_BUF_DBG("buf %p reserve %zu", buf, reserve); + + buf->data = buf->__buf + reserve; +} + +void net_buf_slist_put(sys_slist_t *list, struct net_buf *buf) +{ + struct net_buf *tail; + unsigned int key; + + NET_BUF_ASSERT(list); + NET_BUF_ASSERT(buf); + + for (tail = buf; tail->frags; tail = tail->frags) { + tail->flags |= NET_BUF_FRAGS; + } + + key = bt_mesh_irq_lock(); + sys_slist_append_list(list, &buf->node, &tail->node); + bt_mesh_irq_unlock(key); +} + +struct net_buf *net_buf_slist_get(sys_slist_t *list) +{ + struct net_buf *buf, *frag; + unsigned int key; + + NET_BUF_ASSERT(list); + + key = bt_mesh_irq_lock(); + buf = (void *)sys_slist_get(list); + bt_mesh_irq_unlock(key); + + if (!buf) { + return NULL; + } + + /* Get any fragments belonging to this buffer */ + for (frag = buf; (frag->flags & NET_BUF_FRAGS); frag = frag->frags) { + key = bt_mesh_irq_lock(); + frag->frags = (void *)sys_slist_get(list); + bt_mesh_irq_unlock(key); + + NET_BUF_ASSERT(frag->frags); + + /* The fragments flag is only for list-internal usage */ + frag->flags &= ~NET_BUF_FRAGS; + } + + /* Mark the end of the fragment list */ + frag->frags = NULL; + + return buf; +} + +struct net_buf *net_buf_ref(struct net_buf *buf) +{ + NET_BUF_ASSERT(buf); + + NET_BUF_DBG("buf %p (old) ref %u pool %p", buf, buf->ref, buf->pool); + + buf->ref++; + return buf; +} + +#if defined(CONFIG_BLE_MESH_NET_BUF_LOG) +void net_buf_unref_debug(struct net_buf *buf, const char *func, int line) +#else +void net_buf_unref(struct net_buf *buf) +#endif +{ + NET_BUF_ASSERT(buf); + + while (buf) { + struct net_buf *frags = buf->frags; + struct net_buf_pool *pool; + +#if defined(CONFIG_BLE_MESH_NET_BUF_LOG) + if (!buf->ref) { + NET_BUF_ERR("%s():%d: buf %p double free", func, line, + buf); + return; + } +#endif + NET_BUF_DBG("buf %p ref %u pool %p frags %p", buf, buf->ref, + buf->pool, buf->frags); + + /* Changed by Espressif. Add !buf->ref to avoid minus 0 */ + if (!buf->ref || --buf->ref > 0) { + return; + } + + buf->frags = NULL; + + pool = buf->pool; + + pool->uninit_count++; +#if defined(CONFIG_BLE_MESH_NET_BUF_POOL_USAGE) + pool->avail_count++; + NET_BUF_DBG("%s, pool %p, avail_count %d, uninit_count %d", __func__, + pool, pool->avail_count, pool->uninit_count); + NET_BUF_ASSERT(pool->avail_count <= pool->buf_count); +#endif + + if (pool->destroy) { + pool->destroy(buf); + } + + buf = frags; + } +} + +static u8_t *fixed_data_alloc(struct net_buf *buf, size_t *size, s32_t timeout) +{ + struct net_buf_pool *pool = buf->pool; + const struct net_buf_pool_fixed *fixed = pool->alloc->alloc_data; + + *size = MIN(fixed->data_size, *size); + + return fixed->data_pool + fixed->data_size * net_buf_id(buf); +} + +static void fixed_data_unref(struct net_buf *buf, u8_t *data) +{ + /* Nothing needed for fixed-size data pools */ +} + +const struct net_buf_data_cb net_buf_fixed_cb = { + .alloc = fixed_data_alloc, + .unref = fixed_data_unref, +}; + +static u8_t *data_alloc(struct net_buf *buf, size_t *size, s32_t timeout) +{ + struct net_buf_pool *pool = buf->pool; + + return pool->alloc->cb->alloc(buf, size, timeout); +} + +#if defined(CONFIG_BLE_MESH_NET_BUF_LOG) +struct net_buf *net_buf_alloc_len_debug(struct net_buf_pool *pool, size_t size, + s32_t timeout, const char *func, int line) +#else +struct net_buf *net_buf_alloc_len(struct net_buf_pool *pool, size_t size, + s32_t timeout) +#endif +{ + struct net_buf *buf = NULL; + unsigned int key; + int i; + + NET_BUF_ASSERT(pool); + + NET_BUF_DBG("%s, pool %p, uninit_count %d, buf_count %d", __func__, + pool, pool->uninit_count, pool->buf_count); + + /* We need to lock interrupts temporarily to prevent race conditions + * when accessing pool->uninit_count. + */ + key = bt_mesh_irq_lock(); + + /* If there are uninitialized buffers we're guaranteed to succeed + * with the allocation one way or another. + */ + if (pool->uninit_count) { + /* Changed by Espressif. Use buf when buf->ref is 0 */ + for (i = pool->buf_count; i > 0; i--) { + buf = pool_get_uninit(pool, i); + if (!buf->ref) { + bt_mesh_irq_unlock(key); + goto success; + } + } + } + + bt_mesh_irq_unlock(key); + + NET_BUF_ERR("%s, Failed to get free buffer", __func__); + return NULL; + +success: + NET_BUF_DBG("allocated buf %p", buf); + + if (size) { + buf->__buf = data_alloc(buf, &size, timeout); + if (!buf->__buf) { + NET_BUF_ERR("%s, Failed to allocate data", __func__); + return NULL; + } + } else { + NET_BUF_WARN("%s, Zero data size", __func__); + buf->__buf = NULL; + } + + buf->ref = 1; + buf->flags = 0; + buf->frags = NULL; + buf->size = size; + net_buf_reset(buf); + + pool->uninit_count--; +#if defined(CONFIG_BLE_MESH_NET_BUF_POOL_USAGE) + pool->avail_count--; + NET_BUF_ASSERT(pool->avail_count >= 0); +#endif + + return buf; +} + +#if defined(CONFIG_BLE_MESH_NET_BUF_LOG) +struct net_buf *net_buf_alloc_fixed_debug(struct net_buf_pool *pool, + s32_t timeout, const char *func, + int line) +{ + const struct net_buf_pool_fixed *fixed = pool->alloc->alloc_data; + + return net_buf_alloc_len_debug(pool, fixed->data_size, timeout, func, line); +} +#else +struct net_buf *net_buf_alloc_fixed(struct net_buf_pool *pool, s32_t timeout) +{ + const struct net_buf_pool_fixed *fixed = pool->alloc->alloc_data; + + return net_buf_alloc_len(pool, fixed->data_size, timeout); +} +#endif \ No newline at end of file diff --git a/components/bt/ble_mesh/mesh_core/mesh_hci.c b/components/bt/ble_mesh/mesh_core/mesh_hci.c new file mode 100644 index 0000000000..72da73a389 --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/mesh_hci.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2017 Nordic Semiconductor ASA + * Copyright (c) 2015-2016 Intel Corporation + * Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sdkconfig.h" + +#include "stack/bt_types.h" +#include "device/controller.h" + +#include "mesh_hci.h" + +struct bt_mesh_dev bt_mesh_dev; + +void bt_mesh_hci_init(void) +{ + const uint8_t *features = controller_get_interface()->get_features_ble()->as_array; + if (features != NULL) { + memcpy(bt_mesh_dev.features[0], features, 8); + memcpy(bt_mesh_dev.le.features, features, 8); + } + + /** + * Currently 20ms non-connectable adv interval is supported, and we need to add + * a flag to indicate this support. + */ +#ifdef CONFIG_BLE_MESH_HCI_5_0 + bt_mesh_dev.hci_version = BLE_MESH_HCI_VERSION_5_0; +#else + bt_mesh_dev.hci_version = controller_get_interface()->get_bt_version()->hci_version; +#endif + bt_mesh_dev.lmp_version = controller_get_interface()->get_bt_version()->lmp_version; + bt_mesh_dev.hci_revision = controller_get_interface()->get_bt_version()->hci_revision; + bt_mesh_dev.lmp_subversion = controller_get_interface()->get_bt_version()->lmp_subversion; + bt_mesh_dev.manufacturer = controller_get_interface()->get_bt_version()->manufacturer; + + const uint8_t *p = controller_get_interface()->get_ble_supported_states(); + uint64_t states_fh = 0, states_sh = 0; + STREAM_TO_UINT32(states_fh, p); + STREAM_TO_UINT32(states_sh, p); + bt_mesh_dev.le.states = (states_sh << 32) | states_fh; +} diff --git a/components/bt/ble_mesh/mesh_core/mesh_kernel.c b/components/bt/ble_mesh/mesh_core/mesh_kernel.c new file mode 100644 index 0000000000..d1d1ca6670 --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/mesh_kernel.c @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2016 Intel Corporation + * Copyright (c) 2016 Wind River Systems, Inc. + * Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "sdkconfig.h" + +#include "osi/hash_map.h" +#include "osi/alarm.h" +#include "osi/hash_functions.h" + +#include "common/bt_trace.h" +#include "common/bt_defs.h" + +#include "esp_timer.h" + +#include "mesh_kernel.h" +#include "mesh_trace.h" + +#include "provisioner_prov.h" + +static osi_mutex_t bm_alarm_lock; +static osi_mutex_t bm_irq_lock; +static hash_map_t *bm_alarm_hash_map; +static const size_t BLE_MESH_GENERAL_ALARM_HASH_MAP_SIZE = 20 + CONFIG_BLE_MESH_PBA_SAME_TIME + \ + CONFIG_BLE_MESH_PBG_SAME_TIME; + +typedef struct alarm_t { + /* timer id point to here */ + esp_timer_handle_t alarm_hdl; + osi_alarm_callback_t cb; + void *cb_data; + int64_t deadline_us; +} osi_alarm_t; + +static void bt_mesh_alarm_cb(void *data) +{ + assert(data != NULL); + struct k_delayed_work *work = (struct k_delayed_work *)data; + work->work.handler(&work->work); + return; +} + +unsigned int bt_mesh_irq_lock(void) +{ +#if defined(CONFIG_BLE_MESH_IRQ_LOCK) && CONFIG_BLE_MESH_IRQ_LOCK + unsigned int key = XTOS_SET_INTLEVEL(XCHAL_EXCM_LEVEL); + return key; +#else + /* Change by Espressif. In BLE Mesh, in order to improve the real-time + * requirements of bt controller, we use task lock to replace IRQ lock. + */ + osi_mutex_lock(&bm_irq_lock, OSI_MUTEX_MAX_TIMEOUT); + return 0; +#endif +} + +void bt_mesh_irq_unlock(unsigned int key) +{ +#if defined(CONFIG_BLE_MESH_IRQ_LOCK) && CONFIG_BLE_MESH_IRQ_LOCK + XTOS_RESTORE_INTLEVEL(key); +#else + osi_mutex_unlock(&bm_irq_lock); +#endif +} + +s64_t k_uptime_get(void) +{ + /** k_uptime_get_32 is in in milliseconds, + * but esp_timer_get_time is in microseconds + */ + return (esp_timer_get_time() / 1000); +} + +u32_t k_uptime_get_32(void) +{ + /** k_uptime_get_32 is in in milliseconds, + * but esp_timer_get_time is in microseconds + */ + return (u32_t)(esp_timer_get_time() / 1000); +} + +void k_sleep(s32_t duration) +{ + vTaskDelay(duration / portTICK_PERIOD_MS); + return; +} + +void bt_mesh_k_init(void) +{ + osi_mutex_new(&bm_alarm_lock); + osi_mutex_new(&bm_irq_lock); + bm_alarm_hash_map = hash_map_new(BLE_MESH_GENERAL_ALARM_HASH_MAP_SIZE, + hash_function_pointer, NULL, + (data_free_fn)osi_alarm_free, NULL); + assert(bm_alarm_hash_map != NULL); +} + +void k_delayed_work_init(struct k_delayed_work *work, k_work_handler_t handler) +{ + osi_alarm_t *alarm = NULL; + + assert(work != NULL && bm_alarm_hash_map != NULL); + + k_work_init(&work->work, handler); + + osi_mutex_lock(&bm_alarm_lock, OSI_MUTEX_MAX_TIMEOUT); + if (!hash_map_has_key(bm_alarm_hash_map, (void *)work)) { + alarm = osi_alarm_new("bt_mesh", bt_mesh_alarm_cb, (void *)work, 0); + if (alarm == NULL) { + BT_ERR("%s, Unable to create alarm", __func__); + return; + } + if (!hash_map_set(bm_alarm_hash_map, work, (void *)alarm)) { + BT_ERR("%s Unable to add the timer to hash map.", __func__); + } + } + osi_mutex_unlock(&bm_alarm_lock); + + alarm = hash_map_get(bm_alarm_hash_map, work); + if (alarm == NULL) { + BT_WARN("%s, Unable to find expected alarm in hash map", __func__); + return; + } + + // Just init the work timer only, don't start it. + osi_alarm_cancel(alarm); + return; +} + +int k_delayed_work_submit(struct k_delayed_work *work, + s32_t delay) +{ + assert(work != NULL && bm_alarm_hash_map != NULL); + + osi_alarm_t *alarm = hash_map_get(bm_alarm_hash_map, (void *)work); + if (alarm == NULL) { + BT_WARN("%s, Unable to find expected alarm in hash map", __func__); + return -EINVAL; + } + + // Cancel the alarm first, before start the alarm. + osi_alarm_cancel(alarm); + osi_alarm_set(alarm, delay); + return 0; +} + +int k_delayed_work_cancel(struct k_delayed_work *work) +{ + assert(work != NULL && bm_alarm_hash_map != NULL); + + osi_alarm_t *alarm = hash_map_get(bm_alarm_hash_map, (void *)work); + if (alarm == NULL) { + BT_WARN("%s, Unable to find expected alarm in hash map", __func__); + return -EINVAL; + } + + osi_alarm_cancel(alarm); + alarm->deadline_us = 0; + return 0; +} + +int k_delayed_work_free(struct k_delayed_work *work) +{ + assert(work != NULL && bm_alarm_hash_map != NULL); + + osi_alarm_t *alarm = hash_map_get(bm_alarm_hash_map, work); + if (alarm == NULL) { + BT_WARN("%s Unable to find expected alarm in hash map", __func__); + return -EINVAL; + } + + hash_map_erase(bm_alarm_hash_map, work); + return 0; +} + +s32_t k_delayed_work_remaining_get(struct k_delayed_work *work) +{ + assert(work != NULL && bm_alarm_hash_map != NULL); + + osi_alarm_t *alarm = hash_map_get(bm_alarm_hash_map, (void *)work); + if (alarm == NULL) { + BT_WARN("%s Unable to find expected alarm in hash map", __func__); + return 0; + } + + if (!alarm->deadline_us) { + return 0; + } + + s32_t remain_time = 0; + int64_t now = esp_timer_get_time(); + if ((alarm->deadline_us - now) < 0x1FFFFFFFFFF) { + remain_time = (alarm->deadline_us - now) / 1000; + } else { + return 0; + } + + return remain_time; +} diff --git a/components/bt/ble_mesh/mesh_core/mesh_main.c b/components/bt/ble_mesh/mesh_core/mesh_main.c new file mode 100644 index 0000000000..03760d957c --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/mesh_main.c @@ -0,0 +1,509 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include "sdkconfig.h" +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BLE_MESH_DEBUG) + +#include "mesh_buf.h" +#include "mesh_trace.h" +#include "mesh_main.h" +#include "mesh_hci.h" + +#include "adv.h" +#include "prov.h" +#include "net.h" +#include "beacon.h" +#include "lpn.h" +#include "friend.h" +#include "transport.h" +#include "access.h" +#include "foundation.h" +#include "proxy.h" +#include "settings.h" +#include "mesh.h" +#include "provisioner_prov.h" +#include "provisioner_proxy.h" +#include "provisioner_main.h" + +static volatile bool provisioner_en = false; + +#define ACTION_ENTER 0x01 +#define ACTION_SUSPEND 0x02 +#define ACTION_EXIT 0x03 + +#if CONFIG_BLE_MESH_NODE + +int bt_mesh_provision(const u8_t net_key[16], u16_t net_idx, + u8_t flags, u32_t iv_index, u16_t addr, + const u8_t dev_key[16]) +{ + bool pb_gatt_enabled; + int err; + + BT_INFO("Primary Element: 0x%04x", addr); + BT_DBG("net_idx 0x%04x flags 0x%02x iv_index 0x%04x", + net_idx, flags, iv_index); + + if (bt_mesh_atomic_test_and_set_bit(bt_mesh.flags, BLE_MESH_VALID)) { + return -EALREADY; + } + + if (IS_ENABLED(CONFIG_BLE_MESH_PB_GATT)) { + if (bt_mesh_proxy_prov_disable(false) == 0) { + pb_gatt_enabled = true; + } else { + pb_gatt_enabled = false; + } + } else { + pb_gatt_enabled = false; + } + + err = bt_mesh_net_create(net_idx, flags, net_key, iv_index); + if (err) { + bt_mesh_atomic_clear_bit(bt_mesh.flags, BLE_MESH_VALID); + + if (IS_ENABLED(CONFIG_BLE_MESH_PB_GATT) && pb_gatt_enabled) { + bt_mesh_proxy_prov_enable(); + } + + return err; + } + + bt_mesh.seq = 0U; + + bt_mesh_comp_provision(addr); + + memcpy(bt_mesh.dev_key, dev_key, 16); + + if (IS_ENABLED(CONFIG_BLE_MESH_SETTINGS)) { + BT_DBG("Storing network information persistently"); + bt_mesh_store_net(); + bt_mesh_store_subnet(&bt_mesh.sub[0]); + bt_mesh_store_iv(false); + } + + /* Add this to avoid "already active status" for bt_mesh_scan_enable() */ + bt_mesh_scan_disable(); + + bt_mesh_net_start(); + + return 0; +} + +void bt_mesh_reset(void) +{ + if (!bt_mesh_atomic_test_bit(bt_mesh.flags, BLE_MESH_VALID)) { + BT_WARN("%s, Not provisioned", __func__); + return; + } + + bt_mesh.iv_index = 0U; + bt_mesh.seq = 0U; + + memset(bt_mesh.flags, 0, sizeof(bt_mesh.flags)); + + k_delayed_work_cancel(&bt_mesh.ivu_timer); + + bt_mesh_cfg_reset(); + + bt_mesh_rx_reset(); + bt_mesh_tx_reset(); + + if (IS_ENABLED(CONFIG_BLE_MESH_LOW_POWER)) { + bt_mesh_lpn_disable(true); + } + + if (IS_ENABLED(CONFIG_BLE_MESH_FRIEND)) { + bt_mesh_friend_clear_net_idx(BLE_MESH_KEY_ANY); + } + + if (IS_ENABLED(CONFIG_BLE_MESH_GATT_PROXY)) { + bt_mesh_proxy_gatt_disable(); + } + + if (IS_ENABLED(CONFIG_BLE_MESH_SETTINGS)) { + bt_mesh_clear_net(); + } + + (void)memset(bt_mesh.dev_key, 0, sizeof(bt_mesh.dev_key)); + + bt_mesh_scan_disable(); + bt_mesh_beacon_disable(); + + bt_mesh_comp_unprovision(); + + if (IS_ENABLED(CONFIG_BLE_MESH_PROV)) { + bt_mesh_prov_reset(); + } +} + +bool bt_mesh_is_provisioned(void) +{ + return bt_mesh_atomic_test_bit(bt_mesh.flags, BLE_MESH_VALID); +} + +int bt_mesh_prov_enable(bt_mesh_prov_bearer_t bearers) +{ + if (bt_mesh_is_provisioned()) { + return -EALREADY; + } + + if (IS_ENABLED(CONFIG_BLE_MESH_PB_ADV) && + (bearers & BLE_MESH_PROV_ADV)) { + /* Make sure we're scanning for provisioning inviations */ + bt_mesh_scan_enable(); + /* Enable unprovisioned beacon sending */ + bt_mesh_beacon_enable(); + } + + if (IS_ENABLED(CONFIG_BLE_MESH_PB_GATT) && + (bearers & BLE_MESH_PROV_GATT)) { + bt_mesh_proxy_prov_enable(); + bt_mesh_adv_update(); + } + + return 0; +} + +int bt_mesh_prov_disable(bt_mesh_prov_bearer_t bearers) +{ + if (bt_mesh_is_provisioned()) { + return -EALREADY; + } + + if (IS_ENABLED(CONFIG_BLE_MESH_PB_ADV) && + (bearers & BLE_MESH_PROV_ADV)) { + bt_mesh_beacon_disable(); + bt_mesh_scan_disable(); + } + + if (IS_ENABLED(CONFIG_BLE_MESH_PB_GATT) && + (bearers & BLE_MESH_PROV_GATT)) { + bt_mesh_proxy_prov_disable(true); + } + + return 0; +} + +#endif /* CONFIG_BLE_MESH_NODE */ + +static void model_suspend(struct bt_mesh_model *mod, struct bt_mesh_elem *elem, + bool vnd, bool primary, void *user_data) +{ + if (mod->pub && mod->pub->update) { + mod->pub->count = 0U; + k_delayed_work_cancel(&mod->pub->timer); + } +} + +int bt_mesh_suspend(void) +{ + int err; + + if (!bt_mesh_atomic_test_bit(bt_mesh.flags, BLE_MESH_VALID)) { + return -EINVAL; + } + + if (bt_mesh_atomic_test_and_set_bit(bt_mesh.flags, BLE_MESH_SUSPENDED)) { + return -EALREADY; + } + + err = bt_mesh_scan_disable(); + if (err) { + bt_mesh_atomic_clear_bit(bt_mesh.flags, BLE_MESH_SUSPENDED); + BT_WARN("%s, Disabling scanning failed (err %d)", __func__, err); + return err; + } + + bt_mesh_hb_pub_disable(); + + if (bt_mesh_beacon_get() == BLE_MESH_BEACON_ENABLED) { + bt_mesh_beacon_disable(); + } + + bt_mesh_model_foreach(model_suspend, NULL); + + return 0; +} + +static void model_resume(struct bt_mesh_model *mod, struct bt_mesh_elem *elem, + bool vnd, bool primary, void *user_data) +{ + if (mod->pub && mod->pub->update) { + s32_t period_ms = bt_mesh_model_pub_period_get(mod); + + if (period_ms) { + k_delayed_work_submit(&mod->pub->timer, period_ms); + } + } +} + +int bt_mesh_resume(void) +{ + int err; + + if (!bt_mesh_atomic_test_bit(bt_mesh.flags, BLE_MESH_VALID)) { + return -EINVAL; + } + + if (!bt_mesh_atomic_test_and_clear_bit(bt_mesh.flags, BLE_MESH_SUSPENDED)) { + return -EALREADY; + } + + err = bt_mesh_scan_enable(); + if (err) { + BT_WARN("%s, Re-enabling scanning failed (err %d)", __func__, err); + bt_mesh_atomic_set_bit(bt_mesh.flags, BLE_MESH_SUSPENDED); + return err; + } + + if (bt_mesh_beacon_get() == BLE_MESH_BEACON_ENABLED) { + bt_mesh_beacon_enable(); + } + + bt_mesh_model_foreach(model_resume, NULL); + + return err; +} + +int bt_mesh_init(const struct bt_mesh_prov *prov, + const struct bt_mesh_comp *comp) +{ + int err; + + bt_mesh_k_init(); + + bt_mesh_hci_init(); + + bt_mesh_adapt_init(); + + err = bt_mesh_comp_register(comp); + if (err) { + return err; + } + + bt_mesh_gatt_init(); + +#if CONFIG_BLE_MESH_NODE + extern struct bt_mesh_gatt_service proxy_svc; + if (IS_ENABLED(CONFIG_BLE_MESH_GATT_PROXY)) { + bt_mesh_gatts_service_register(&proxy_svc); + } + + extern struct bt_mesh_gatt_service prov_svc; + if (IS_ENABLED(CONFIG_BLE_MESH_PB_GATT)) { + bt_mesh_gatts_service_register(&prov_svc); + } +#endif + + if (IS_ENABLED(CONFIG_BLE_MESH_PROV)) { +#if CONFIG_BLE_MESH_NODE + err = bt_mesh_prov_init(prov); + if (err) { + return err; + } +#endif +#if CONFIG_BLE_MESH_PROVISIONER + err = provisioner_prov_init(prov); + if (err) { + return err; + } +#endif + } + + bt_mesh_net_init(); + bt_mesh_trans_init(); + +#if CONFIG_BLE_MESH_NODE + /* Changed by Espressif, add random delay (0 ~ 3s) */ +#if defined(CONFIG_BLE_MESH_FAST_PROV) + u32_t delay = 0; + bt_mesh_rand(&delay, sizeof(u32_t)); + vTaskDelay((delay % 3000) / portTICK_PERIOD_MS); +#endif + bt_mesh_beacon_init(); +#endif + + bt_mesh_adv_init(); + + if (IS_ENABLED(CONFIG_BLE_MESH_PROXY)) { +#if CONFIG_BLE_MESH_NODE + bt_mesh_proxy_init(); +#endif +#if CONFIG_BLE_MESH_PROVISIONER + provisioner_proxy_init(); +#endif + } + +#if !CONFIG_BLE_MESH_NODE && CONFIG_BLE_MESH_PROVISIONER + /* If node & provisioner are both enabled and the + * device starts as a node, it must finish provisioning */ + err = provisioner_upper_init(); + if (err) { + return err; + } +#endif + +#if defined(CONFIG_BLE_MESH_SETTINGS) + if (IS_ENABLED(CONFIG_BLE_MESH_SETTINGS)) { + bt_mesh_settings_init(); + } +#endif + + return 0; +} + +bool bt_mesh_is_provisioner_en(void) +{ + return provisioner_en; +} + +/* The following APIs are for fast provisioning */ + +#if CONFIG_BLE_MESH_PROVISIONER +int bt_mesh_provisioner_enable(bt_mesh_prov_bearer_t bearers) +{ + int err; + + if (bt_mesh_is_provisioner_en()) { + BT_WARN("%s, Provisioner is already enabled", __func__); + return -EALREADY; + } + + err = provisioner_upper_init(); + if (err) { + BT_ERR("%s, provisioner_upper_init fail", __func__); + return err; + } + +#if defined(CONFIG_BLE_MESH_USE_DUPLICATE_SCAN) + if (IS_ENABLED(CONFIG_BLE_MESH_PB_ADV) && + (bearers & BLE_MESH_PROV_ADV)) { + bt_mesh_update_exceptional_list(BLE_MESH_EXCEP_LIST_ADD, + BLE_MESH_EXCEP_INFO_MESH_BEACON, NULL); + } + + if (IS_ENABLED(CONFIG_BLE_MESH_PB_GATT) && + (bearers & BLE_MESH_PROV_GATT)) { + bt_mesh_update_exceptional_list(BLE_MESH_EXCEP_LIST_ADD, + BLE_MESH_EXCEP_INFO_MESH_PROV_ADV, NULL); + } + + if (IS_ENABLED(CONFIG_BLE_MESH_PROXY)) { + bt_mesh_update_exceptional_list(BLE_MESH_EXCEP_LIST_ADD, + BLE_MESH_EXCEP_INFO_MESH_PROXY_ADV, NULL); + } +#endif + + if ((IS_ENABLED(CONFIG_BLE_MESH_PB_ADV) && + (bearers & BLE_MESH_PROV_ADV)) || + (IS_ENABLED(CONFIG_BLE_MESH_PB_GATT) && + (bearers & BLE_MESH_PROV_GATT))) { + bt_mesh_scan_enable(); + } + + if (IS_ENABLED(CONFIG_BLE_MESH_PB_GATT) && + (bearers & BLE_MESH_PROV_GATT)) { + provisioner_pb_gatt_enable(); + } + + provisioner_en = true; + + return 0; +} + +int bt_mesh_provisioner_disable(bt_mesh_prov_bearer_t bearers) +{ + if (!bt_mesh_is_provisioner_en()) { + BT_WARN("%s, Provisioner is already disabled", __func__); + return -EALREADY; + } + + if (IS_ENABLED(CONFIG_BLE_MESH_PB_GATT) && + (bearers & BLE_MESH_PROV_GATT)) { + provisioner_pb_gatt_disable(); + } + + if ((IS_ENABLED(CONFIG_BLE_MESH_PB_ADV) && + (bearers & BLE_MESH_PROV_ADV)) && + (IS_ENABLED(CONFIG_BLE_MESH_PB_GATT) && + (bearers & BLE_MESH_PROV_GATT))) { + bt_mesh_scan_disable(); + } + + provisioner_en = false; + + return 0; +} +#endif /* CONFIG_BLE_MESH_PROVISIONER */ + +/* The following API is for fast provisioning */ + +#if CONFIG_BLE_MESH_FAST_PROV +u8_t bt_mesh_set_fast_prov_action(u8_t action) +{ + if (!action || action > ACTION_EXIT) { + return 0x01; + } + + if ((!provisioner_en && (action == ACTION_SUSPEND || action == ACTION_EXIT)) || + (provisioner_en && (action == ACTION_ENTER))) { + BT_WARN("%s, Action is already done", __func__); + return 0x0; + } + + if (action == ACTION_ENTER) { +#if 0 + /* If the device is provisioned using PB-GATT and connected to + * the phone with proxy service, proxy_gatt shall not be disabled + * here. The node needs to send some status messages to the phone + * while it is connected. + */ + if (IS_ENABLED(CONFIG_BLE_MESH_GATT_PROXY)) { + bt_mesh_proxy_gatt_disable(); + } +#endif + if (bt_mesh_beacon_get() == BLE_MESH_BEACON_ENABLED) { + bt_mesh_beacon_disable(); + } + if (IS_ENABLED(CONFIG_BLE_MESH_PB_GATT)) { + provisioner_pb_gatt_enable(); + } + provisioner_set_fast_prov_flag(true); + provisioner_en = true; + } else { + if (IS_ENABLED(CONFIG_BLE_MESH_PB_GATT)) { + provisioner_pb_gatt_disable(); + } + if (bt_mesh_beacon_get() == BLE_MESH_BEACON_ENABLED) { + bt_mesh_beacon_enable(); + } +#if 0 + /* Mesh Proxy GATT will be re-enabled on application layer */ + if (IS_ENABLED(CONFIG_BLE_MESH_GATT_PROXY) && + bt_mesh_gatt_proxy_get() != BLE_MESH_GATT_PROXY_NOT_SUPPORTED) { + bt_mesh_proxy_gatt_enable(); + bt_mesh_adv_update(); + } +#endif + provisioner_set_fast_prov_flag(false); + provisioner_en = false; + if (action == ACTION_EXIT) { + provisioner_upper_reset_all_nodes(); + provisioner_prov_reset_all_nodes(); + } + } + + return 0x0; +} +#endif /* CONFIG_BLE_MESH_FAST_PROV */ diff --git a/components/bt/ble_mesh/mesh_core/mesh_util.c b/components/bt/ble_mesh/mesh_core/mesh_util.c new file mode 100644 index 0000000000..4650bb2603 --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/mesh_util.c @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2017 Nordic Semiconductor ASA + * Copyright (c) 2016 Vinayak Kariappa Chettimada + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "sdkconfig.h" +#include "mesh_util.h" +#include "mesh_kernel.h" +#include "mesh_aes_encrypt.h" + +#define MASK_TWENTY_SEVEN 0x1b + +const char *bt_hex(const void *buf, size_t len) +{ + static const char hex[] = "0123456789abcdef"; + static char hexbufs[4][129]; + static u8_t curbuf; + const u8_t *b = buf; + unsigned int mask; + char *str; + int i; + + mask = bt_mesh_irq_lock(); + str = hexbufs[curbuf++]; + curbuf %= ARRAY_SIZE(hexbufs); + bt_mesh_irq_unlock(mask); + + len = MIN(len, (sizeof(hexbufs[0]) - 1) / 2); + + for (i = 0; i < len; i++) { + str[i * 2] = hex[b[i] >> 4]; + str[i * 2 + 1] = hex[b[i] & 0xf]; + } + + str[i * 2] = '\0'; + + return str; +} + +void mem_rcopy(u8_t *dst, u8_t const *src, u16_t len) +{ + src += len; + while (len--) { + *dst++ = *--src; + } +} + +unsigned int _copy(uint8_t *to, unsigned int to_len, + const uint8_t *from, unsigned int from_len) +{ + if (from_len <= to_len) { + (void)memcpy(to, from, from_len); + return from_len; + } else { + return TC_CRYPTO_FAIL; + } +} + +void _set(void *to, uint8_t val, unsigned int len) +{ + (void)memset(to, val, len); +} + +/* + * Doubles the value of a byte for values up to 127. + */ +uint8_t _double_byte(uint8_t a) +{ + return ((a << 1) ^ ((a >> 7) * MASK_TWENTY_SEVEN)); +} + +int _compare(const uint8_t *a, const uint8_t *b, size_t size) +{ + const uint8_t *tempa = a; + const uint8_t *tempb = b; + uint8_t result = 0; + + for (unsigned int i = 0; i < size; i++) { + result |= tempa[i] ^ tempb[i]; + } + return result; +} diff --git a/components/bt/ble_mesh/mesh_core/net.c b/components/bt/ble_mesh/mesh_core/net.c new file mode 100644 index 0000000000..020ea02707 --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/net.c @@ -0,0 +1,1517 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include "sdkconfig.h" +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BLE_MESH_DEBUG_NET) + +#include "mesh_util.h" +#include "mesh_buf.h" +#include "mesh_main.h" +#include "mesh_trace.h" +#include "mesh.h" + +#include "crypto.h" +#include "adv.h" +#include "mesh.h" +#include "net.h" +#include "lpn.h" +#include "friend.h" +#include "proxy.h" +#include "transport.h" +#include "access.h" +#include "foundation.h" +#include "beacon.h" +#include "settings.h" +#include "prov.h" +#include "provisioner_main.h" + +/* Minimum valid Mesh Network PDU length. The Network headers + * themselves take up 9 bytes. After that there is a minumum of 1 byte + * payload for both CTL=1 and CTL=0 PDUs (smallest OpCode is 1 byte). CTL=1 + * PDUs must use a 64-bit (8 byte) NetMIC, whereas CTL=0 PDUs have at least + * a 32-bit (4 byte) NetMIC and AppMIC giving again a total of 8 bytes. + */ +#define BLE_MESH_NET_MIN_PDU_LEN (BLE_MESH_NET_HDR_LEN + 1 + 8) + +/* Seq limit after IV Update is triggered */ +#define IV_UPDATE_SEQ_LIMIT 8000000 + +#define IVI(pdu) ((pdu)[0] >> 7) +#define NID(pdu) ((pdu)[0] & 0x7f) +#define CTL(pdu) ((pdu)[1] >> 7) +#define TTL(pdu) ((pdu)[1] & 0x7f) +#define SEQ(pdu) (((u32_t)(pdu)[2] << 16) | \ + ((u32_t)(pdu)[3] << 8) | (u32_t)(pdu)[4]); +#define SRC(pdu) (sys_get_be16(&(pdu)[5])) +#define DST(pdu) (sys_get_be16(&(pdu)[7])) + +/* Determine how many friendship credentials we need */ +#if defined(CONFIG_BLE_MESH_FRIEND) +#define FRIEND_CRED_COUNT CONFIG_BLE_MESH_FRIEND_LPN_COUNT +#elif defined(CONFIG_BLE_MESH_LOW_POWER) +#define FRIEND_CRED_COUNT CONFIG_BLE_MESH_SUBNET_COUNT +#else +#define FRIEND_CRED_COUNT 0 +#endif + +#if FRIEND_CRED_COUNT > 0 +static struct friend_cred friend_cred[FRIEND_CRED_COUNT]; +#endif + +static u64_t msg_cache[CONFIG_BLE_MESH_MSG_CACHE_SIZE]; +static u16_t msg_cache_next; + +/* Singleton network context (the implementation only supports one) */ +struct bt_mesh_net bt_mesh = { + .local_queue = SYS_SLIST_STATIC_INIT(&bt_mesh.local_queue), + .sub = { + [0 ... (CONFIG_BLE_MESH_SUBNET_COUNT - 1)] = { + .net_idx = BLE_MESH_KEY_UNUSED, + } + }, + .app_keys = { + [0 ... (CONFIG_BLE_MESH_APP_KEY_COUNT - 1)] = { + .net_idx = BLE_MESH_KEY_UNUSED, + } + }, +}; + +static u32_t dup_cache[4]; +static int dup_cache_next; + +static bool check_dup(struct net_buf_simple *data) +{ + const u8_t *tail = net_buf_simple_tail(data); + u32_t val; + int i; + + val = sys_get_be32(tail - 4) ^ sys_get_be32(tail - 8); + + for (i = 0; i < ARRAY_SIZE(dup_cache); i++) { + if (dup_cache[i] == val) { + return true; + } + } + + dup_cache[dup_cache_next++] = val; + dup_cache_next %= ARRAY_SIZE(dup_cache); + + return false; +} + +static u64_t msg_hash(struct bt_mesh_net_rx *rx, struct net_buf_simple *pdu) +{ + u32_t hash1, hash2; + + /* Three least significant bytes of IVI + first byte of SEQ */ + hash1 = (BLE_MESH_NET_IVI_RX(rx) << 8) | pdu->data[2]; + + /* Two last bytes of SEQ + SRC */ + memcpy(&hash2, &pdu->data[3], 4); + + return (u64_t)hash1 << 32 | (u64_t)hash2; +} + +static bool msg_cache_match(struct bt_mesh_net_rx *rx, + struct net_buf_simple *pdu) +{ + u64_t hash = msg_hash(rx, pdu); + u16_t i; + + for (i = 0U; i < ARRAY_SIZE(msg_cache); i++) { + if (msg_cache[i] == hash) { + return true; + } + } + + /* Add to the cache */ + msg_cache[msg_cache_next++] = hash; + msg_cache_next %= ARRAY_SIZE(msg_cache); + + return false; +} + +struct bt_mesh_subnet *bt_mesh_subnet_get(u16_t net_idx) +{ + int i; + + if (net_idx == BLE_MESH_KEY_ANY) { + return &bt_mesh.sub[0]; + } + + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + if (bt_mesh.sub[i].net_idx == net_idx) { + return &bt_mesh.sub[i]; + } + } + + return NULL; +} + +int bt_mesh_net_keys_create(struct bt_mesh_subnet_keys *keys, + const u8_t key[16]) +{ + u8_t p[] = { 0 }; + u8_t nid; + int err; + + err = bt_mesh_k2(key, p, sizeof(p), &nid, keys->enc, keys->privacy); + if (err) { + BT_ERR("%s, Unable to generate NID, EncKey & PrivacyKey", __func__); + return err; + } + + memcpy(keys->net, key, 16); + + keys->nid = nid; + + BT_DBG("NID 0x%02x EncKey %s", keys->nid, bt_hex(keys->enc, 16)); + BT_DBG("PrivacyKey %s", bt_hex(keys->privacy, 16)); + + err = bt_mesh_k3(key, keys->net_id); + if (err) { + BT_ERR("%s, Unable to generate Net ID", __func__); + return err; + } + + BT_DBG("NetID %s", bt_hex(keys->net_id, 8)); + +#if defined(CONFIG_BLE_MESH_GATT_PROXY) + err = bt_mesh_identity_key(key, keys->identity); + if (err) { + BT_ERR("%s, Unable to generate IdentityKey", __func__); + return err; + } + + BT_DBG("IdentityKey %s", bt_hex(keys->identity, 16)); +#endif /* GATT_PROXY */ + + err = bt_mesh_beacon_key(key, keys->beacon); + if (err) { + BT_ERR("%s, Unable to generate beacon key", __func__); + return err; + } + + BT_DBG("BeaconKey %s", bt_hex(keys->beacon, 16)); + + return 0; +} + +#if (defined(CONFIG_BLE_MESH_LOW_POWER) || \ + defined(CONFIG_BLE_MESH_FRIEND)) +int friend_cred_set(struct friend_cred *cred, u8_t idx, const u8_t net_key[16]) +{ + u16_t lpn_addr, frnd_addr; + int err; + u8_t p[9]; + +#if defined(CONFIG_BLE_MESH_LOW_POWER) + if (cred->addr == bt_mesh.lpn.frnd) { + lpn_addr = bt_mesh_primary_addr(); + frnd_addr = cred->addr; + } else { + lpn_addr = cred->addr; + frnd_addr = bt_mesh_primary_addr(); + } +#else + lpn_addr = cred->addr; + frnd_addr = bt_mesh_primary_addr(); +#endif + + BT_DBG("LPNAddress 0x%04x FriendAddress 0x%04x", lpn_addr, frnd_addr); + BT_DBG("LPNCounter 0x%04x FriendCounter 0x%04x", cred->lpn_counter, + cred->frnd_counter); + + p[0] = 0x01; + sys_put_be16(lpn_addr, p + 1); + sys_put_be16(frnd_addr, p + 3); + sys_put_be16(cred->lpn_counter, p + 5); + sys_put_be16(cred->frnd_counter, p + 7); + + err = bt_mesh_k2(net_key, p, sizeof(p), &cred->cred[idx].nid, + cred->cred[idx].enc, cred->cred[idx].privacy); + if (err) { + BT_ERR("%s, Unable to generate NID, EncKey & PrivacyKey", __func__); + return err; + } + + BT_DBG("Friend NID 0x%02x EncKey %s", cred->cred[idx].nid, + bt_hex(cred->cred[idx].enc, 16)); + BT_DBG("Friend PrivacyKey %s", bt_hex(cred->cred[idx].privacy, 16)); + + return 0; +} + +void friend_cred_refresh(u16_t net_idx) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(friend_cred); i++) { + struct friend_cred *cred = &friend_cred[i]; + + if (cred->addr != BLE_MESH_ADDR_UNASSIGNED && + cred->net_idx == net_idx) { + memcpy(&cred->cred[0], &cred->cred[1], + sizeof(cred->cred[0])); + } + } +} + +int friend_cred_update(struct bt_mesh_subnet *sub) +{ + int err, i; + + BT_DBG("net_idx 0x%04x", sub->net_idx); + + for (i = 0; i < ARRAY_SIZE(friend_cred); i++) { + struct friend_cred *cred = &friend_cred[i]; + + if (cred->addr == BLE_MESH_ADDR_UNASSIGNED || + cred->net_idx != sub->net_idx) { + continue; + } + + err = friend_cred_set(cred, 1, sub->keys[1].net); + if (err) { + return err; + } + } + + return 0; +} + +struct friend_cred *friend_cred_create(struct bt_mesh_subnet *sub, u16_t addr, + u16_t lpn_counter, u16_t frnd_counter) +{ + struct friend_cred *cred; + int i, err; + + BT_DBG("net_idx 0x%04x addr 0x%04x", sub->net_idx, addr); + + for (cred = NULL, i = 0; i < ARRAY_SIZE(friend_cred); i++) { + if ((friend_cred[i].addr == BLE_MESH_ADDR_UNASSIGNED) || + (friend_cred[i].addr == addr && + friend_cred[i].net_idx == sub->net_idx)) { + cred = &friend_cred[i]; + break; + } + } + + if (!cred) { + BT_WARN("No free friend credential slots"); + return NULL; + } + + cred->net_idx = sub->net_idx; + cred->addr = addr; + cred->lpn_counter = lpn_counter; + cred->frnd_counter = frnd_counter; + + err = friend_cred_set(cred, 0, sub->keys[0].net); + if (err) { + friend_cred_clear(cred); + return NULL; + } + + if (sub->kr_flag) { + err = friend_cred_set(cred, 1, sub->keys[1].net); + if (err) { + friend_cred_clear(cred); + return NULL; + } + } + + return cred; +} + +void friend_cred_clear(struct friend_cred *cred) +{ + cred->net_idx = BLE_MESH_KEY_UNUSED; + cred->addr = BLE_MESH_ADDR_UNASSIGNED; + cred->lpn_counter = 0U; + cred->frnd_counter = 0U; + (void)memset(cred->cred, 0, sizeof(cred->cred)); +} + +int friend_cred_del(u16_t net_idx, u16_t addr) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(friend_cred); i++) { + struct friend_cred *cred = &friend_cred[i]; + + if (cred->addr == addr && cred->net_idx == net_idx) { + friend_cred_clear(cred); + return 0; + } + } + + return -ENOENT; +} + +int friend_cred_get(struct bt_mesh_subnet *sub, u16_t addr, u8_t *nid, + const u8_t **enc, const u8_t **priv) +{ + int i; + + BT_DBG("net_idx 0x%04x addr 0x%04x", sub->net_idx, addr); + + for (i = 0; i < ARRAY_SIZE(friend_cred); i++) { + struct friend_cred *cred = &friend_cred[i]; + + if (cred->net_idx != sub->net_idx) { + continue; + } + + if (addr != BLE_MESH_ADDR_UNASSIGNED && cred->addr != addr) { + continue; + } + + if (nid) { + *nid = cred->cred[sub->kr_flag].nid; + } + + if (enc) { + *enc = cred->cred[sub->kr_flag].enc; + } + + if (priv) { + *priv = cred->cred[sub->kr_flag].privacy; + } + + return 0; + } + + return -ENOENT; +} +#else +int friend_cred_get(struct bt_mesh_subnet *sub, u16_t addr, u8_t *nid, + const u8_t **enc, const u8_t **priv) +{ + return -ENOENT; +} +#endif /* FRIEND || LOW_POWER */ + +u8_t bt_mesh_net_flags(struct bt_mesh_subnet *sub) +{ + u8_t flags = 0x00; + + if (sub && sub->kr_flag) { + flags |= BLE_MESH_NET_FLAG_KR; + } + + if (bt_mesh_atomic_test_bit(bt_mesh.flags, BLE_MESH_IVU_IN_PROGRESS)) { + flags |= BLE_MESH_NET_FLAG_IVU; + } + + return flags; +} + +int bt_mesh_net_beacon_update(struct bt_mesh_subnet *sub) +{ + u8_t flags = bt_mesh_net_flags(sub); + struct bt_mesh_subnet_keys *keys; + + if (sub->kr_flag) { + BT_DBG("NetIndex %u Using new key", sub->net_idx); + keys = &sub->keys[1]; + } else { + BT_DBG("NetIndex %u Using current key", sub->net_idx); + keys = &sub->keys[0]; + } + + BT_DBG("flags 0x%02x, IVI 0x%08x", flags, bt_mesh.iv_index); + + return bt_mesh_beacon_auth(keys->beacon, flags, keys->net_id, + bt_mesh.iv_index, sub->auth); +} + +int bt_mesh_net_create(u16_t idx, u8_t flags, const u8_t key[16], + u32_t iv_index) +{ + struct bt_mesh_subnet *sub; + int err; + + BT_DBG("idx %u flags 0x%02x iv_index %u", idx, flags, iv_index); + + BT_DBG("NetKey %s", bt_hex(key, 16)); + + (void)memset(msg_cache, 0, sizeof(msg_cache)); + msg_cache_next = 0U; + + sub = &bt_mesh.sub[0]; + + sub->kr_flag = BLE_MESH_KEY_REFRESH(flags); + if (sub->kr_flag) { + err = bt_mesh_net_keys_create(&sub->keys[1], key); + if (err) { + return -EIO; + } + + sub->kr_phase = BLE_MESH_KR_PHASE_2; + } else { + err = bt_mesh_net_keys_create(&sub->keys[0], key); + if (err) { + return -EIO; + } + } + + sub->net_idx = idx; + + if (IS_ENABLED(CONFIG_BLE_MESH_GATT_PROXY)) { + sub->node_id = BLE_MESH_NODE_IDENTITY_STOPPED; + } else { + sub->node_id = BLE_MESH_NODE_IDENTITY_NOT_SUPPORTED; + } + + bt_mesh.iv_index = iv_index; + bt_mesh_atomic_set_bit_to(bt_mesh.flags, BLE_MESH_IVU_IN_PROGRESS, + BLE_MESH_IV_UPDATE(flags)); + + /* Set minimum required hours, since the 96-hour minimum requirement + * doesn't apply straight after provisioning (since we can't know how + * long has actually passed since the network changed its state). + */ + bt_mesh.ivu_duration = BLE_MESH_IVU_MIN_HOURS; + + /* Make sure we have valid beacon data to be sent */ + bt_mesh_net_beacon_update(sub); + + return 0; +} + +void bt_mesh_net_revoke_keys(struct bt_mesh_subnet *sub) +{ + int i; + + BT_DBG("idx 0x%04x", sub->net_idx); + + memcpy(&sub->keys[0], &sub->keys[1], sizeof(sub->keys[0])); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.app_keys); i++) { + struct bt_mesh_app_key *key = &bt_mesh.app_keys[i]; + + if (key->net_idx != sub->net_idx || !key->updated) { + continue; + } + + memcpy(&key->keys[0], &key->keys[1], sizeof(key->keys[0])); + key->updated = false; + } +} + +bool bt_mesh_kr_update(struct bt_mesh_subnet *sub, u8_t new_kr, bool new_key) +{ + if (new_kr != sub->kr_flag && sub->kr_phase == BLE_MESH_KR_NORMAL) { + BT_WARN("KR change in normal operation. Are we blacklisted?"); + return false; + } + + sub->kr_flag = new_kr; + + if (sub->kr_flag) { + if (sub->kr_phase == BLE_MESH_KR_PHASE_1) { + BT_DBG("Phase 1 -> Phase 2"); + sub->kr_phase = BLE_MESH_KR_PHASE_2; + return true; + } + } else { + switch (sub->kr_phase) { + case BLE_MESH_KR_PHASE_1: + if (!new_key) { + /* Ignore */ + break; + } + /* Upon receiving a Secure Network beacon with the KR flag set + * to 0 using the new NetKey in Phase 1, the node shall + * immediately transition to Phase 3, which effectively skips + * Phase 2. + * + * Intentional fall-through. + */ + case BLE_MESH_KR_PHASE_2: + BT_DBG("KR Phase 0x%02x -> Normal", sub->kr_phase); + bt_mesh_net_revoke_keys(sub); + if (IS_ENABLED(CONFIG_BLE_MESH_LOW_POWER) || + IS_ENABLED(CONFIG_BLE_MESH_FRIEND)) { + friend_cred_refresh(sub->net_idx); + } + sub->kr_phase = BLE_MESH_KR_NORMAL; + return true; + } + } + + return false; +} + +void bt_mesh_rpl_reset(void) +{ + int i; + + /* Discard "old old" IV Index entries from RPL and flag + * any other ones (which are valid) as old. + */ + for (i = 0; i < ARRAY_SIZE(bt_mesh.rpl); i++) { + struct bt_mesh_rpl *rpl = &bt_mesh.rpl[i]; + + if (rpl->src) { + if (rpl->old_iv) { + (void)memset(rpl, 0, sizeof(*rpl)); + } else { + rpl->old_iv = true; + } + } + } +} + +#if defined(CONFIG_BLE_MESH_IV_UPDATE_TEST) +void bt_mesh_iv_update_test(bool enable) +{ + bt_mesh_atomic_set_bit_to(bt_mesh.flags, BLE_MESH_IVU_TEST, enable); + /* Reset the duration variable - needed for some PTS tests */ + bt_mesh.ivu_duration = 0U; +} + +bool bt_mesh_iv_update(void) +{ + if (!bt_mesh_is_provisioned()) { + BT_ERR("%s, Not yet provisioned", __func__); + return false; + } + + if (bt_mesh_atomic_test_bit(bt_mesh.flags, BLE_MESH_IVU_IN_PROGRESS)) { + bt_mesh_net_iv_update(bt_mesh.iv_index, false); + } else { + bt_mesh_net_iv_update(bt_mesh.iv_index + 1, true); + } + + bt_mesh_net_sec_update(NULL); + + return bt_mesh_atomic_test_bit(bt_mesh.flags, BLE_MESH_IVU_IN_PROGRESS); +} +#endif /* CONFIG_BLE_MESH_IV_UPDATE_TEST */ + +/* Used for sending immediate beacons to Friend queues and GATT clients */ +void bt_mesh_net_sec_update(struct bt_mesh_subnet *sub) +{ + if (IS_ENABLED(CONFIG_BLE_MESH_FRIEND)) { + bt_mesh_friend_sec_update(sub ? sub->net_idx : BLE_MESH_KEY_ANY); + } + + if (IS_ENABLED(CONFIG_BLE_MESH_GATT_PROXY) && + bt_mesh_gatt_proxy_get() == BLE_MESH_GATT_PROXY_ENABLED) { +#if CONFIG_BLE_MESH_NODE + bt_mesh_proxy_beacon_send(sub); +#endif + } +} + +bool bt_mesh_net_iv_update(u32_t iv_index, bool iv_update) +{ + int i; + + if (bt_mesh_atomic_test_bit(bt_mesh.flags, BLE_MESH_IVU_IN_PROGRESS)) { + /* We're currently in IV Update mode */ + + if (iv_index != bt_mesh.iv_index) { + BT_WARN("IV Index mismatch: 0x%08x != 0x%08x", + iv_index, bt_mesh.iv_index); + return false; + } + + if (iv_update) { + /* Nothing to do */ + BT_DBG("Already in IV Update in Progress state"); + return false; + } + } else { + /* We're currently in Normal mode */ + + if (iv_index == bt_mesh.iv_index) { + BT_DBG("Same IV Index in normal mode"); + return false; + } + + if (iv_index < bt_mesh.iv_index || + iv_index > bt_mesh.iv_index + 42) { + BT_ERR("IV Index out of sync: 0x%08x != 0x%08x", + iv_index, bt_mesh.iv_index); + return false; + } + + if (iv_index > bt_mesh.iv_index + 1) { + BT_WARN("Performing IV Index Recovery"); + (void)memset(bt_mesh.rpl, 0, sizeof(bt_mesh.rpl)); + bt_mesh.iv_index = iv_index; + bt_mesh.seq = 0U; + goto do_update; + } + + if (iv_index == bt_mesh.iv_index + 1 && !iv_update) { + BT_WARN("Ignoring new index in normal mode"); + return false; + } + + if (!iv_update) { + /* Nothing to do */ + BT_DBG("Already in Normal state"); + return false; + } + } + + if (!(IS_ENABLED(CONFIG_BLE_MESH_IV_UPDATE_TEST) && + bt_mesh_atomic_test_bit(bt_mesh.flags, BLE_MESH_IVU_TEST))) { + if (bt_mesh.ivu_duration < BLE_MESH_IVU_MIN_HOURS) { + BT_WARN("IV Update before minimum duration"); + return false; + } + } + + /* Defer change to Normal Operation if there are pending acks */ + if (!iv_update && bt_mesh_tx_in_progress()) { + BT_WARN("IV Update deferred because of pending transfer"); + bt_mesh_atomic_set_bit(bt_mesh.flags, BLE_MESH_IVU_PENDING); + return false; + } + +do_update: + bt_mesh_atomic_set_bit_to(bt_mesh.flags, BLE_MESH_IVU_IN_PROGRESS, iv_update); + bt_mesh.ivu_duration = 0U; + + if (iv_update) { + bt_mesh.iv_index = iv_index; + BT_DBG("IV Update state entered. New index 0x%08x", + bt_mesh.iv_index); + + bt_mesh_rpl_reset(); + } else { + BT_DBG("Normal mode entered"); + bt_mesh.seq = 0U; + } + + k_delayed_work_submit(&bt_mesh.ivu_timer, BLE_MESH_IVU_TIMEOUT); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + if (bt_mesh.sub[i].net_idx != BLE_MESH_KEY_UNUSED) { + bt_mesh_net_beacon_update(&bt_mesh.sub[i]); + } + } + + if (IS_ENABLED(CONFIG_BLE_MESH_SETTINGS)) { + bt_mesh_store_iv(false); + } + + return true; +} + +u32_t bt_mesh_next_seq(void) +{ + u32_t seq = bt_mesh.seq++; + + if (IS_ENABLED(CONFIG_BLE_MESH_SETTINGS)) { + bt_mesh_store_seq(); + } + + return seq; +} + +int bt_mesh_net_resend(struct bt_mesh_subnet *sub, struct net_buf *buf, + bool new_key, const struct bt_mesh_send_cb *cb, + void *cb_data) +{ + const u8_t *enc, *priv; + u32_t seq; + int err; + + BT_DBG("net_idx 0x%04x new_key %u len %u", sub->net_idx, new_key, + buf->len); + + enc = sub->keys[new_key].enc; + priv = sub->keys[new_key].privacy; + + err = bt_mesh_net_obfuscate(buf->data, BLE_MESH_NET_IVI_TX, priv); + if (err) { + BT_ERR("%s, Deobfuscate failed (err %d)", __func__, err); + return err; + } + + err = bt_mesh_net_decrypt(enc, &buf->b, BLE_MESH_NET_IVI_TX, false); + if (err) { + BT_ERR("%s, Decrypt failed (err %d)", __func__, err); + return err; + } + + /* Update with a new sequence number */ + seq = bt_mesh_next_seq(); + buf->data[2] = seq >> 16; + buf->data[3] = seq >> 8; + buf->data[4] = seq; + + err = bt_mesh_net_encrypt(enc, &buf->b, BLE_MESH_NET_IVI_TX, false); + if (err) { + BT_ERR("%s, Encrypt failed (err %d)", __func__, err); + return err; + } + + err = bt_mesh_net_obfuscate(buf->data, BLE_MESH_NET_IVI_TX, priv); + if (err) { + BT_ERR("%s, Obfuscate failed (err %d)", __func__, err); + return err; + } + + bt_mesh_adv_send(buf, cb, cb_data); + + if (!bt_mesh_atomic_test_bit(bt_mesh.flags, BLE_MESH_IVU_IN_PROGRESS) && + bt_mesh.seq > IV_UPDATE_SEQ_LIMIT) { +#if CONFIG_BLE_MESH_NODE + bt_mesh_beacon_ivu_initiator(true); +#endif + bt_mesh_net_iv_update(bt_mesh.iv_index + 1, true); + bt_mesh_net_sec_update(NULL); + } + + return 0; +} + +static void bt_mesh_net_local(struct k_work *work) +{ + struct net_buf *buf; + + while ((buf = net_buf_slist_get(&bt_mesh.local_queue))) { + BT_DBG("len %u: %s", buf->len, bt_hex(buf->data, buf->len)); + bt_mesh_net_recv(&buf->b, 0, BLE_MESH_NET_IF_LOCAL); + net_buf_unref(buf); + } +} + +int bt_mesh_net_encode(struct bt_mesh_net_tx *tx, struct net_buf_simple *buf, + bool proxy) +{ + const bool ctl = (tx->ctx->app_idx == BLE_MESH_KEY_UNUSED); + u32_t seq_val; + u8_t nid; + const u8_t *enc, *priv; + u8_t *seq; + int err; + + if (ctl && net_buf_simple_tailroom(buf) < 8) { + BT_ERR("%s, Insufficient MIC space for CTL PDU", __func__); + return -EINVAL; + } else if (net_buf_simple_tailroom(buf) < 4) { + BT_ERR("%s, Insufficient MIC space for PDU", __func__); + return -EINVAL; + } + + BT_DBG("src 0x%04x dst 0x%04x ctl %u seq 0x%06x", + tx->src, tx->ctx->addr, ctl, bt_mesh.seq); + + net_buf_simple_push_be16(buf, tx->ctx->addr); + net_buf_simple_push_be16(buf, tx->src); + + seq = net_buf_simple_push(buf, 3); + seq_val = bt_mesh_next_seq(); + seq[0] = seq_val >> 16; + seq[1] = seq_val >> 8; + seq[2] = seq_val; + + if (ctl) { + net_buf_simple_push_u8(buf, tx->ctx->send_ttl | 0x80); + } else { + net_buf_simple_push_u8(buf, tx->ctx->send_ttl); + } + + if (IS_ENABLED(CONFIG_BLE_MESH_LOW_POWER) && tx->friend_cred) { + if (friend_cred_get(tx->sub, BLE_MESH_ADDR_UNASSIGNED, + &nid, &enc, &priv)) { + BT_WARN("Falling back to master credentials"); + + tx->friend_cred = 0U; + + nid = tx->sub->keys[tx->sub->kr_flag].nid; + enc = tx->sub->keys[tx->sub->kr_flag].enc; + priv = tx->sub->keys[tx->sub->kr_flag].privacy; + } + } else { + tx->friend_cred = 0U; + nid = tx->sub->keys[tx->sub->kr_flag].nid; + enc = tx->sub->keys[tx->sub->kr_flag].enc; + priv = tx->sub->keys[tx->sub->kr_flag].privacy; + } + + net_buf_simple_push_u8(buf, (nid | (BLE_MESH_NET_IVI_TX & 1) << 7)); + + err = bt_mesh_net_encrypt(enc, buf, BLE_MESH_NET_IVI_TX, proxy); + if (err) { + return err; + } + + return bt_mesh_net_obfuscate(buf->data, BLE_MESH_NET_IVI_TX, priv); +} + +int bt_mesh_net_send(struct bt_mesh_net_tx *tx, struct net_buf *buf, + const struct bt_mesh_send_cb *cb, void *cb_data) +{ + int err; + + BT_DBG("src 0x%04x dst 0x%04x len %u headroom %u tailroom %u", + tx->src, tx->ctx->addr, buf->len, net_buf_headroom(buf), + net_buf_tailroom(buf)); + BT_DBG("Payload len %u: %s", buf->len, bt_hex(buf->data, buf->len)); + BT_DBG("Seq 0x%06x", bt_mesh.seq); + + if (tx->ctx->send_ttl == BLE_MESH_TTL_DEFAULT) { + tx->ctx->send_ttl = bt_mesh_default_ttl_get(); + } + + err = bt_mesh_net_encode(tx, &buf->b, false); + if (err) { + goto done; + } + + /* Deliver to GATT Proxy Clients if necessary. Mesh spec 3.4.5.2: + * "The output filter of the interface connected to advertising or + * GATT bearers shall drop all messages with TTL value set to 1." + */ +#if CONFIG_BLE_MESH_NODE + if (bt_mesh_is_provisioned()) { + if (IS_ENABLED(CONFIG_BLE_MESH_GATT_PROXY) && + tx->ctx->send_ttl != 1U) { + if (bt_mesh_proxy_relay(&buf->b, tx->ctx->addr) && + BLE_MESH_ADDR_IS_UNICAST(tx->ctx->addr)) { + /* Notify completion if this only went + * through the Mesh Proxy. + */ + if (cb) { + if (cb->start) { + cb->start(0, 0, cb_data); + } + + if (cb->end) { + cb->end(0, cb_data); + } + } + + err = 0; + goto done; + } + } + } +#endif + + /* Deliver to local network interface if necessary */ + if (bt_mesh_fixed_group_match(tx->ctx->addr) || + bt_mesh_elem_find(tx->ctx->addr)) { + if (cb && cb->start) { + cb->start(0, 0, cb_data); + } + net_buf_slist_put(&bt_mesh.local_queue, net_buf_ref(buf)); + if (cb && cb->end) { + cb->end(0, cb_data); + } + k_work_submit(&bt_mesh.local_work); + } else if (tx->ctx->send_ttl != 1U) { + /* Deliver to the advertising network interface. Mesh spec + * 3.4.5.2: "The output filter of the interface connected to + * advertising or GATT bearers shall drop all messages with + * TTL value set to 1." + */ + bt_mesh_adv_send(buf, cb, cb_data); + } + +done: + net_buf_unref(buf); + return err; +} + +static bool auth_match(struct bt_mesh_subnet_keys *keys, + const u8_t net_id[8], u8_t flags, + u32_t iv_index, const u8_t auth[8]) +{ + u8_t net_auth[8]; + + if (memcmp(net_id, keys->net_id, 8)) { + return false; + } + + bt_mesh_beacon_auth(keys->beacon, flags, keys->net_id, iv_index, + net_auth); + + if (memcmp(auth, net_auth, 8)) { + BT_WARN("Authentication Value %s != %s", + bt_hex(auth, 8), bt_hex(net_auth, 8)); + return false; + } + + return true; +} + +struct bt_mesh_subnet *bt_mesh_subnet_find(const u8_t net_id[8], u8_t flags, + u32_t iv_index, const u8_t auth[8], + bool *new_key) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + struct bt_mesh_subnet *sub = &bt_mesh.sub[i]; + + if (sub->net_idx == BLE_MESH_KEY_UNUSED) { + continue; + } + + if (auth_match(&sub->keys[0], net_id, flags, iv_index, auth)) { + *new_key = false; + return sub; + } + + if (sub->kr_phase == BLE_MESH_KR_NORMAL) { + continue; + } + + if (auth_match(&sub->keys[1], net_id, flags, iv_index, auth)) { + *new_key = true; + return sub; + } + } + + return NULL; +} + +static int net_decrypt(struct bt_mesh_subnet *sub, const u8_t *enc, + const u8_t *priv, const u8_t *data, + size_t data_len, struct bt_mesh_net_rx *rx, + struct net_buf_simple *buf) +{ + BT_DBG("NID 0x%02x net_idx 0x%04x", NID(data), sub->net_idx); + BT_DBG("IVI %u net->iv_index 0x%08x", IVI(data), bt_mesh.iv_index); + + rx->old_iv = (IVI(data) != (bt_mesh.iv_index & 0x01)); + + net_buf_simple_reset(buf); + memcpy(net_buf_simple_add(buf, data_len), data, data_len); + + if (bt_mesh_net_obfuscate(buf->data, BLE_MESH_NET_IVI_RX(rx), priv)) { + return -ENOENT; + } + + /* TODO: For provisioner, when a device is re-provisioned and start to + * send the same message(e.g. cfg_appkey_add), the status message is easy + * to be filtered here. So when a device is re-provisioned, the related + * msg_cache should be cleared. Will do it later. + */ + if (rx->net_if == BLE_MESH_NET_IF_ADV && msg_cache_match(rx, buf)) { + BT_WARN("Duplicate found in Network Message Cache"); + return -EALREADY; + } + + rx->ctx.addr = SRC(buf->data); + if (!BLE_MESH_ADDR_IS_UNICAST(rx->ctx.addr)) { + BT_WARN("Ignoring non-unicast src addr 0x%04x", rx->ctx.addr); + return -EINVAL; + } + + BT_DBG("src 0x%04x", rx->ctx.addr); + +#if CONFIG_BLE_MESH_NODE + if (bt_mesh_is_provisioned()) { + if (IS_ENABLED(CONFIG_BLE_MESH_PROXY) && + rx->net_if == BLE_MESH_NET_IF_PROXY_CFG) { + return bt_mesh_net_decrypt(enc, buf, BLE_MESH_NET_IVI_RX(rx), + true); + } + } +#endif + + return bt_mesh_net_decrypt(enc, buf, BLE_MESH_NET_IVI_RX(rx), false); +} + +#if (defined(CONFIG_BLE_MESH_LOW_POWER) || \ + defined(CONFIG_BLE_MESH_FRIEND)) +static int friend_decrypt(struct bt_mesh_subnet *sub, const u8_t *data, + size_t data_len, struct bt_mesh_net_rx *rx, + struct net_buf_simple *buf) +{ + int i; + + BT_DBG("NID 0x%02x net_idx 0x%04x", NID(data), sub->net_idx); + + for (i = 0; i < ARRAY_SIZE(friend_cred); i++) { + struct friend_cred *cred = &friend_cred[i]; + + if (cred->net_idx != sub->net_idx) { + continue; + } + + if (NID(data) == cred->cred[0].nid && + !net_decrypt(sub, cred->cred[0].enc, cred->cred[0].privacy, + data, data_len, rx, buf)) { + return 0; + } + + if (sub->kr_phase == BLE_MESH_KR_NORMAL) { + continue; + } + + if (NID(data) == cred->cred[1].nid && + !net_decrypt(sub, cred->cred[1].enc, cred->cred[1].privacy, + data, data_len, rx, buf)) { + rx->new_key = 1U; + return 0; + } + } + + return -ENOENT; +} +#endif + +static bool net_find_and_decrypt(const u8_t *data, size_t data_len, + struct bt_mesh_net_rx *rx, + struct net_buf_simple *buf) +{ + struct bt_mesh_subnet *sub = NULL; + u32_t array_size = 0; + int i; + + BT_DBG("%s", __func__); + +#if CONFIG_BLE_MESH_NODE && !CONFIG_BLE_MESH_PROVISIONER + if (!bt_mesh_is_provisioner_en()) { + array_size = ARRAY_SIZE(bt_mesh.sub); + } +#endif + +#if !CONFIG_BLE_MESH_NODE && CONFIG_BLE_MESH_PROVISIONER + if (bt_mesh_is_provisioner_en()) { + array_size = ARRAY_SIZE(bt_mesh.p_sub); + } +#endif + +#if CONFIG_BLE_MESH_NODE && CONFIG_BLE_MESH_PROVISIONER + array_size = ARRAY_SIZE(bt_mesh.sub); + if (bt_mesh_is_provisioner_en()) { + array_size += ARRAY_SIZE(bt_mesh.p_sub); + } +#endif + + if (!array_size) { + BT_ERR("%s, Unable to get subnet size", __func__); + return false; + } + + for (i = 0; i < array_size; i++) { +#if CONFIG_BLE_MESH_NODE && !CONFIG_BLE_MESH_PROVISIONER + if (!bt_mesh_is_provisioner_en()) { + sub = &bt_mesh.sub[i]; + } +#endif + +#if !CONFIG_BLE_MESH_NODE && CONFIG_BLE_MESH_PROVISIONER + if (bt_mesh_is_provisioner_en()) { + sub = bt_mesh.p_sub[i]; + } +#endif + +#if CONFIG_BLE_MESH_NODE && CONFIG_BLE_MESH_PROVISIONER + if (i < ARRAY_SIZE(bt_mesh.sub)) { + sub = &bt_mesh.sub[i]; + } else { + sub = bt_mesh.p_sub[i - ARRAY_SIZE(bt_mesh.sub)]; + } +#endif + + if (!sub) { + BT_DBG("%s, NULL subnet", __func__); + continue; + } + + if (sub->net_idx == BLE_MESH_KEY_UNUSED) { + continue; + } + +#if CONFIG_BLE_MESH_NODE + if (bt_mesh_is_provisioned()) { +#if (defined(CONFIG_BLE_MESH_LOW_POWER) || defined(CONFIG_BLE_MESH_FRIEND)) + if (!friend_decrypt(sub, data, data_len, rx, buf)) { + rx->friend_cred = 1; + rx->ctx.net_idx = sub->net_idx; + rx->sub = sub; + return true; + } +#endif + } +#endif /* CONFIG_BLE_MESH_NODE */ + + if (NID(data) == sub->keys[0].nid && + !net_decrypt(sub, sub->keys[0].enc, sub->keys[0].privacy, + data, data_len, rx, buf)) { + rx->ctx.net_idx = sub->net_idx; + rx->sub = sub; + return true; + } + + if (sub->kr_phase == BLE_MESH_KR_NORMAL) { + continue; + } + + if (NID(data) == sub->keys[1].nid && + !net_decrypt(sub, sub->keys[1].enc, sub->keys[1].privacy, + data, data_len, rx, buf)) { + rx->new_key = 1U; + rx->ctx.net_idx = sub->net_idx; + rx->sub = sub; + return true; + } + } + + return false; +} + +/* Relaying from advertising to the advertising bearer should only happen + * if the Relay state is set to enabled. Locally originated packets always + * get sent to the advertising bearer. If the packet came in through GATT, + * then we should only relay it if the GATT Proxy state is enabled. + */ +#if CONFIG_BLE_MESH_NODE + +static bool relay_to_adv(enum bt_mesh_net_if net_if) +{ + switch (net_if) { + case BLE_MESH_NET_IF_LOCAL: + return true; + case BLE_MESH_NET_IF_ADV: + return (bt_mesh_relay_get() == BLE_MESH_RELAY_ENABLED); + case BLE_MESH_NET_IF_PROXY: + return (bt_mesh_gatt_proxy_get() == BLE_MESH_GATT_PROXY_ENABLED); + default: + return false; + } +} + +static void bt_mesh_net_relay(struct net_buf_simple *sbuf, + struct bt_mesh_net_rx *rx) +{ + const u8_t *enc, *priv; + struct net_buf *buf; + u8_t nid, transmit; + + if (rx->net_if == BLE_MESH_NET_IF_LOCAL) { + /* Locally originated PDUs with TTL=1 will only be delivered + * to local elements as per Mesh Profile 1.0 section 3.4.5.2: + * "The output filter of the interface connected to + * advertising or GATT bearers shall drop all messages with + * TTL value set to 1." + */ + if (rx->ctx.recv_ttl == 1U) { + return; + } + } else { + if (rx->ctx.recv_ttl <= 1U) { + return; + } + } + + if (rx->net_if == BLE_MESH_NET_IF_ADV && + bt_mesh_relay_get() != BLE_MESH_RELAY_ENABLED && + bt_mesh_gatt_proxy_get() != BLE_MESH_GATT_PROXY_ENABLED) { + return; + } + + BT_DBG("TTL %u CTL %u dst 0x%04x", rx->ctx.recv_ttl, rx->ctl, + rx->ctx.recv_dst); + + /* The Relay Retransmit state is only applied to adv-adv relaying. + * Anything else (like GATT to adv, or locally originated packets) + * use the Network Transmit state. + */ + if (rx->net_if == BLE_MESH_NET_IF_ADV) { + transmit = bt_mesh_relay_retransmit_get(); + } else { + transmit = bt_mesh_net_transmit_get(); + if (rx->net_if == BLE_MESH_NET_IF_PROXY && + transmit < BLE_MESH_TRANSMIT(5, 20)) { + /** + * Add this in case EspBleMesh APP just send a message once, and + * the Proxy Node will send this message using advertising bearer + * with duration not less than 180ms. + */ + transmit = BLE_MESH_TRANSMIT(5, 20); + } + } + + buf = bt_mesh_adv_create(BLE_MESH_ADV_DATA, transmit, K_NO_WAIT); + if (!buf) { + BT_ERR("%s, Out of relay buffers", __func__); + return; + } + + /* Only decrement TTL for non-locally originated packets */ + if (rx->net_if != BLE_MESH_NET_IF_LOCAL) { + /* Leave CTL bit intact */ + sbuf->data[1] &= 0x80; + sbuf->data[1] |= rx->ctx.recv_ttl - 1U; + } + + net_buf_add_mem(buf, sbuf->data, sbuf->len); + + enc = rx->sub->keys[rx->sub->kr_flag].enc; + priv = rx->sub->keys[rx->sub->kr_flag].privacy; + nid = rx->sub->keys[rx->sub->kr_flag].nid; + + BT_DBG("Relaying packet. TTL is now %u", TTL(buf->data)); + + /* Update NID if RX or RX was with friend credentials */ + if (rx->friend_cred) { + buf->data[0] &= 0x80; /* Clear everything except IVI */ + buf->data[0] |= nid; + } + + /* We re-encrypt and obfuscate using the received IVI rather than + * the normal TX IVI (which may be different) since the transport + * layer nonce includes the IVI. + */ + if (bt_mesh_net_encrypt(enc, &buf->b, BLE_MESH_NET_IVI_RX(rx), false)) { + BT_ERR("%s, Re-encrypting failed", __func__); + goto done; + } + + if (bt_mesh_net_obfuscate(buf->data, BLE_MESH_NET_IVI_RX(rx), priv)) { + BT_ERR("%s, Re-obfuscating failed", __func__); + goto done; + } + + /* Sending to the GATT bearer should only happen if GATT Proxy + * is enabled or the message originates from the local node. + */ + if (IS_ENABLED(CONFIG_BLE_MESH_GATT_PROXY) && + (bt_mesh_gatt_proxy_get() == BLE_MESH_GATT_PROXY_ENABLED || + rx->net_if == BLE_MESH_NET_IF_LOCAL)) { + if (bt_mesh_proxy_relay(&buf->b, rx->ctx.recv_dst) && + BLE_MESH_ADDR_IS_UNICAST(rx->ctx.recv_dst)) { + goto done; + } + } + + if (relay_to_adv(rx->net_if)) { + bt_mesh_adv_send(buf, NULL, NULL); + } + +done: + net_buf_unref(buf); +} + +#endif /* CONFIG_BLE_MESH_NODE */ + +int bt_mesh_net_decode(struct net_buf_simple *data, enum bt_mesh_net_if net_if, + struct bt_mesh_net_rx *rx, struct net_buf_simple *buf) +{ + if (data->len < BLE_MESH_NET_MIN_PDU_LEN) { + BT_WARN("Dropping too short mesh packet (len %u)", data->len); + BT_WARN("%s", bt_hex(data->data, data->len)); + return -EINVAL; + } + + if (net_if == BLE_MESH_NET_IF_ADV && check_dup(data)) { + return -EINVAL; + } + + BT_DBG("%u bytes: %s", data->len, bt_hex(data->data, data->len)); + + rx->net_if = net_if; + + if (!net_find_and_decrypt(data->data, data->len, rx, buf)) { + BT_DBG("Unable to find matching net for packet"); + return -ENOENT; + } + + /* Initialize AppIdx to a sane value */ + rx->ctx.app_idx = BLE_MESH_KEY_UNUSED; + + rx->ctx.recv_ttl = TTL(buf->data); + + /* Default to responding with TTL 0 for non-routed messages */ + if (rx->ctx.recv_ttl == 0U) { + rx->ctx.send_ttl = 0U; + } else { + rx->ctx.send_ttl = BLE_MESH_TTL_DEFAULT; + } + + rx->ctl = CTL(buf->data); + rx->seq = SEQ(buf->data); + rx->ctx.recv_dst = DST(buf->data); + + BT_DBG("Decryption successful. Payload len %u", buf->len); + + if (net_if != BLE_MESH_NET_IF_PROXY_CFG && + rx->ctx.recv_dst == BLE_MESH_ADDR_UNASSIGNED) { + BT_ERR("%s, Destination address is unassigned; dropping packet", __func__); + return -EBADMSG; + } + + if (BLE_MESH_ADDR_IS_RFU(rx->ctx.recv_dst)) { + BT_ERR("%s, Destination address is RFU; dropping packet", __func__); + return -EBADMSG; + } + + if (net_if != BLE_MESH_NET_IF_LOCAL && bt_mesh_elem_find(rx->ctx.addr)) { + BT_DBG("Dropping locally originated packet"); + return -EBADMSG; + } + + BT_DBG("src 0x%04x dst 0x%04x ttl %u", rx->ctx.addr, rx->ctx.recv_dst, + rx->ctx.recv_ttl); + BT_DBG("PDU: %s", bt_hex(buf->data, buf->len)); + + return 0; +} + +void bt_mesh_net_recv(struct net_buf_simple *data, s8_t rssi, + enum bt_mesh_net_if net_if) +{ + NET_BUF_SIMPLE_DEFINE(buf, 29); + struct bt_mesh_net_rx rx = { .rssi = rssi }; + struct net_buf_simple_state state; + + BT_DBG("rssi %d net_if %u", rssi, net_if); + +#if CONFIG_BLE_MESH_NODE + if (!bt_mesh_is_provisioner_en()) { + if (!bt_mesh_is_provisioned()) { + return; + } + } +#endif + +#if !CONFIG_BLE_MESH_NODE && CONFIG_BLE_MESH_PROVISIONER + if (!bt_mesh_is_provisioner_en()) { + BT_WARN("%s, Provisioner is disabled", __func__); + return; + } + if (!provisioner_get_prov_node_count()) { + return; + } +#endif + + if (bt_mesh_net_decode(data, net_if, &rx, &buf)) { + return; + } + + /* Save the state so the buffer can later be relayed */ + net_buf_simple_save(&buf, &state); + +#if CONFIG_BLE_MESH_NODE + if (bt_mesh_is_provisioned()) { + if (IS_ENABLED(CONFIG_BLE_MESH_GATT_PROXY) && + net_if == BLE_MESH_NET_IF_PROXY) { + bt_mesh_proxy_addr_add(data, rx.ctx.addr); + } + } +#endif + + rx.local_match = (bt_mesh_fixed_group_match(rx.ctx.recv_dst) || + bt_mesh_elem_find(rx.ctx.recv_dst)); + + bt_mesh_trans_recv(&buf, &rx); + + /* Relay if this was a group/virtual address, or if the destination + * was neither a local element nor an LPN we're Friends for. + */ +#if CONFIG_BLE_MESH_NODE + if (bt_mesh_is_provisioned()) { + if (!BLE_MESH_ADDR_IS_UNICAST(rx.ctx.recv_dst) || + (!rx.local_match && !rx.friend_match)) { + net_buf_simple_restore(&buf, &state); + bt_mesh_net_relay(&buf, &rx); + } + } +#endif +} + +static void ivu_refresh(struct k_work *work) +{ + bt_mesh.ivu_duration += BLE_MESH_IVU_HOURS; + + BT_DBG("%s for %u hour%s", + bt_mesh_atomic_test_bit(bt_mesh.flags, BLE_MESH_IVU_IN_PROGRESS) ? + "IVU in Progress" : "IVU Normal mode", + bt_mesh.ivu_duration, bt_mesh.ivu_duration == 1U ? "" : "s"); + + if (bt_mesh.ivu_duration < BLE_MESH_IVU_MIN_HOURS) { + if (IS_ENABLED(CONFIG_BLE_MESH_SETTINGS)) { + bt_mesh_store_iv(true); + } + + k_delayed_work_submit(&bt_mesh.ivu_timer, BLE_MESH_IVU_TIMEOUT); + return; + } + + if (bt_mesh_atomic_test_bit(bt_mesh.flags, BLE_MESH_IVU_IN_PROGRESS)) { +#if CONFIG_BLE_MESH_NODE + bt_mesh_beacon_ivu_initiator(true); +#endif + bt_mesh_net_iv_update(bt_mesh.iv_index, false); + } else if (IS_ENABLED(CONFIG_BLE_MESH_SETTINGS)) { + bt_mesh_store_iv(true); + } +} + +#if defined(CONFIG_BLE_MESH_NODE) +void bt_mesh_net_start(void) +{ + if (bt_mesh_beacon_get() == BLE_MESH_BEACON_ENABLED) { + bt_mesh_beacon_enable(); + } else { + bt_mesh_beacon_disable(); + } + + if (IS_ENABLED(CONFIG_BLE_MESH_GATT_PROXY) && + bt_mesh_gatt_proxy_get() != BLE_MESH_GATT_PROXY_NOT_SUPPORTED) { + bt_mesh_proxy_gatt_enable(); + bt_mesh_adv_update(); + } + +#if defined(CONFIG_BLE_MESH_USE_DUPLICATE_SCAN) + /* Add Mesh beacon type (Secure Network Beacon) to the exceptional list */ + bt_mesh_update_exceptional_list(BLE_MESH_EXCEP_LIST_ADD, + BLE_MESH_EXCEP_INFO_MESH_BEACON, NULL); +#endif + + if (IS_ENABLED(CONFIG_BLE_MESH_LOW_POWER)) { + /* TODO: Enable duplicate scan in Low Power Mode */ + bt_mesh_lpn_init(); + } else { + bt_mesh_scan_enable(); + } + + if (IS_ENABLED(CONFIG_BLE_MESH_FRIEND)) { + bt_mesh_friend_init(); + } + + if (IS_ENABLED(CONFIG_BLE_MESH_PROV)) { + u16_t net_idx = bt_mesh.sub[0].net_idx; + u16_t addr = bt_mesh_primary_addr(); + u32_t iv_index = bt_mesh.iv_index; + u8_t flags = (u8_t)bt_mesh.sub[0].kr_flag; + if (bt_mesh_atomic_test_bit(bt_mesh.flags, BLE_MESH_IVU_IN_PROGRESS)) { + flags |= BLE_MESH_NET_FLAG_IVU; + } + + bt_mesh_prov_complete(net_idx, addr, flags, iv_index); + } +} +#endif + +void bt_mesh_net_init(void) +{ + k_delayed_work_init(&bt_mesh.ivu_timer, ivu_refresh); + + k_work_init(&bt_mesh.local_work, bt_mesh_net_local); +} diff --git a/components/bt/ble_mesh/mesh_core/net.h b/components/bt/ble_mesh/mesh_core/net.h new file mode 100644 index 0000000000..90515fd2ca --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/net.h @@ -0,0 +1,388 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _NET_H_ +#define _NET_H_ + +#include "mesh_util.h" +#include "mesh_kernel.h" +#include "mesh_access.h" + +#define BLE_MESH_NET_FLAG_KR BIT(0) +#define BLE_MESH_NET_FLAG_IVU BIT(1) + +#define BLE_MESH_KR_NORMAL 0x00 +#define BLE_MESH_KR_PHASE_1 0x01 +#define BLE_MESH_KR_PHASE_2 0x02 +#define BLE_MESH_KR_PHASE_3 0x03 + +#define BLE_MESH_IV_UPDATE(flags) ((flags >> 1) & 0x01) +#define BLE_MESH_KEY_REFRESH(flags) (flags & 0x01) + +/* How many hours in between updating IVU duration */ +#define BLE_MESH_IVU_MIN_HOURS 96 +#define BLE_MESH_IVU_HOURS (BLE_MESH_IVU_MIN_HOURS / \ + CONFIG_BLE_MESH_IVU_DIVIDER) +#define BLE_MESH_IVU_TIMEOUT K_HOURS(BLE_MESH_IVU_HOURS) + +struct bt_mesh_app_key { + u16_t net_idx; + u16_t app_idx; + bool updated; + struct bt_mesh_app_keys { + u8_t id; + u8_t val[16]; + } keys[2]; +}; + +struct bt_mesh_subnet { + u32_t beacon_sent; /* Timestamp of last sent beacon */ + u8_t beacons_last; /* Number of beacons during last + * observation window + */ + u8_t beacons_cur; /* Number of beaconds observed during + * currently ongoing window. + */ + + u8_t beacon_cache[21]; /* Cached last authenticated beacon */ + + u16_t net_idx; /* NetKeyIndex */ + + bool kr_flag; /* Key Refresh Flag */ + u8_t kr_phase; /* Key Refresh Phase */ + + u8_t node_id; /* Node Identity State */ + u32_t node_id_start; /* Node Identity started timestamp */ + + u8_t auth[8]; /* Beacon Authentication Value */ + + struct bt_mesh_subnet_keys { + u8_t net[16]; /* NetKey */ + u8_t nid; /* NID */ + u8_t enc[16]; /* EncKey */ + u8_t net_id[8]; /* Network ID */ +#if defined(CONFIG_BLE_MESH_GATT_PROXY) + u8_t identity[16]; /* IdentityKey */ +#endif + u8_t privacy[16]; /* PrivacyKey */ + u8_t beacon[16]; /* BeaconKey */ + } keys[2]; +}; + +struct bt_mesh_rpl { + u16_t src; + bool old_iv; +#if defined(CONFIG_BLE_MESH_SETTINGS) + bool store; +#endif + u32_t seq; +}; + +#if defined(CONFIG_BLE_MESH_FRIEND) +#define FRIEND_SEG_RX CONFIG_BLE_MESH_FRIEND_SEG_RX +#define FRIEND_SUB_LIST_SIZE CONFIG_BLE_MESH_FRIEND_SUB_LIST_SIZE +#else +#define FRIEND_SEG_RX 0 +#define FRIEND_SUB_LIST_SIZE 0 +#endif + +struct bt_mesh_friend { + u16_t lpn; + u8_t recv_delay; + u8_t fsn: 1, + send_last: 1, + pending_req: 1, + sec_update: 1, + pending_buf: 1, + valid: 1, + established: 1; + s32_t poll_to; + u8_t num_elem; + u16_t lpn_counter; + u16_t counter; + + u16_t net_idx; + + u16_t sub_list[FRIEND_SUB_LIST_SIZE]; + + struct k_delayed_work timer; + + struct bt_mesh_friend_seg { + sys_slist_t queue; + } seg[FRIEND_SEG_RX]; + + struct net_buf *last; + + sys_slist_t queue; + u32_t queue_size; + + /* Friend Clear Procedure */ + struct { + u32_t start; /* Clear Procedure start */ + u16_t frnd; /* Previous Friend's address */ + u16_t repeat_sec; /* Repeat timeout in seconds */ + struct k_delayed_work timer; /* Repeat timer */ + } clear; +}; + +#if defined(CONFIG_BLE_MESH_LOW_POWER) +#define LPN_GROUPS CONFIG_BLE_MESH_LPN_GROUPS +#else +#define LPN_GROUPS 0 +#endif + +/* Low Power Node state */ +struct bt_mesh_lpn { + enum __packed { + BLE_MESH_LPN_DISABLED, /* LPN feature is disabled */ + BLE_MESH_LPN_CLEAR, /* Clear in progress */ + BLE_MESH_LPN_TIMER, /* Waiting for auto timer expiry */ + BLE_MESH_LPN_ENABLED, /* LPN enabled, but no Friend */ + BLE_MESH_LPN_REQ_WAIT, /* Wait before scanning for offers */ + BLE_MESH_LPN_WAIT_OFFER, /* Friend Req sent */ + BLE_MESH_LPN_ESTABLISHED, /* Friendship established */ + BLE_MESH_LPN_RECV_DELAY, /* Poll sent, waiting ReceiveDelay */ + BLE_MESH_LPN_WAIT_UPDATE, /* Waiting for Update or message */ + BLE_MESH_LPN_OFFER_RECV, /* Friend offer received */ + } state; + + /* Transaction Number (used for subscription list) */ + u8_t xact_next; + u8_t xact_pending; + u8_t sent_req; + + /* Address of our Friend when we're a LPN. Unassigned if we don't + * have a friend yet. + */ + u16_t frnd; + + /* Value from the friend offer */ + u8_t recv_win; + + u8_t req_attempts; /* Number of Request attempts */ + + s32_t poll_timeout; + + u8_t groups_changed: 1, /* Friend Subscription List needs updating */ + pending_poll: 1, /* Poll to be sent after subscription */ + disable: 1, /* Disable LPN after clearing */ + fsn: 1, /* Friend Sequence Number */ + established: 1, /* Friendship established */ + clear_success: 1; /* Friend Clear Confirm received */ + + /* Friend Queue Size */ + u8_t queue_size; + + /* LPNCounter */ + u16_t counter; + + /* Previous Friend of this LPN */ + u16_t old_friend; + + /* Duration reported for last advertising packet */ + u16_t adv_duration; + + /* Next LPN related action timer */ + struct k_delayed_work timer; + + /* Subscribed groups */ + u16_t groups[LPN_GROUPS]; + + /* Bit fields for tracking which groups the Friend knows about */ + BLE_MESH_ATOMIC_DEFINE(added, LPN_GROUPS); + BLE_MESH_ATOMIC_DEFINE(pending, LPN_GROUPS); + BLE_MESH_ATOMIC_DEFINE(to_remove, LPN_GROUPS); +}; + +/* bt_mesh_net.flags */ +enum { + BLE_MESH_VALID, /* We have been provisioned */ + BLE_MESH_SUSPENDED, /* Network is temporarily suspended */ + BLE_MESH_IVU_IN_PROGRESS, /* IV Update in Progress */ + BLE_MESH_IVU_INITIATOR, /* IV Update initiated by us */ + BLE_MESH_IVU_TEST, /* IV Update test mode */ + BLE_MESH_IVU_PENDING, /* Update blocked by SDU in progress */ + + /* pending storage actions */ + BLE_MESH_RPL_PENDING, + BLE_MESH_KEYS_PENDING, + BLE_MESH_NET_PENDING, + BLE_MESH_IV_PENDING, + BLE_MESH_SEQ_PENDING, + BLE_MESH_HB_PUB_PENDING, + BLE_MESH_CFG_PENDING, + BLE_MESH_MOD_PENDING, + + /* Don't touch - intentionally last */ + BLE_MESH_FLAG_COUNT, +}; + +struct bt_mesh_net { + u32_t iv_index; /* Current IV Index */ + u32_t seq; /* Next outgoing sequence number (24 bits) */ + + BLE_MESH_ATOMIC_DEFINE(flags, BLE_MESH_FLAG_COUNT); + + /* Local network interface */ + struct k_work local_work; + sys_slist_t local_queue; + +#if defined(CONFIG_BLE_MESH_FRIEND) + /* Friend state, unique for each LPN that we're Friends for */ + struct bt_mesh_friend frnd[CONFIG_BLE_MESH_FRIEND_LPN_COUNT]; +#endif + +#if defined(CONFIG_BLE_MESH_LOW_POWER) + struct bt_mesh_lpn lpn; /* Low Power Node state */ +#endif + + /* Number of hours in current IV Update state */ + u8_t ivu_duration; + + /* Timer to track duration in current IV Update state */ + struct k_delayed_work ivu_timer; + + u8_t dev_key[16]; + + struct bt_mesh_app_key app_keys[CONFIG_BLE_MESH_APP_KEY_COUNT]; + + struct bt_mesh_subnet sub[CONFIG_BLE_MESH_SUBNET_COUNT]; + + struct bt_mesh_rpl rpl[CONFIG_BLE_MESH_CRPL]; + +#if defined(CONFIG_BLE_MESH_PROVISIONER) + /* Application keys stored by provisioner */ + struct bt_mesh_app_key *p_app_keys[CONFIG_BLE_MESH_PROVISIONER_APP_KEY_COUNT]; + /* Next app_idx can be assigned */ + u16_t p_app_idx_next; + + /* Network keys stored by provisioner */ + struct bt_mesh_subnet *p_sub[CONFIG_BLE_MESH_PROVISIONER_SUBNET_COUNT]; + /* Next net_idx can be assigned */ + u16_t p_net_idx_next; +#endif +}; + +/* Network interface */ +enum bt_mesh_net_if { + BLE_MESH_NET_IF_ADV, + BLE_MESH_NET_IF_LOCAL, + BLE_MESH_NET_IF_PROXY, + BLE_MESH_NET_IF_PROXY_CFG, +}; + +/* Decoding context for Network/Transport data */ +struct bt_mesh_net_rx { + struct bt_mesh_subnet *sub; + struct bt_mesh_msg_ctx ctx; + u32_t seq; /* Sequence Number */ + u8_t old_iv: 1, /* iv_index - 1 was used */ + new_key: 1, /* Data was encrypted with updated key */ + friend_cred: 1, /* Data was encrypted with friend cred */ + ctl: 1, /* Network Control */ + net_if: 2, /* Network interface */ + local_match: 1, /* Matched a local element */ + friend_match: 1; /* Matched an LPN we're friends for */ + s8_t rssi; +}; + +/* Encoding context for Network/Transport data */ +struct bt_mesh_net_tx { + struct bt_mesh_subnet *sub; + struct bt_mesh_msg_ctx *ctx; + u16_t src; + u8_t xmit; + u8_t friend_cred: 1, + aszmic: 1, + aid: 6; +}; + +extern struct bt_mesh_net bt_mesh; + +#define BLE_MESH_NET_IVI_TX (bt_mesh.iv_index - \ + bt_mesh_atomic_test_bit(bt_mesh.flags, \ + BLE_MESH_IVU_IN_PROGRESS)) +#define BLE_MESH_NET_IVI_RX(rx) (bt_mesh.iv_index - (rx)->old_iv) + +#define BLE_MESH_NET_HDR_LEN 9 + +int bt_mesh_net_keys_create(struct bt_mesh_subnet_keys *keys, + const u8_t key[16]); + +int bt_mesh_net_create(u16_t idx, u8_t flags, const u8_t key[16], + u32_t iv_index); + +u8_t bt_mesh_net_flags(struct bt_mesh_subnet *sub); + +bool bt_mesh_kr_update(struct bt_mesh_subnet *sub, u8_t new_kr, bool new_key); + +void bt_mesh_net_revoke_keys(struct bt_mesh_subnet *sub); + +int bt_mesh_net_beacon_update(struct bt_mesh_subnet *sub); + +void bt_mesh_rpl_reset(void); + +bool bt_mesh_net_iv_update(u32_t iv_index, bool iv_update); + +void bt_mesh_net_sec_update(struct bt_mesh_subnet *sub); + +struct bt_mesh_subnet *bt_mesh_subnet_get(u16_t net_idx); + +struct bt_mesh_subnet *bt_mesh_subnet_find(const u8_t net_id[8], u8_t flags, + u32_t iv_index, const u8_t auth[8], + bool *new_key); + +int bt_mesh_net_encode(struct bt_mesh_net_tx *tx, struct net_buf_simple *buf, + bool proxy); + +int bt_mesh_net_send(struct bt_mesh_net_tx *tx, struct net_buf *buf, + const struct bt_mesh_send_cb *cb, void *cb_data); + +int bt_mesh_net_resend(struct bt_mesh_subnet *sub, struct net_buf *buf, + bool new_key, const struct bt_mesh_send_cb *cb, + void *cb_data); + +int bt_mesh_net_decode(struct net_buf_simple *data, enum bt_mesh_net_if net_if, + struct bt_mesh_net_rx *rx, struct net_buf_simple *buf); + +void bt_mesh_net_recv(struct net_buf_simple *data, s8_t rssi, + enum bt_mesh_net_if net_if); + +u32_t bt_mesh_next_seq(void); + +void bt_mesh_net_start(void); + +void bt_mesh_net_init(void); + +/* Friendship Credential Management */ +struct friend_cred { + u16_t net_idx; + u16_t addr; + + u16_t lpn_counter; + u16_t frnd_counter; + + struct { + u8_t nid; /* NID */ + u8_t enc[16]; /* EncKey */ + u8_t privacy[16]; /* PrivacyKey */ + } cred[2]; +}; + +int friend_cred_get(struct bt_mesh_subnet *sub, u16_t addr, u8_t *nid, + const u8_t **enc, const u8_t **priv); +int friend_cred_set(struct friend_cred *cred, u8_t idx, const u8_t net_key[16]); +void friend_cred_refresh(u16_t net_idx); +int friend_cred_update(struct bt_mesh_subnet *sub); +struct friend_cred *friend_cred_create(struct bt_mesh_subnet *sub, u16_t addr, + u16_t lpn_counter, u16_t frnd_counter); +void friend_cred_clear(struct friend_cred *cred); +int friend_cred_del(u16_t net_idx, u16_t addr); + +#endif /* _NET_H_ */ diff --git a/components/bt/ble_mesh/mesh_core/prov.c b/components/bt/ble_mesh/mesh_core/prov.c new file mode 100644 index 0000000000..0a3329ff65 --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/prov.c @@ -0,0 +1,1774 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include + +#include "sdkconfig.h" +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BLE_MESH_DEBUG_PROV) + +#include "mesh_util.h" +#include "mesh_main.h" +#include "mesh_uuid.h" +#include "mesh_trace.h" +#include "mesh_proxy.h" + +#include "crypto.h" +#include "adv.h" +#include "mesh.h" +#include "net.h" +#include "access.h" +#include "foundation.h" +#include "proxy.h" +#include "prov.h" + +#if CONFIG_BLE_MESH_NODE + +/* 3 transmissions, 20ms interval */ +#define PROV_XMIT BLE_MESH_TRANSMIT(2, 20) + +#define AUTH_METHOD_NO_OOB 0x00 +#define AUTH_METHOD_STATIC 0x01 +#define AUTH_METHOD_OUTPUT 0x02 +#define AUTH_METHOD_INPUT 0x03 + +#define OUTPUT_OOB_BLINK 0x00 +#define OUTPUT_OOB_BEEP 0x01 +#define OUTPUT_OOB_VIBRATE 0x02 +#define OUTPUT_OOB_NUMBER 0x03 +#define OUTPUT_OOB_STRING 0x04 + +#define INPUT_OOB_PUSH 0x00 +#define INPUT_OOB_TWIST 0x01 +#define INPUT_OOB_NUMBER 0x02 +#define INPUT_OOB_STRING 0x03 + +#define PROV_ERR_NONE 0x00 +#define PROV_ERR_NVAL_PDU 0x01 +#define PROV_ERR_NVAL_FMT 0x02 +#define PROV_ERR_UNEXP_PDU 0x03 +#define PROV_ERR_CFM_FAILED 0x04 +#define PROV_ERR_RESOURCES 0x05 +#define PROV_ERR_DECRYPT 0x06 +#define PROV_ERR_UNEXP_ERR 0x07 +#define PROV_ERR_ADDR 0x08 + +#define PROV_INVITE 0x00 +#define PROV_CAPABILITIES 0x01 +#define PROV_START 0x02 +#define PROV_PUB_KEY 0x03 +#define PROV_INPUT_COMPLETE 0x04 +#define PROV_CONFIRM 0x05 +#define PROV_RANDOM 0x06 +#define PROV_DATA 0x07 +#define PROV_COMPLETE 0x08 +#define PROV_FAILED 0x09 + +#define PROV_ALG_P256 0x00 + +#define GPCF(gpc) (gpc & 0x03) +#define GPC_START(last_seg) (((last_seg) << 2) | 0x00) +#define GPC_ACK 0x01 +#define GPC_CONT(seg_id) (((seg_id) << 2) | 0x02) +#define GPC_CTL(op) (((op) << 2) | 0x03) + +#define START_PAYLOAD_MAX 20 +#define CONT_PAYLOAD_MAX 23 + +#define START_LAST_SEG(gpc) (gpc >> 2) +#define CONT_SEG_INDEX(gpc) (gpc >> 2) + +#define BEARER_CTL(gpc) (gpc >> 2) +#define LINK_OPEN 0x00 +#define LINK_ACK 0x01 +#define LINK_CLOSE 0x02 + +#define CLOSE_REASON_SUCCESS 0x00 +#define CLOSE_REASON_TIMEOUT 0x01 +#define CLOSE_REASON_FAILED 0x02 + +#define XACT_SEG_DATA(_seg) (&link.rx.buf->data[20 + ((_seg - 1) * 23)]) +#define XACT_SEG_RECV(_seg) (link.rx.seg &= ~(1 << (_seg))) + +#define XACT_NVAL 0xff + +enum { + REMOTE_PUB_KEY, /* Remote key has been received */ + OOB_PUB_KEY, /* OOB public key is available */ + LINK_ACTIVE, /* Link has been opened */ + HAVE_DHKEY, /* DHKey has been calcualted */ + SEND_CONFIRM, /* Waiting to send Confirm value */ + WAIT_NUMBER, /* Waiting for number input from user */ + WAIT_STRING, /* Waiting for string input from user */ + TIMEOUT_START, /* Provision timeout timer has started */ + + NUM_FLAGS, +}; + +struct prov_link { + BLE_MESH_ATOMIC_DEFINE(flags, NUM_FLAGS); +#if defined(CONFIG_BLE_MESH_PB_GATT) + struct bt_mesh_conn *conn; /* GATT connection */ +#endif + u8_t dhkey[32]; /* Calculated DHKey */ + u8_t expect; /* Next expected PDU */ + + bool oob_pk_flag; /* Flag indicates whether using OOB public key */ + + u8_t oob_method; + u8_t oob_action; + u8_t oob_size; + + u8_t conf[16]; /* Remote Confirmation */ + u8_t rand[16]; /* Local Random */ + u8_t auth[16]; /* Authentication Value */ + + u8_t conf_salt[16]; /* ConfirmationSalt */ + u8_t conf_key[16]; /* ConfirmationKey */ + u8_t conf_inputs[145]; /* ConfirmationInputs */ + u8_t prov_salt[16]; /* Provisioning Salt */ + +#if defined(CONFIG_BLE_MESH_PB_ADV) + u32_t id; /* Link ID */ + u8_t tx_pdu_type; /* The previously transmitted Provisioning PDU type */ + + struct { + u8_t id; /* Transaction ID */ + u8_t prev_id; /* Previous Transaction ID */ + u8_t seg; /* Bit-field of unreceived segments */ + u8_t last_seg; /* Last segment (to check length) */ + u8_t fcs; /* Expected FCS value */ + struct net_buf_simple *buf; + } rx; + + struct { + /* Start timestamp of the transaction */ + s64_t start; + + /* Transaction id*/ + u8_t id; + + /* Pending outgoing buffer(s) */ + struct net_buf *buf[3]; + + /* Retransmit timer */ + struct k_delayed_work retransmit; + } tx; +#endif + + /* Provision timeout timer */ + struct k_delayed_work timeout; +}; + +struct prov_rx { + u32_t link_id; + u8_t xact_id; + u8_t gpc; +}; + +#define BUF_TIMEOUT K_MSEC(400) + +#if defined(CONFIG_BLE_MESH_FAST_PROV) +#define RETRANSMIT_TIMEOUT K_MSEC(360) +#define TRANSACTION_TIMEOUT K_SECONDS(3) +#define PROVISION_TIMEOUT K_SECONDS(6) +#else +#define RETRANSMIT_TIMEOUT K_MSEC(500) +#define TRANSACTION_TIMEOUT K_SECONDS(30) +#define PROVISION_TIMEOUT K_SECONDS(60) +#endif /* CONFIG_BLE_MESH_FAST_PROV */ + +#if defined(CONFIG_BLE_MESH_PB_GATT) +#define PROV_BUF_HEADROOM 5 +#else +#define PROV_BUF_HEADROOM 0 +NET_BUF_SIMPLE_DEFINE_STATIC(rx_buf, 65); +#endif + +#define PROV_BUF(name, len) \ + NET_BUF_SIMPLE_DEFINE(name, PROV_BUF_HEADROOM + len) + +static struct prov_link link; + +static const struct bt_mesh_prov *prov; + +static void close_link(u8_t err, u8_t reason); + +static void reset_state(void) +{ + /* Disable Attention Timer if it was set */ + if (link.conf_inputs[0]) { + bt_mesh_attention(NULL, 0); + } + +#if defined(CONFIG_BLE_MESH_PB_GATT) + if (link.conn) { + bt_mesh_conn_unref(link.conn); + } +#endif + +#if defined(CONFIG_BLE_MESH_PB_ADV) + /* Clear everything except the retransmit delayed work config */ + (void)memset(&link, 0, offsetof(struct prov_link, tx.retransmit)); + link.rx.prev_id = XACT_NVAL; + +#if defined(CONFIG_BLE_MESH_PB_GATT) + link.rx.buf = bt_mesh_proxy_get_buf(); +#else + net_buf_simple_reset(&rx_buf); + link.rx.buf = &rx_buf; +#endif /* PB_GATT */ + +#else + (void)memset(&link, 0, offsetof(struct prov_link, timeout)); +#endif /* PB_ADV */ +} + +#if defined(CONFIG_BLE_MESH_PB_ADV) +static void buf_sent(int err, void *user_data) +{ + if (!link.tx.buf[0]) { + return; + } + + k_delayed_work_submit(&link.tx.retransmit, RETRANSMIT_TIMEOUT); +} + +static struct bt_mesh_send_cb buf_sent_cb = { + .end = buf_sent, +}; + +static void free_segments(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(link.tx.buf); i++) { + struct net_buf *buf = link.tx.buf[i]; + + if (!buf) { + break; + } + + link.tx.buf[i] = NULL; + /* Mark as canceled */ + BLE_MESH_ADV(buf)->busy = 0U; + /** Changed by Espressif. Add this to avoid buf->ref is 2 which will + * cause lack of buf. + */ + if (buf->ref > 1) { + buf->ref = 1; + } + net_buf_unref(buf); + } +} + +static void prov_clear_tx(void) +{ + BT_DBG("%s", __func__); + + k_delayed_work_cancel(&link.tx.retransmit); + + free_segments(); +} + +static void reset_link(void) +{ + prov_clear_tx(); + + if (bt_mesh_atomic_test_and_clear_bit(link.flags, TIMEOUT_START)) { + k_delayed_work_cancel(&link.timeout); + } + + if (prov->link_close) { + prov->link_close(BLE_MESH_PROV_ADV); + } + +#if defined(CONFIG_BLE_MESH_USE_DUPLICATE_SCAN) + /* Remove the link id from exceptional list */ + bt_mesh_update_exceptional_list(BLE_MESH_EXCEP_LIST_REMOVE, + BLE_MESH_EXCEP_INFO_MESH_LINK_ID, &link.id); +#endif + + reset_state(); +} + +static struct net_buf *adv_buf_create(void) +{ + struct net_buf *buf; + + buf = bt_mesh_adv_create(BLE_MESH_ADV_PROV, PROV_XMIT, BUF_TIMEOUT); + if (!buf) { + BT_ERR("%s, Out of provisioning buffers", __func__); + return NULL; + } + + return buf; +} + +static u8_t pending_ack = XACT_NVAL; + +static void ack_complete(u16_t duration, int err, void *user_data) +{ + BT_DBG("xact %u complete", (u8_t)pending_ack); + pending_ack = XACT_NVAL; +} + +static void gen_prov_ack_send(u8_t xact_id) +{ + static const struct bt_mesh_send_cb cb = { + .start = ack_complete, + }; + const struct bt_mesh_send_cb *complete; + struct net_buf *buf; + + BT_DBG("xact_id %u", xact_id); + + if (pending_ack == xact_id) { + BT_DBG("Not sending duplicate ack"); + return; + } + + buf = adv_buf_create(); + if (!buf) { + return; + } + + if (pending_ack == XACT_NVAL) { + pending_ack = xact_id; + complete = &cb; + } else { + complete = NULL; + } + + net_buf_add_be32(buf, link.id); + net_buf_add_u8(buf, xact_id); + net_buf_add_u8(buf, GPC_ACK); + + bt_mesh_adv_send(buf, complete, NULL); + net_buf_unref(buf); +} + +static void send_reliable(void) +{ + int i; + + link.tx.start = k_uptime_get(); + + for (i = 0; i < ARRAY_SIZE(link.tx.buf); i++) { + struct net_buf *buf = link.tx.buf[i]; + + if (!buf) { + break; + } + + if (i + 1 < ARRAY_SIZE(link.tx.buf) && link.tx.buf[i + 1]) { + bt_mesh_adv_send(buf, NULL, NULL); + } else { + bt_mesh_adv_send(buf, &buf_sent_cb, NULL); + } + } +} + +static int bearer_ctl_send(u8_t op, void *data, u8_t data_len) +{ + struct net_buf *buf; + + BT_DBG("op 0x%02x data_len %u", op, data_len); + + prov_clear_tx(); + + buf = adv_buf_create(); + if (!buf) { + return -ENOBUFS; + } + + net_buf_add_be32(buf, link.id); + /* Transaction ID, always 0 for Bearer messages */ + net_buf_add_u8(buf, 0x00); + net_buf_add_u8(buf, GPC_CTL(op)); + net_buf_add_mem(buf, data, data_len); + + link.tx.buf[0] = buf; + send_reliable(); + + return 0; +} + +static u8_t last_seg(u8_t len) +{ + if (len <= START_PAYLOAD_MAX) { + return 0; + } + + len -= START_PAYLOAD_MAX; + + return 1 + (len / CONT_PAYLOAD_MAX); +} + +static inline u8_t next_transaction_id(void) +{ + if (link.tx.id != 0U && link.tx.id != 0xFF) { + return ++link.tx.id; + } + + link.tx.id = 0x80; + return link.tx.id; +} + +static int prov_send_adv(struct net_buf_simple *msg) +{ + struct net_buf *start, *buf; + u8_t seg_len, seg_id; + u8_t xact_id; + s32_t timeout = PROVISION_TIMEOUT; + + BT_DBG("%s, len %u: %s", __func__, msg->len, bt_hex(msg->data, msg->len)); + + prov_clear_tx(); + + start = adv_buf_create(); + if (!start) { + return -ENOBUFS; + } + + xact_id = next_transaction_id(); + net_buf_add_be32(start, link.id); + net_buf_add_u8(start, xact_id); + + net_buf_add_u8(start, GPC_START(last_seg(msg->len))); + net_buf_add_be16(start, msg->len); + net_buf_add_u8(start, bt_mesh_fcs_calc(msg->data, msg->len)); + + link.tx.buf[0] = start; + /* Changed by Espressif, get message type */ + link.tx_pdu_type = msg->data[0]; + + seg_len = MIN(msg->len, START_PAYLOAD_MAX); + BT_DBG("seg 0 len %u: %s", seg_len, bt_hex(msg->data, seg_len)); + net_buf_add_mem(start, msg->data, seg_len); + net_buf_simple_pull(msg, seg_len); + + buf = start; + for (seg_id = 1U; msg->len > 0; seg_id++) { + if (seg_id >= ARRAY_SIZE(link.tx.buf)) { + BT_ERR("%s, Too big message", __func__); + free_segments(); + return -E2BIG; + } + + buf = adv_buf_create(); + if (!buf) { + free_segments(); + return -ENOBUFS; + } + + link.tx.buf[seg_id] = buf; + + seg_len = MIN(msg->len, CONT_PAYLOAD_MAX); + + BT_DBG("seg_id %u len %u: %s", seg_id, seg_len, + bt_hex(msg->data, seg_len)); + + net_buf_add_be32(buf, link.id); + net_buf_add_u8(buf, xact_id); + net_buf_add_u8(buf, GPC_CONT(seg_id)); + net_buf_add_mem(buf, msg->data, seg_len); + net_buf_simple_pull(msg, seg_len); + } + + send_reliable(); + + /* Changed by Espressif, add provisioning timeout timer operations. + * When sending a provisioning PDU successfully, restart the 60s timer. + */ + if (bt_mesh_atomic_test_and_clear_bit(link.flags, TIMEOUT_START)) { + k_delayed_work_cancel(&link.timeout); + } +#if defined(CONFIG_BLE_MESH_FAST_PROV) + if (link.tx_pdu_type >= PROV_COMPLETE) { + timeout = K_SECONDS(60); + } +#endif + if (!bt_mesh_atomic_test_and_set_bit(link.flags, TIMEOUT_START)) { + k_delayed_work_submit(&link.timeout, timeout); + } + + return 0; +} + +#endif /* CONFIG_BLE_MESH_PB_ADV */ + +#if defined(CONFIG_BLE_MESH_PB_GATT) +static int prov_send_gatt(struct net_buf_simple *msg) +{ + int err = 0; + + if (!link.conn) { + return -ENOTCONN; + } + + /* Changed by Espressif, add provisioning timeout timer operations. + * When sending a provisioning PDU successfully, restart the 60s timer. + */ + err = bt_mesh_proxy_send(link.conn, BLE_MESH_PROXY_PROV, msg); + if (err) { + BT_ERR("%s, Failed to send provisioning PDU", __func__); + return err; + } + + if (bt_mesh_atomic_test_and_clear_bit(link.flags, TIMEOUT_START)) { + k_delayed_work_cancel(&link.timeout); + } + if (msg->data[1] != PROV_COMPLETE && msg->data[1] != PROV_FAILED) { + if (!bt_mesh_atomic_test_and_set_bit(link.flags, TIMEOUT_START)) { + k_delayed_work_submit(&link.timeout, PROVISION_TIMEOUT); + } + } + + return 0; +} +#endif /* CONFIG_BLE_MESH_PB_GATT */ + +static inline int prov_send(struct net_buf_simple *buf) +{ +#if defined(CONFIG_BLE_MESH_PB_GATT) + if (link.conn) { + return prov_send_gatt(buf); + } +#endif +#if defined(CONFIG_BLE_MESH_PB_ADV) + return prov_send_adv(buf); +#else + return 0; +#endif +} + +static void prov_buf_init(struct net_buf_simple *buf, u8_t type) +{ + net_buf_simple_reserve(buf, PROV_BUF_HEADROOM); + net_buf_simple_add_u8(buf, type); +} + +static void prov_send_fail_msg(u8_t err) +{ + PROV_BUF(buf, 2); + + prov_buf_init(&buf, PROV_FAILED); + net_buf_simple_add_u8(&buf, err); + prov_send(&buf); +} + +static void prov_invite(const u8_t *data) +{ + PROV_BUF(buf, 12); + + BT_DBG("Attention Duration: %u seconds", data[0]); + + if (data[0]) { + bt_mesh_attention(NULL, data[0]); + } + + link.conf_inputs[0] = data[0]; + + prov_buf_init(&buf, PROV_CAPABILITIES); + + /* Number of Elements supported */ + net_buf_simple_add_u8(&buf, bt_mesh_elem_count()); + + /* Supported algorithms - FIPS P-256 Eliptic Curve */ + net_buf_simple_add_be16(&buf, BIT(PROV_ALG_P256)); + + /* Public Key Type */ + net_buf_simple_add_u8(&buf, prov->oob_pub_key); + + /* Static OOB Type */ + net_buf_simple_add_u8(&buf, prov->static_val ? BIT(0) : 0x00); + + /* Output OOB Size */ + net_buf_simple_add_u8(&buf, prov->output_size); + + /* Output OOB Action */ + net_buf_simple_add_be16(&buf, prov->output_actions); + + /* Input OOB Size */ + net_buf_simple_add_u8(&buf, prov->input_size); + + /* Input OOB Action */ + net_buf_simple_add_be16(&buf, prov->input_actions); + + memcpy(&link.conf_inputs[1], &buf.data[1], 11); + + if (prov_send(&buf)) { + BT_ERR("%s, Failed to send capabilities", __func__); + close_link(PROV_ERR_RESOURCES, CLOSE_REASON_FAILED); + return; + } + + link.expect = PROV_START; +} + +static void prov_capabilities(const u8_t *data) +{ + u16_t algorithms, output_action, input_action; + + BT_DBG("Elements: %u", data[0]); + + algorithms = sys_get_be16(&data[1]); + BT_DBG("Algorithms: %u", algorithms); + + BT_DBG("Public Key Type: 0x%02x", data[3]); + BT_DBG("Static OOB Type: 0x%02x", data[4]); + BT_DBG("Output OOB Size: %u", data[5]); + + output_action = sys_get_be16(&data[6]); + BT_DBG("Output OOB Action: 0x%04x", output_action); + + BT_DBG("Input OOB Size: %u", data[8]); + + input_action = sys_get_be16(&data[9]); + BT_DBG("Input OOB Action: 0x%04x", input_action); +} + +static bt_mesh_output_action_t output_action(u8_t action) +{ + switch (action) { + case OUTPUT_OOB_BLINK: + return BLE_MESH_BLINK; + case OUTPUT_OOB_BEEP: + return BLE_MESH_BEEP; + case OUTPUT_OOB_VIBRATE: + return BLE_MESH_VIBRATE; + case OUTPUT_OOB_NUMBER: + return BLE_MESH_DISPLAY_NUMBER; + case OUTPUT_OOB_STRING: + return BLE_MESH_DISPLAY_STRING; + default: + return BLE_MESH_NO_OUTPUT; + } +} + +static bt_mesh_input_action_t input_action(u8_t action) +{ + switch (action) { + case INPUT_OOB_PUSH: + return BLE_MESH_PUSH; + case INPUT_OOB_TWIST: + return BLE_MESH_TWIST; + case INPUT_OOB_NUMBER: + return BLE_MESH_ENTER_NUMBER; + case INPUT_OOB_STRING: + return BLE_MESH_ENTER_STRING; + default: + return BLE_MESH_NO_INPUT; + } +} + +static int prov_auth(u8_t method, u8_t action, u8_t size) +{ + bt_mesh_output_action_t output; + bt_mesh_input_action_t input; + + switch (method) { + case AUTH_METHOD_NO_OOB: + if (action || size) { + return -EINVAL; + } + + (void)memset(link.auth, 0, sizeof(link.auth)); + return 0; + case AUTH_METHOD_STATIC: + if (action || size) { + return -EINVAL; + } + + memcpy(link.auth + 16 - prov->static_val_len, + prov->static_val, prov->static_val_len); + (void)memset(link.auth, 0, + sizeof(link.auth) - prov->static_val_len); + return 0; + + case AUTH_METHOD_OUTPUT: + output = output_action(action); + if (!output) { + return -EINVAL; + } + + if (!(prov->output_actions & output)) { + return -EINVAL; + } + + if (size > prov->output_size) { + return -EINVAL; + } + + if (output == BLE_MESH_DISPLAY_STRING) { + unsigned char str[9]; + u8_t i; + + bt_mesh_rand(str, size); + + /* Normalize to '0' .. '9' & 'A' .. 'Z' */ + for (i = 0U; i < size; i++) { + str[i] %= 36; + if (str[i] < 10) { + str[i] += '0'; + } else { + str[i] += 'A' - 10; + } + } + str[size] = '\0'; + + memcpy(link.auth, str, size); + (void)memset(link.auth + size, 0, + sizeof(link.auth) - size); + + return prov->output_string((char *)str); + } else { + u32_t div[8] = { 10, 100, 1000, 10000, 100000, + 1000000, 10000000, 100000000 + }; + u32_t num; + + bt_mesh_rand(&num, sizeof(num)); + num %= div[size - 1]; + + sys_put_be32(num, &link.auth[12]); + (void)memset(link.auth, 0, 12); + + return prov->output_number(output, num); + } + + case AUTH_METHOD_INPUT: + input = input_action(action); + if (!input) { + return -EINVAL; + } + + if (!(prov->input_actions & input)) { + return -EINVAL; + } + + if (size > prov->input_size) { + return -EINVAL; + } + + if (input == BLE_MESH_ENTER_STRING) { + bt_mesh_atomic_set_bit(link.flags, WAIT_STRING); + } else { + bt_mesh_atomic_set_bit(link.flags, WAIT_NUMBER); + } + + return prov->input(input, size); + + default: + return -EINVAL; + } +} + +static void prov_start(const u8_t *data) +{ + BT_DBG("Algorithm: 0x%02x", data[0]); + BT_DBG("Public Key: 0x%02x", data[1]); + BT_DBG("Auth Method: 0x%02x", data[2]); + BT_DBG("Auth Action: 0x%02x", data[3]); + BT_DBG("Auth Size: 0x%02x", data[4]); + + if (data[0] != PROV_ALG_P256) { + BT_ERR("%s, Unknown algorithm 0x%02x", __func__, data[0]); + prov_send_fail_msg(PROV_ERR_NVAL_FMT); + return; + } + + if (data[1] > 0x01) { + BT_ERR("%s, Invalid public key value: 0x%02x", __func__, data[1]); + prov_send_fail_msg(PROV_ERR_NVAL_FMT); + return; + } + + memcpy(&link.conf_inputs[12], data, 5); + + link.expect = PROV_PUB_KEY; + + /* If Provisioning Start PDU indicates that provisioner chooses + * OOB public key, then callback to the application layer to let + * users input public & private key pair. + */ + link.oob_pk_flag = data[1] ? true : false; + if (link.oob_pk_flag) { + prov->oob_pub_key_cb(); + } + + if (prov_auth(data[2], data[3], data[4]) < 0) { + BT_ERR("%s, Invalid authentication method: 0x%02x; " + "action: 0x%02x; size: 0x%02x", + __func__, data[2], data[3], data[4]); + prov_send_fail_msg(PROV_ERR_NVAL_FMT); + } +} + +static void send_confirm(void) +{ + PROV_BUF(cfm, 17); + + BT_DBG("ConfInputs[0] %s", bt_hex(link.conf_inputs, 64)); + BT_DBG("ConfInputs[64] %s", bt_hex(&link.conf_inputs[64], 64)); + BT_DBG("ConfInputs[128] %s", bt_hex(&link.conf_inputs[128], 17)); + + if (bt_mesh_prov_conf_salt(link.conf_inputs, link.conf_salt)) { + BT_ERR("%s, Unable to generate confirmation salt", __func__); + close_link(PROV_ERR_UNEXP_ERR, CLOSE_REASON_FAILED); + return; + } + + BT_DBG("ConfirmationSalt: %s", bt_hex(link.conf_salt, 16)); + + if (bt_mesh_prov_conf_key(link.dhkey, link.conf_salt, link.conf_key)) { + BT_ERR("%s, Unable to generate confirmation key", __func__); + close_link(PROV_ERR_UNEXP_ERR, CLOSE_REASON_FAILED); + return; + } + + BT_DBG("ConfirmationKey: %s", bt_hex(link.conf_key, 16)); + + if (bt_mesh_rand(link.rand, 16)) { + BT_ERR("%s, Unable to generate random number", __func__); + close_link(PROV_ERR_UNEXP_ERR, CLOSE_REASON_FAILED); + return; + } + + BT_DBG("LocalRandom: %s", bt_hex(link.rand, 16)); + + prov_buf_init(&cfm, PROV_CONFIRM); + + if (bt_mesh_prov_conf(link.conf_key, link.rand, link.auth, + net_buf_simple_add(&cfm, 16))) { + BT_ERR("%s, Unable to generate confirmation value", __func__); + close_link(PROV_ERR_UNEXP_ERR, CLOSE_REASON_FAILED); + return; + } + + if (prov_send(&cfm)) { + BT_ERR("%s, Unable to send Provisioning Confirm", __func__); + close_link(PROV_ERR_RESOURCES, CLOSE_REASON_FAILED); + return; + } + + link.expect = PROV_RANDOM; +} + +static void send_input_complete(void) +{ + PROV_BUF(buf, 1); + + prov_buf_init(&buf, PROV_INPUT_COMPLETE); + prov_send(&buf); +} + +int bt_mesh_input_number(u32_t num) +{ + BT_DBG("%u", num); + + if (!bt_mesh_atomic_test_and_clear_bit(link.flags, WAIT_NUMBER)) { + return -EINVAL; + } + + sys_put_be32(num, &link.auth[12]); + + send_input_complete(); + + if (!bt_mesh_atomic_test_bit(link.flags, HAVE_DHKEY)) { + return 0; + } + + if (bt_mesh_atomic_test_and_clear_bit(link.flags, SEND_CONFIRM)) { + send_confirm(); + } + + return 0; +} + +int bt_mesh_input_string(const char *str) +{ + BT_DBG("%s", str); + + if (!bt_mesh_atomic_test_and_clear_bit(link.flags, WAIT_STRING)) { + return -EINVAL; + } + + (void)memcpy(link.auth, str, prov->input_size); + + send_input_complete(); + + if (!bt_mesh_atomic_test_bit(link.flags, HAVE_DHKEY)) { + return 0; + } + + if (bt_mesh_atomic_test_and_clear_bit(link.flags, SEND_CONFIRM)) { + send_confirm(); + } + + return 0; +} + +static void prov_dh_key_cb(const u8_t key[32], const u8_t idx) +{ + BT_DBG("%p", key); + + if (!key) { + BT_ERR("%s, DHKey generation failed", __func__); + close_link(PROV_ERR_UNEXP_ERR, CLOSE_REASON_FAILED); + return; + } + + sys_memcpy_swap(link.dhkey, key, 32); + + BT_DBG("DHkey: %s", bt_hex(link.dhkey, 32)); + + bt_mesh_atomic_set_bit(link.flags, HAVE_DHKEY); + + if (bt_mesh_atomic_test_bit(link.flags, WAIT_NUMBER) || + bt_mesh_atomic_test_bit(link.flags, WAIT_STRING)) { + return; + } + + if (bt_mesh_atomic_test_and_clear_bit(link.flags, SEND_CONFIRM)) { + send_confirm(); + } +} + +static void send_pub_key(void) +{ + PROV_BUF(buf, 65); + const u8_t *key; + + key = bt_mesh_pub_key_get(); + if (!key) { + BT_ERR("%s, No public key available", __func__); + close_link(PROV_ERR_RESOURCES, CLOSE_REASON_FAILED); + return; + } + + BT_DBG("Local Public Key: %s", bt_hex(key, 64)); + + prov_buf_init(&buf, PROV_PUB_KEY); + + /* Swap X and Y halves independently to big-endian */ + sys_memcpy_swap(net_buf_simple_add(&buf, 32), key, 32); + sys_memcpy_swap(net_buf_simple_add(&buf, 32), &key[32], 32); + + memcpy(&link.conf_inputs[81], &buf.data[1], 64); + + prov_send(&buf); + + /* Copy remote key in little-endian for bt_mesh_dh_key_gen(). + * X and Y halves are swapped independently. + */ + net_buf_simple_reset(&buf); + sys_memcpy_swap(buf.data, &link.conf_inputs[17], 32); + sys_memcpy_swap(&buf.data[32], &link.conf_inputs[49], 32); + + if (bt_mesh_dh_key_gen(buf.data, prov_dh_key_cb, 0)) { + BT_ERR("%s, Unable to generate DHKey", __func__); + close_link(PROV_ERR_UNEXP_ERR, CLOSE_REASON_FAILED); + return; + } + + link.expect = PROV_CONFIRM; +} + +static int bt_mesh_calc_dh_key(void) +{ + NET_BUF_SIMPLE_DEFINE(buf, 64); + + /* Copy remote key in little-endian for bt_mesh_dh_key_gen(). + * X and Y halves are swapped independently. + */ + net_buf_simple_reset(&buf); + sys_memcpy_swap(buf.data, &link.conf_inputs[17], 32); + sys_memcpy_swap(&buf.data[32], &link.conf_inputs[49], 32); + + if (bt_mesh_dh_key_gen(buf.data, prov_dh_key_cb, 0)) { + BT_ERR("%s, Unable to generate DHKey", __func__); + close_link(PROV_ERR_UNEXP_ERR, CLOSE_REASON_FAILED); + return -EIO; + } + + return 0; +} + +int bt_mesh_set_oob_pub_key(const u8_t pub_key_x[32], const u8_t pub_key_y[32], + const u8_t pri_key[32]) +{ + if (!pub_key_x || !pub_key_y || !pri_key) { + BT_ERR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + /* Copy OOB public key in big-endian to Provisioning ConfirmationInputs, + * X and Y halves are swapped independently. + * And set input private key to mesh_bearer_adapt.c + */ + sys_memcpy_swap(&link.conf_inputs[81], pub_key_x, 32); + sys_memcpy_swap(&link.conf_inputs[81] + 32, pub_key_y, 32); + bt_mesh_set_private_key(pri_key); + + bt_mesh_atomic_set_bit(link.flags, OOB_PUB_KEY); + + /* If remote public key is not got, just return */ + if (!bt_mesh_atomic_test_bit(link.flags, REMOTE_PUB_KEY)) { + return 0; + } + + return bt_mesh_calc_dh_key(); +} + +static void prov_pub_key(const u8_t *data) +{ + BT_DBG("Remote Public Key: %s", bt_hex(data, 64)); + + /* BLE Mesh BQB test case MESH/NODE/PROV/UPD/BI-13-C needs to + * check the public key using the following rules: + * (1) X > 0, Y > 0 + * (2) X > 0, Y = 0 + * (3) X = 0, Y = 0 + */ + if (!bt_mesh_check_public_key(data)) { + BT_ERR("%s, Invalid public key", __func__); + prov_send_fail_msg(PROV_ERR_UNEXP_PDU); + return; + } + + memcpy(&link.conf_inputs[17], data, 64); + bt_mesh_atomic_set_bit(link.flags, REMOTE_PUB_KEY); + + if (!bt_mesh_pub_key_get()) { + /* Clear retransmit timer */ +#if defined(CONFIG_BLE_MESH_PB_ADV) + prov_clear_tx(); +#endif + BT_WARN("Waiting for a local public key"); + return; + } + + if (!link.oob_pk_flag) { + send_pub_key(); + } else { + link.expect = PROV_CONFIRM; + } +} + +static void prov_input_complete(const u8_t *data) +{ + BT_DBG("%s", __func__); +} + +static void prov_confirm(const u8_t *data) +{ + BT_DBG("Remote Confirm: %s", bt_hex(data, 16)); + + memcpy(link.conf, data, 16); + + if (!bt_mesh_atomic_test_bit(link.flags, HAVE_DHKEY)) { +#if defined(CONFIG_BLE_MESH_PB_ADV) + prov_clear_tx(); +#endif + bt_mesh_atomic_set_bit(link.flags, SEND_CONFIRM); + /* If using OOB public key and it has already got, calculates dhkey */ + if (link.oob_pk_flag && bt_mesh_atomic_test_bit(link.flags, OOB_PUB_KEY)) { + bt_mesh_calc_dh_key(); + } + } else { + send_confirm(); + } +} + +static void prov_random(const u8_t *data) +{ + PROV_BUF(rnd, 17); + u8_t conf_verify[16]; + + BT_DBG("Remote Random: %s", bt_hex(data, 16)); + + if (bt_mesh_prov_conf(link.conf_key, data, link.auth, conf_verify)) { + BT_ERR("%s, Unable to calculate confirmation verification", __func__); + close_link(PROV_ERR_UNEXP_ERR, CLOSE_REASON_FAILED); + return; + } + + if (memcmp(conf_verify, link.conf, 16)) { + BT_ERR("%s, Invalid confirmation value", __func__); + BT_DBG("Received: %s", bt_hex(link.conf, 16)); + BT_DBG("Calculated: %s", bt_hex(conf_verify, 16)); + close_link(PROV_ERR_CFM_FAILED, CLOSE_REASON_FAILED); + return; + } + + prov_buf_init(&rnd, PROV_RANDOM); + net_buf_simple_add_mem(&rnd, link.rand, 16); + + if (prov_send(&rnd)) { + BT_ERR("%s, Failed to send Provisioning Random", __func__); + close_link(PROV_ERR_RESOURCES, CLOSE_REASON_FAILED); + return; + } + + if (bt_mesh_prov_salt(link.conf_salt, data, link.rand, + link.prov_salt)) { + BT_ERR("%s, Failed to generate provisioning salt", __func__); + close_link(PROV_ERR_UNEXP_ERR, CLOSE_REASON_FAILED); + return; + } + + BT_DBG("ProvisioningSalt: %s", bt_hex(link.prov_salt, 16)); + + link.expect = PROV_DATA; +} + +static inline bool is_pb_gatt(void) +{ +#if defined(CONFIG_BLE_MESH_PB_GATT) + return !!link.conn; +#else + return false; +#endif +} + +static void prov_data(const u8_t *data) +{ + PROV_BUF(msg, 1); + u8_t session_key[16]; + u8_t nonce[13]; + u8_t dev_key[16]; + u8_t pdu[25]; + u8_t flags; + u32_t iv_index; + u16_t addr; + u16_t net_idx; + int err; + bool identity_enable; + + BT_DBG("%s", __func__); + + err = bt_mesh_session_key(link.dhkey, link.prov_salt, session_key); + if (err) { + BT_ERR("%s, Unable to generate session key", __func__); + close_link(PROV_ERR_UNEXP_ERR, CLOSE_REASON_FAILED); + return; + } + + BT_DBG("SessionKey: %s", bt_hex(session_key, 16)); + + err = bt_mesh_prov_nonce(link.dhkey, link.prov_salt, nonce); + if (err) { + BT_ERR("%s, Unable to generate session nonce", __func__); + close_link(PROV_ERR_UNEXP_ERR, CLOSE_REASON_FAILED); + return; + } + + BT_DBG("Nonce: %s", bt_hex(nonce, 13)); + + err = bt_mesh_prov_decrypt(session_key, nonce, data, pdu); + if (err) { + BT_ERR("%s, Unable to decrypt provisioning data", __func__); + close_link(PROV_ERR_DECRYPT, CLOSE_REASON_FAILED); + return; + } + + err = bt_mesh_dev_key(link.dhkey, link.prov_salt, dev_key); + if (err) { + BT_ERR("%s, Unable to generate device key", __func__); + close_link(PROV_ERR_UNEXP_ERR, CLOSE_REASON_FAILED); + return; + } + + BT_DBG("DevKey: %s", bt_hex(dev_key, 16)); + + net_idx = sys_get_be16(&pdu[16]); + flags = pdu[18]; + iv_index = sys_get_be32(&pdu[19]); + addr = sys_get_be16(&pdu[23]); + + BT_DBG("net_idx %u iv_index 0x%08x, addr 0x%04x", + net_idx, iv_index, addr); + + prov_buf_init(&msg, PROV_COMPLETE); + prov_send(&msg); + + /* Ignore any further PDUs on this link */ + link.expect = 0U; + + /* Store info, since bt_mesh_provision() will end up clearing it */ + if (IS_ENABLED(CONFIG_BLE_MESH_GATT_PROXY)) { + identity_enable = is_pb_gatt(); + } else { + identity_enable = false; + } + + err = bt_mesh_provision(pdu, net_idx, flags, iv_index, addr, dev_key); + if (err) { + BT_ERR("Failed to provision (err %d)", err); + return; + } + + /* After PB-GATT provisioning we should start advertising + * using Node Identity. + */ + if (IS_ENABLED(CONFIG_BLE_MESH_GATT_PROXY) && identity_enable) { + bt_mesh_proxy_identity_enable(); + } +} + +static void prov_complete(const u8_t *data) +{ + BT_DBG("%s", __func__); +} + +static void prov_failed(const u8_t *data) +{ + BT_WARN("Error: 0x%02x", data[0]); +} + +static const struct { + void (*func)(const u8_t *data); + u16_t len; +} prov_handlers[] = { + { prov_invite, 1 }, + { prov_capabilities, 11 }, + { prov_start, 5, }, + { prov_pub_key, 64 }, + { prov_input_complete, 0 }, + { prov_confirm, 16 }, + { prov_random, 16 }, + { prov_data, 33 }, + { prov_complete, 0 }, + { prov_failed, 1 }, +}; + +static void close_link(u8_t err, u8_t reason) +{ +#if defined(CONFIG_BLE_MESH_PB_GATT) + if (link.conn) { + bt_mesh_pb_gatt_close(link.conn); + return; + } +#endif + +#if defined(CONFIG_BLE_MESH_PB_ADV) + if (err) { + prov_send_fail_msg(err); + } + + link.rx.seg = 0U; + bearer_ctl_send(LINK_CLOSE, &reason, sizeof(reason)); +#endif + + reset_state(); +} + +/* Changed by Espressif, add provisioning timeout timer callback */ +static void prov_timeout(struct k_work *work) +{ + BT_DBG("%s", __func__); + + close_link(PROV_ERR_UNEXP_ERR, CLOSE_REASON_TIMEOUT); +} + +#if defined(CONFIG_BLE_MESH_PB_ADV) +static void prov_retransmit(struct k_work *work) +{ + s64_t timeout = TRANSACTION_TIMEOUT; + int i; + + BT_DBG("%s", __func__); + + if (!bt_mesh_atomic_test_bit(link.flags, LINK_ACTIVE)) { + BT_WARN("Link not active"); + return; + } + +#if defined(CONFIG_BLE_MESH_FAST_PROV) + /* When Provisioning Failed PDU is sent, 3s may be used here. */ + if (link.tx_pdu_type >= PROV_COMPLETE) { + timeout = K_SECONDS(30); + } +#endif + if (k_uptime_get() - link.tx.start > timeout) { + BT_WARN("Node timeout, giving up transaction"); + reset_link(); + return; + } + + for (i = 0; i < ARRAY_SIZE(link.tx.buf); i++) { + struct net_buf *buf = link.tx.buf[i]; + + if (!buf) { + break; + } + + if (BLE_MESH_ADV(buf)->busy) { + continue; + } + + BT_DBG("%u bytes: %s", buf->len, bt_hex(buf->data, buf->len)); + + if (i + 1 < ARRAY_SIZE(link.tx.buf) && link.tx.buf[i + 1]) { + bt_mesh_adv_send(buf, NULL, NULL); + } else { + bt_mesh_adv_send(buf, &buf_sent_cb, NULL); + } + + } +} + +static void link_open(struct prov_rx *rx, struct net_buf_simple *buf) +{ + BT_DBG("len %u", buf->len); + + if (buf->len < 16) { + BT_ERR("%s, Too short bearer open message (len %u)", __func__, buf->len); + return; + } + + if (bt_mesh_atomic_test_bit(link.flags, LINK_ACTIVE)) { + /* Send another link ack if the provisioner missed the last */ + if (link.id == rx->link_id && link.expect == PROV_INVITE) { + BT_DBG("Resending link ack"); + bearer_ctl_send(LINK_ACK, NULL, 0); + } else { + BT_WARN("Ignoring bearer open: link already active"); + } + + return; + } + + if (memcmp(buf->data, prov->uuid, 16)) { + BT_DBG("Bearer open message not for us"); + return; + } + + if (prov->link_open) { + prov->link_open(BLE_MESH_PROV_ADV); + } + + link.id = rx->link_id; + bt_mesh_atomic_set_bit(link.flags, LINK_ACTIVE); + net_buf_simple_reset(link.rx.buf); + +#if defined(CONFIG_BLE_MESH_USE_DUPLICATE_SCAN) + /* Add the link id into exceptional list */ + bt_mesh_update_exceptional_list(BLE_MESH_EXCEP_LIST_ADD, + BLE_MESH_EXCEP_INFO_MESH_LINK_ID, &link.id); +#endif + + bearer_ctl_send(LINK_ACK, NULL, 0); + + link.expect = PROV_INVITE; +} + +static void link_ack(struct prov_rx *rx, struct net_buf_simple *buf) +{ + BT_DBG("len %u", buf->len); +} + +static void link_close(struct prov_rx *rx, struct net_buf_simple *buf) +{ + BT_DBG("len %u", buf->len); + + reset_link(); +} + +static void gen_prov_ctl(struct prov_rx *rx, struct net_buf_simple *buf) +{ + BT_DBG("op 0x%02x len %u", BEARER_CTL(rx->gpc), buf->len); + + switch (BEARER_CTL(rx->gpc)) { + case LINK_OPEN: + link_open(rx, buf); + break; + case LINK_ACK: + if (!bt_mesh_atomic_test_bit(link.flags, LINK_ACTIVE)) { + return; + } + + link_ack(rx, buf); + break; + case LINK_CLOSE: + if (!bt_mesh_atomic_test_bit(link.flags, LINK_ACTIVE)) { + return; + } + + link_close(rx, buf); + break; + default: + BT_ERR("%s, Unknown bearer opcode: 0x%02x", __func__, BEARER_CTL(rx->gpc)); + return; + } +} + +static void prov_msg_recv(void) +{ + u8_t type = link.rx.buf->data[0]; + + BT_DBG("type 0x%02x len %u", type, link.rx.buf->len); + + if (!bt_mesh_fcs_check(link.rx.buf, link.rx.fcs)) { + BT_ERR("%s, Incorrect FCS", __func__); + return; + } + + gen_prov_ack_send(link.rx.id); + link.rx.prev_id = link.rx.id; + link.rx.id = 0U; + + if (type != PROV_FAILED && type != link.expect) { + BT_WARN("Unexpected msg 0x%02x != 0x%02x", type, link.expect); + prov_send_fail_msg(PROV_ERR_UNEXP_PDU); + return; + } + + if (type >= ARRAY_SIZE(prov_handlers)) { + BT_ERR("%s, Unknown provisioning PDU type 0x%02x", __func__, type); + close_link(PROV_ERR_NVAL_PDU, CLOSE_REASON_FAILED); + return; + } + + if (1 + prov_handlers[type].len != link.rx.buf->len) { + BT_ERR("%s, Invalid length %u for type 0x%02x", + __func__, link.rx.buf->len, type); + close_link(PROV_ERR_NVAL_FMT, CLOSE_REASON_FAILED); + return; + } + + /* Changed by Espressif, add provisioning timeout timer operations. + * When received a provisioning PDU, restart the 60s timer. + */ + if (bt_mesh_atomic_test_and_clear_bit(link.flags, TIMEOUT_START)) { + k_delayed_work_cancel(&link.timeout); + } + if (!bt_mesh_atomic_test_and_set_bit(link.flags, TIMEOUT_START)) { + k_delayed_work_submit(&link.timeout, PROVISION_TIMEOUT); + } + + prov_handlers[type].func(&link.rx.buf->data[1]); +} + +static void gen_prov_cont(struct prov_rx *rx, struct net_buf_simple *buf) +{ + u8_t seg = CONT_SEG_INDEX(rx->gpc); + + BT_DBG("len %u, seg_index %u", buf->len, seg); + + if (!link.rx.seg && link.rx.prev_id == rx->xact_id) { + BT_WARN("Resending ack"); + gen_prov_ack_send(rx->xact_id); + return; + } + + /* An issue here: + * If the Transaction Start PDU is lost and the device receives corresponding + * Transaction Continuation PDU fist, this will trigger the following error - + * handling code to be executed and the device must wait for the timeout of + * PB-ADV provisioning procedure. Then another provisioning procedure can be + * started (link.rx.id will be reset after each provisioning PDU is received + * completely). This issue also exists in Provisioner. + */ + if (rx->xact_id != link.rx.id) { + BT_WARN("Data for unknown transaction (%u != %u)", + rx->xact_id, link.rx.id); + return; + } + + if (seg > link.rx.last_seg) { + BT_ERR("%s, Invalid segment index %u", __func__, seg); + close_link(PROV_ERR_NVAL_FMT, CLOSE_REASON_FAILED); + return; + } else if (seg == link.rx.last_seg) { + u8_t expect_len; + + expect_len = (link.rx.buf->len - 20U - + ((link.rx.last_seg - 1) * 23U)); + if (expect_len != buf->len) { + BT_ERR("%s, Incorrect last seg len: %u != %u", + __func__, expect_len, buf->len); + close_link(PROV_ERR_NVAL_FMT, CLOSE_REASON_FAILED); + return; + } + } + + if (!(link.rx.seg & BIT(seg))) { + BT_WARN("Ignoring already received segment"); + return; + } + + memcpy(XACT_SEG_DATA(seg), buf->data, buf->len); + XACT_SEG_RECV(seg); + + if (!link.rx.seg) { + prov_msg_recv(); + } +} + +static void gen_prov_ack(struct prov_rx *rx, struct net_buf_simple *buf) +{ + BT_DBG("len %u", buf->len); + + if (!link.tx.buf[0]) { + return; + } + + if (rx->xact_id == link.tx.id) { + prov_clear_tx(); + } +} + +static void gen_prov_start(struct prov_rx *rx, struct net_buf_simple *buf) +{ + if (link.rx.seg) { + BT_WARN("Got Start while there are unreceived segments"); + return; + } + + if (link.rx.prev_id == rx->xact_id) { + BT_WARN("Resending ack"); + gen_prov_ack_send(rx->xact_id); + return; + } + + link.rx.buf->len = net_buf_simple_pull_be16(buf); + link.rx.id = rx->xact_id; + link.rx.fcs = net_buf_simple_pull_u8(buf); + + BT_DBG("len %u last_seg %u total_len %u fcs 0x%02x", buf->len, + START_LAST_SEG(rx->gpc), link.rx.buf->len, link.rx.fcs); + + if (link.rx.buf->len < 1) { + BT_ERR("%s, Ignoring zero-length provisioning PDU", __func__); + close_link(PROV_ERR_NVAL_FMT, CLOSE_REASON_FAILED); + return; + } + + if (link.rx.buf->len > link.rx.buf->size) { + BT_ERR("%s, Too large provisioning PDU (%u bytes)", + __func__, link.rx.buf->len); + // close_link(PROV_ERR_NVAL_FMT, CLOSE_REASON_FAILED); + return; + } + + if (START_LAST_SEG(rx->gpc) > 0 && link.rx.buf->len <= 20U) { + BT_ERR("%s, Too small total length for multi-segment PDU", __func__); + close_link(PROV_ERR_NVAL_FMT, CLOSE_REASON_FAILED); + return; + } + + link.rx.seg = (1 << (START_LAST_SEG(rx->gpc) + 1)) - 1; + link.rx.last_seg = START_LAST_SEG(rx->gpc); + memcpy(link.rx.buf->data, buf->data, buf->len); + XACT_SEG_RECV(0); + + if (!link.rx.seg) { + prov_msg_recv(); + } +} + +static const struct { + void (*func)(struct prov_rx *rx, struct net_buf_simple *buf); + bool require_link; + u8_t min_len; +} gen_prov[] = { + { gen_prov_start, true, 3 }, + { gen_prov_ack, true, 0 }, + { gen_prov_cont, true, 0 }, + { gen_prov_ctl, false, 0 }, +}; + +static void gen_prov_recv(struct prov_rx *rx, struct net_buf_simple *buf) +{ + if (buf->len < gen_prov[GPCF(rx->gpc)].min_len) { + BT_ERR("%s, Too short GPC message type %u", __func__, GPCF(rx->gpc)); + return; + } + + if (!bt_mesh_atomic_test_bit(link.flags, LINK_ACTIVE) && + gen_prov[GPCF(rx->gpc)].require_link) { + BT_DBG("Ignoring message that requires active link"); + return; + } + + gen_prov[GPCF(rx->gpc)].func(rx, buf); +} + +void bt_mesh_pb_adv_recv(struct net_buf_simple *buf) +{ + struct prov_rx rx; + + if (!bt_prov_active() && bt_mesh_is_provisioned()) { + BT_DBG("Ignoring provisioning PDU - already provisioned"); + return; + } + + if (buf->len < 6) { + BT_WARN("Too short provisioning packet (len %u)", buf->len); + return; + } + + rx.link_id = net_buf_simple_pull_be32(buf); + rx.xact_id = net_buf_simple_pull_u8(buf); + rx.gpc = net_buf_simple_pull_u8(buf); + + BT_DBG("link_id 0x%08x xact_id %u", rx.link_id, rx.xact_id); + + if (bt_mesh_atomic_test_bit(link.flags, LINK_ACTIVE) && link.id != rx.link_id) { + BT_DBG("Ignoring mesh beacon for unknown link"); + return; + } + + gen_prov_recv(&rx, buf); +} +#endif /* CONFIG_BLE_MESH_PB_ADV */ + +#if defined(CONFIG_BLE_MESH_PB_GATT) +int bt_mesh_pb_gatt_recv(struct bt_mesh_conn *conn, struct net_buf_simple *buf) +{ + u8_t type; + + BT_DBG("%u bytes: %s", buf->len, bt_hex(buf->data, buf->len)); + + if (link.conn != conn) { + BT_WARN("Data for unexpected connection"); + return -ENOTCONN; + } + + if (buf->len < 1) { + BT_WARN("Too short provisioning packet (len %u)", buf->len); + return -EINVAL; + } + + type = net_buf_simple_pull_u8(buf); + if (type != PROV_FAILED && type != link.expect) { + BT_WARN("Unexpected msg 0x%02x != 0x%02x", type, link.expect); + prov_send_fail_msg(PROV_ERR_UNEXP_PDU); + return -EINVAL; + } + + if (type >= ARRAY_SIZE(prov_handlers)) { + BT_ERR("%s, Unknown provisioning PDU type 0x%02x", __func__, type); + return -EINVAL; + } + + if (prov_handlers[type].len != buf->len) { + BT_ERR("%s, Invalid length %u for type 0x%02x", __func__, buf->len, type); + return -EINVAL; + } + + /* Changed by Espressif, add provisioning timeout timer operations. + * When received a provisioning PDU, restart the 60s timer. + */ + if (bt_mesh_atomic_test_and_clear_bit(link.flags, TIMEOUT_START)) { + k_delayed_work_cancel(&link.timeout); + } + if (!bt_mesh_atomic_test_and_set_bit(link.flags, TIMEOUT_START)) { + k_delayed_work_submit(&link.timeout, PROVISION_TIMEOUT); + } + + prov_handlers[type].func(buf->data); + + return 0; +} + +int bt_mesh_pb_gatt_open(struct bt_mesh_conn *conn) +{ + BT_DBG("conn %p", conn); + + if (bt_mesh_atomic_test_and_set_bit(link.flags, LINK_ACTIVE)) { + return -EBUSY; + } + + link.conn = bt_mesh_conn_ref(conn); + link.expect = PROV_INVITE; + + if (prov->link_open) { + prov->link_open(BLE_MESH_PROV_GATT); + } + + return 0; +} + +int bt_mesh_pb_gatt_close(struct bt_mesh_conn *conn) +{ + BT_DBG("conn %p", conn); + + if (link.conn != conn) { + BT_ERR("%s, Not connected", __func__); + return -ENOTCONN; + } + + if (prov->link_close) { + prov->link_close(BLE_MESH_PROV_GATT); + } + + reset_state(); + + return 0; +} +#endif /* CONFIG_BLE_MESH_PB_GATT */ + +const struct bt_mesh_prov *bt_mesh_prov_get(void) +{ + return prov; +} + +bool bt_prov_active(void) +{ + return bt_mesh_atomic_test_bit(link.flags, LINK_ACTIVE); +} + +int bt_mesh_prov_init(const struct bt_mesh_prov *prov_info) +{ + const u8_t *key = NULL; + + if (!prov_info) { + BT_ERR("%s, No provisioning context provided", __func__); + return -EINVAL; + } + + /* Changed by Espressif. Use micro-ecc to generate public key now. */ + key = bt_mesh_pub_key_get(); + if (!key) { + BT_ERR("%s, Failed to generate public key", __func__); + return -EIO; + } + + prov = prov_info; + +#if defined(CONFIG_BLE_MESH_PB_ADV) + k_delayed_work_init(&link.tx.retransmit, prov_retransmit); +#endif + + /* Changed by Espressif, add provisioning timeout timer init */ + k_delayed_work_init(&link.timeout, prov_timeout); + + reset_state(); + + return 0; +} + +void bt_mesh_prov_complete(u16_t net_idx, u16_t addr, u8_t flags, u32_t iv_index) +{ + if (prov->complete) { + prov->complete(net_idx, addr, flags, iv_index); + } +} + +void bt_mesh_prov_reset(void) +{ + if (prov->reset) { + prov->reset(); + } +} + +#endif /* CONFIG_BLE_MESH_NODE */ diff --git a/components/bt/ble_mesh/mesh_core/prov.h b/components/bt/ble_mesh/mesh_core/prov.h new file mode 100644 index 0000000000..f96ed7707f --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/prov.h @@ -0,0 +1,34 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef _PROV_H_ +#define _PROV_H_ + +#include "mesh_main.h" +#include "mesh_buf.h" +#include "mesh_bearer_adapt.h" + +void bt_mesh_pb_adv_recv(struct net_buf_simple *buf); + +bool bt_prov_active(void); + +int bt_mesh_pb_gatt_open(struct bt_mesh_conn *conn); +int bt_mesh_pb_gatt_close(struct bt_mesh_conn *conn); +int bt_mesh_pb_gatt_recv(struct bt_mesh_conn *conn, struct net_buf_simple *buf); + +int bt_mesh_set_oob_pub_key(const u8_t pub_key_x[32], const u8_t pub_key_y[32], + const u8_t pri_key[32]); + +const struct bt_mesh_prov *bt_mesh_prov_get(void); + +int bt_mesh_prov_init(const struct bt_mesh_prov *prov); + +void bt_mesh_prov_complete(u16_t net_idx, u16_t addr, u8_t flags, u32_t iv_index); +void bt_mesh_prov_reset(void); + +#endif /* _PROV_H_ */ diff --git a/components/bt/ble_mesh/mesh_core/provisioner_beacon.c b/components/bt/ble_mesh/mesh_core/provisioner_beacon.c new file mode 100644 index 0000000000..dfe4e39fa0 --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/provisioner_beacon.c @@ -0,0 +1,71 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 + +#include "sdkconfig.h" +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BLE_MESH_DEBUG_BEACON) + +#include "mesh_util.h" +#include "mesh_buf.h" +#include "mesh_main.h" +#include "mesh_trace.h" + +#include "adv.h" +#include "mesh.h" +#include "net.h" +#include "prov.h" +#include "crypto.h" +#include "beacon.h" +#include "foundation.h" +#include "provisioner_prov.h" + +#define BEACON_TYPE_UNPROVISIONED 0x00 +#define BEACON_TYPE_SECURE 0x01 + +#if CONFIG_BLE_MESH_PROVISIONER + +static void provisioner_secure_beacon_recv(struct net_buf_simple *buf) +{ + // TODO: Provisioner receive and handle Secure Network Beacon +} + +void provisioner_beacon_recv(struct net_buf_simple *buf) +{ + u8_t type; + + BT_DBG("%u bytes: %s", buf->len, bt_hex(buf->data, buf->len)); + + if (buf->len < 1) { + BT_ERR("%s, Too short beacon", __func__); + return; + } + + type = net_buf_simple_pull_u8(buf); + switch (type) { + case BEACON_TYPE_UNPROVISIONED: + BT_DBG("Unprovisioned device beacon received"); + provisioner_unprov_beacon_recv(buf); + break; + case BEACON_TYPE_SECURE: + provisioner_secure_beacon_recv(buf); + break; + default: + BT_DBG("%s, Unknown beacon type 0x%02x", __func__, type); + break; + } +} + +#endif /* CONFIG_BLE_MESH_PROVISIONER */ diff --git a/components/bt/ble_mesh/mesh_core/provisioner_beacon.h b/components/bt/ble_mesh/mesh_core/provisioner_beacon.h new file mode 100644 index 0000000000..0b3cfae6e6 --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/provisioner_beacon.h @@ -0,0 +1,20 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#ifndef _PROVISIONER_BEACON_H_ +#define _PROVISIONER_BEACON_H_ + +void provisioner_beacon_recv(struct net_buf_simple *buf); + +#endif /* _PROVISIONER_BEACON_H_ */ \ No newline at end of file diff --git a/components/bt/ble_mesh/mesh_core/provisioner_main.c b/components/bt/ble_mesh/mesh_core/provisioner_main.c new file mode 100644 index 0000000000..d78cc1484a --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/provisioner_main.c @@ -0,0 +1,1278 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 + +#include "sdkconfig.h" +#include "osi/allocator.h" + +#include "mesh_util.h" +#include "mesh_main.h" +#include "mesh_trace.h" +#include "mesh_bearer_adapt.h" + +#include "mesh.h" +#include "crypto.h" +#include "adv.h" +#include "net.h" +#include "access.h" + +#include "provisioner_prov.h" +#include "provisioner_proxy.h" +#include "provisioner_main.h" + +#if CONFIG_BLE_MESH_PROVISIONER + +static const struct bt_mesh_prov *prov; +static const struct bt_mesh_comp *comp; + +static struct bt_mesh_node_t *mesh_nodes[CONFIG_BLE_MESH_MAX_STORED_NODES]; +static u32_t mesh_node_count; + +static bool prov_upper_init = false; + +static int provisioner_index_check(int node_index) +{ + struct bt_mesh_node_t *node = NULL; + + BT_DBG("%s", __func__); + + if (node_index < 0) { + BT_ERR("%s, Invalid node index %d", __func__, node_index); + return -EINVAL; + } + + if (node_index >= ARRAY_SIZE(mesh_nodes)) { + BT_ERR("%s, Too big node index", __func__); + return -EINVAL; + } + + node = mesh_nodes[node_index]; + if (!node) { + BT_ERR("%s, Node is not found", __func__); + return -EINVAL; + } + + return 0; +} + +int provisioner_node_provision(int node_index, const u8_t uuid[16], u16_t oob_info, + u16_t unicast_addr, u8_t element_num, u16_t net_idx, + u8_t flags, u32_t iv_index, const u8_t dev_key[16]) +{ + struct bt_mesh_node_t *node = NULL; + + BT_DBG("%s", __func__); + + if (mesh_node_count >= ARRAY_SIZE(mesh_nodes)) { + BT_ERR("%s, Node queue is full", __func__); + return -ENOMEM; + } + + if (node_index >= ARRAY_SIZE(mesh_nodes) || !uuid || !dev_key) { + BT_ERR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + node = osi_calloc(sizeof(struct bt_mesh_node_t)); + if (!node) { + BT_ERR("%s, Failed to allocate memory", __func__); + return -ENOMEM; + } + + BT_DBG("node_index: 0x%x, unicast_addr: 0x%x, element_num: 0x%x, net_idx: 0x%x", + node_index, unicast_addr, element_num, net_idx); + BT_DBG("dev_uuid: %s", bt_hex(uuid, 16)); + BT_DBG("dev_key: %s", bt_hex(dev_key, 16)); + + mesh_nodes[node_index] = node; + + memcpy(node->dev_uuid, uuid, 16); + node->oob_info = oob_info; + node->unicast_addr = unicast_addr; + node->element_num = element_num; + node->net_idx = net_idx; + node->flags = flags; + node->iv_index = iv_index; + memcpy(node->dev_key, dev_key, 16); + + mesh_node_count++; + + return 0; +} + +int provisioner_node_reset(int node_index) +{ + struct bt_mesh_node_t *node = NULL; + struct bt_mesh_rpl *rpl = NULL; + int i; + + BT_DBG("%s, reset node %d", __func__, node_index); + + if (!mesh_node_count) { + BT_ERR("%s, Node queue is empty", __func__); + return -ENODEV; + } + + if (provisioner_index_check(node_index)) { + BT_ERR("%s, Failed to check node index", __func__); + return -EINVAL; + } + + node = mesh_nodes[node_index]; + + /* Reset corresponding rpl when reset the node */ + for (i = 0; i < ARRAY_SIZE(bt_mesh.rpl); i++) { + rpl = &bt_mesh.rpl[i]; + if (rpl->src >= node->unicast_addr && + rpl->src < node->unicast_addr + node->element_num) { + memset(rpl, 0, sizeof(struct bt_mesh_rpl)); + } + } + + osi_free(mesh_nodes[node_index]); + mesh_nodes[node_index] = NULL; + + mesh_node_count--; + + return 0; +} + +int provisioner_upper_reset_all_nodes(void) +{ + int i, err; + + BT_DBG("%s", __func__); + + for (i = 0; i < ARRAY_SIZE(mesh_nodes); i++) { + err = provisioner_node_reset(i); + if (err == -ENODEV) { + return 0; + } + } + + return 0; +} + +/** For Provisioner, we use the same data structure + * (like, struct bt_mesh_subnet, etc.) for netkey + * & appkey because if not we need to change a lot + * of APIs. + */ +int provisioner_upper_init(void) +{ + struct bt_mesh_subnet *sub = NULL; + u8_t p_key[16] = {0}; + + BT_DBG("%s", __func__); + + if (prov_upper_init) { + return 0; + } + + comp = bt_mesh_comp_get(); + if (!comp) { + BT_ERR("%s, NULL composition data", __func__); + return -EINVAL; + } + + prov = provisioner_get_prov_info(); + if (!prov) { + BT_ERR("%s, NULL provisioning context", __func__); + return -EINVAL; + } + + /* If the device only acts as a Provisioner, need to initialize + each element's address. */ + bt_mesh_comp_provision(prov->prov_unicast_addr); + + /* Generate the primary netkey */ + if (bt_mesh_rand(p_key, 16)) { + BT_ERR("%s, Failed to generate Primary NetKey", __func__); + return -EIO; + } + + sub = osi_calloc(sizeof(struct bt_mesh_subnet)); + if (!sub) { + BT_ERR("%s, Failed to allocate memory", __func__); + return -ENOMEM; + } + + sub->kr_flag = BLE_MESH_KEY_REFRESH(prov->flags); + if (sub->kr_flag) { + if (bt_mesh_net_keys_create(&sub->keys[1], p_key)) { + BT_ERR("%s, Failed to generate net-related keys", __func__); + osi_free(sub); + return -EIO; + } + sub->kr_phase = BLE_MESH_KR_PHASE_2; + } else { + /* Currently provisioner only use keys[0] */ + if (bt_mesh_net_keys_create(&sub->keys[0], p_key)) { + BT_ERR("%s, Failed to create net-related keys", __func__); + osi_free(sub); + return -EIO; + } + sub->kr_phase = BLE_MESH_KR_NORMAL; + } + sub->net_idx = BLE_MESH_KEY_PRIMARY; + sub->node_id = BLE_MESH_NODE_IDENTITY_NOT_SUPPORTED; + + bt_mesh.p_sub[0] = sub; + + /* Dynamically added appkey & netkey will use these key_idx */ + bt_mesh.p_app_idx_next = 0x0000; + bt_mesh.p_net_idx_next = 0x0001; + + /* In this function, we use the values of struct bt_mesh_prov + which has been initialized in the application layer */ + bt_mesh.iv_index = prov->iv_index; + bt_mesh_atomic_set_bit_to(bt_mesh.flags, BLE_MESH_IVU_IN_PROGRESS, + BLE_MESH_IV_UPDATE(prov->flags)); + + /* Set minimum required hours, since the 96-hour minimum requirement + * doesn't apply straight after provisioning (since we can't know how + * long has actually passed since the network changed its state). + * This operation is the same with node initialization. + */ + bt_mesh.ivu_duration = BLE_MESH_IVU_MIN_HOURS; + + prov_upper_init = true; + + BT_DBG("kr_flag: %d, kr_phase: %d, net_idx: 0x%02x, node_id %d", + sub->kr_flag, sub->kr_phase, sub->net_idx, sub->node_id); + BT_DBG("netkey: %s, nid: 0x%x", bt_hex(sub->keys[0].net, 16), sub->keys[0].nid); + BT_DBG("enckey: %s", bt_hex(sub->keys[0].enc, 16)); + BT_DBG("network id: %s", bt_hex(sub->keys[0].net_id, 8)); + BT_DBG("identity: %s", bt_hex(sub->keys[0].identity, 16)); + BT_DBG("privacy: %s", bt_hex(sub->keys[0].privacy, 16)); + BT_DBG("beacon: %s", bt_hex(sub->keys[0].beacon, 16)); + + return 0; +} + +/* The following APIs are for provisioner upper layers internal use */ + +const u8_t *provisioner_net_key_get(u16_t net_idx) +{ + struct bt_mesh_subnet *sub = NULL; + int i; + + BT_DBG("%s", __func__); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.p_sub); i++) { + sub = bt_mesh.p_sub[i]; + if (!sub || (sub->net_idx != net_idx)) { + continue; + } + if (sub->kr_flag) { + return sub->keys[1].net; + } + return sub->keys[0].net; + } + + return NULL; +} + +struct bt_mesh_subnet *provisioner_subnet_get(u16_t net_idx) +{ + struct bt_mesh_subnet *sub = NULL; + int i; + + BT_DBG("%s", __func__); + + if (net_idx == BLE_MESH_KEY_ANY) { + return bt_mesh.p_sub[0]; + } + + for (i = 0; i < ARRAY_SIZE(bt_mesh.p_sub); i++) { + sub = bt_mesh.p_sub[i]; + if (!sub || (sub->net_idx != net_idx)) { + continue; + } + return sub; + } + + return NULL; +} + +bool provisioner_check_msg_dst_addr(u16_t dst_addr) +{ + struct bt_mesh_node_t *node = NULL; + int i; + + BT_DBG("%s", __func__); + + if (!BLE_MESH_ADDR_IS_UNICAST(dst_addr)) { + return true; + } + + for (i = 0; i < ARRAY_SIZE(mesh_nodes); i++) { + node = mesh_nodes[i]; + if (node && dst_addr >= node->unicast_addr && + dst_addr < node->unicast_addr + node->element_num) { + return true; + } + } + + return false; +} + +const u8_t *provisioner_get_device_key(u16_t dst_addr) +{ + /* Device key is only used to encrypt configuration messages. + * Configuration model shall only be supported by the primary + * element which uses the primary unicast address. + */ + struct bt_mesh_node_t *node = NULL; + int i; + + BT_DBG("%s", __func__); + + if (!BLE_MESH_ADDR_IS_UNICAST(dst_addr)) { + BT_ERR("%s, Not a unicast address 0x%04x", __func__, dst_addr); + return NULL; + } + + for (i = 0; i < ARRAY_SIZE(mesh_nodes); i++) { + node = mesh_nodes[i]; + if (node && node->unicast_addr == dst_addr) { + return node->dev_key; + } + } + + return NULL; +} + +struct bt_mesh_app_key *provisioner_app_key_find(u16_t app_idx) +{ + struct bt_mesh_app_key *key = NULL; + int i; + + BT_DBG("%s", __func__); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.p_app_keys); i++) { + key = bt_mesh.p_app_keys[i]; + if (!key) { + continue; + } + if (key->net_idx != BLE_MESH_KEY_UNUSED && + key->app_idx == app_idx) { + return key; + } + } + + return NULL; +} + +u32_t provisioner_get_prov_node_count(void) +{ + return mesh_node_count; +} + +/* The following APIs are for provisioner application use */ + +#if 0 +static int bt_mesh_provisioner_set_kr_flag(u16_t net_idx, bool kr_flag) +{ + struct bt_mesh_subnet *sub = NULL; + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.p_sub); i++) { + sub = bt_mesh.p_sub[i]; + if (!sub || (sub->net_idx != net_idx)) { + continue; + } + sub->kr_flag = kr_flag; + break; + } + if (i == ARRAY_SIZE(bt_mesh.p_sub)) { + return -ENODEV; + } + + /* TODO: When kr_flag is changed, provisioner may need + * to change the netkey of the subnet and update + * corresponding appkey. + */ + + return 0; +} + +static void bt_mesh_provisioner_set_iv_index(u32_t iv_index) +{ + bt_mesh.iv_index = iv_index; + + /* TODO: When iv_index is changed, provisioner may need to + * start iv update procedure. And the ivu_initiator + * & iv_update flags may also need to be set. + */ +} +#endif + +int bt_mesh_provisioner_store_node_info(struct bt_mesh_node_t *node_info) +{ + struct bt_mesh_node_t *node = NULL; + int i; + + if (!node_info) { + BT_ERR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + /* Check if the device uuid already exists */ + for (i = 0; i < ARRAY_SIZE(mesh_nodes); i++) { + node = mesh_nodes[i]; + if (node && !memcmp(node->dev_uuid, node_info->dev_uuid, 16)) { + BT_WARN("%s, Node info already exists", __func__); + return -EEXIST; + } + } + + /* 0 ~ (CONFIG_BLE_MESH_MAX_PROV_NODES-1) are left for self-provisioned nodes */ + for (i = CONFIG_BLE_MESH_MAX_PROV_NODES; i < ARRAY_SIZE(mesh_nodes); i++) { + node = mesh_nodes[i]; + if (!node) { + node = osi_calloc(sizeof(struct bt_mesh_node_t)); + if (!node) { + BT_ERR("%s, Failed to allocate memory", __func__); + return -ENOMEM; + } + memcpy(node, node_info, sizeof(struct bt_mesh_node_t)); + mesh_nodes[i] = node; + mesh_node_count++; + return 0; + } + } + + BT_ERR("%s, Node info is full", __func__); + return -ENOMEM; +} + +int bt_mesh_provisioner_get_all_node_unicast_addr(struct net_buf_simple *buf) +{ + struct bt_mesh_node_t *node = NULL; + int i; + + if (!buf) { + BT_ERR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(mesh_nodes); i++) { + node = mesh_nodes[i]; + if (!node || !BLE_MESH_ADDR_IS_UNICAST(node->unicast_addr)) { + continue; + } + net_buf_simple_add_le16(buf, node->unicast_addr); + } + + return 0; +} + +int bt_mesh_provisioner_set_node_name(int node_index, const char *name) +{ + size_t length, name_len; + int i; + + BT_DBG("%s", __func__); + + if (!name) { + BT_ERR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + if (provisioner_index_check(node_index)) { + BT_ERR("%s, Failed to check node index", __func__); + return -EINVAL; + } + + BT_DBG("name len is %d, name is %s", strlen(name), name); + + length = (strlen(name) <= MESH_NAME_SIZE) ? strlen(name) : MESH_NAME_SIZE; + for (i = 0; i < ARRAY_SIZE(mesh_nodes); i++) { + if (!mesh_nodes[i] || !mesh_nodes[i]->node_name) { + continue; + } + name_len = strlen(mesh_nodes[i]->node_name); + if (length != name_len) { + continue; + } + if (!strncmp(mesh_nodes[i]->node_name, name, length)) { + BT_WARN("%s, Name %s already exists", __func__, name); + return -EEXIST; + } + } + + strncpy(mesh_nodes[node_index]->node_name, name, length); + + return 0; +} + +const char *bt_mesh_provisioner_get_node_name(int node_index) +{ + BT_DBG("%s", __func__); + + if (provisioner_index_check(node_index)) { + BT_ERR("%s, Failed to check node index", __func__); + return NULL; + } + + return mesh_nodes[node_index]->node_name; +} + +int bt_mesh_provisioner_get_node_index(const char *name) +{ + size_t length, name_len; + int i; + + BT_DBG("%s", __func__); + + if (!name) { + return -EINVAL; + } + + length = (strlen(name) <= MESH_NAME_SIZE) ? strlen(name) : MESH_NAME_SIZE; + for (i = 0; i < ARRAY_SIZE(mesh_nodes); i++) { + if (!mesh_nodes[i] || !mesh_nodes[i]->node_name) { + continue; + } + name_len = strlen(mesh_nodes[i]->node_name); + if (length != name_len) { + continue; + } + if (!strncmp(mesh_nodes[i]->node_name, name, length)) { + return i; + } + } + + return -ENODEV; +} + +struct bt_mesh_node_t *bt_mesh_provisioner_get_node_info(u16_t unicast_addr) +{ + struct bt_mesh_node_t *node = NULL; + int i; + + BT_DBG("%s", __func__); + + if (!BLE_MESH_ADDR_IS_UNICAST(unicast_addr)) { + BT_ERR("%s, Not a unicast address 0x%04x", __func__, unicast_addr); + return NULL; + } + + for (i = 0; i < ARRAY_SIZE(mesh_nodes); i++) { + node = mesh_nodes[i]; + if (!node) { + continue; + } + if (unicast_addr >= node->unicast_addr && + unicast_addr < (node->unicast_addr + node->element_num)) { + return node; + } + } + + return NULL; +} + +u32_t bt_mesh_provisioner_get_net_key_count(void) +{ + return ARRAY_SIZE(bt_mesh.p_sub); +} + +u32_t bt_mesh_provisioner_get_app_key_count(void) +{ + return ARRAY_SIZE(bt_mesh.p_app_keys); +} + +static int provisioner_check_app_key(const u8_t app_key[16], u16_t *app_idx) +{ + struct bt_mesh_app_key *key = NULL; + int i; + + if (!app_key) { + return 0; + } + + /* Check if app_key is already existed */ + for (i = 0; i < ARRAY_SIZE(bt_mesh.p_app_keys); i++) { + key = bt_mesh.p_app_keys[i]; + if (key && (!memcmp(key->keys[0].val, app_key, 16) || + !memcmp(key->keys[1].val, app_key, 16))) { + *app_idx = key->app_idx; + return -EEXIST; + } + } + + return 0; +} + +static int provisioner_check_app_idx(u16_t app_idx, bool exist) +{ + struct bt_mesh_app_key *key = NULL; + int i; + + if (exist) { + /* Check if app_idx is already existed */ + for (i = 0; i < ARRAY_SIZE(bt_mesh.p_app_keys); i++) { + key = bt_mesh.p_app_keys[i]; + if (key && (key->app_idx == app_idx)) { + return -EEXIST; + } + } + return 0; + } + + /* Check if app_idx is not existed */ + for (i = 0; i < ARRAY_SIZE(bt_mesh.p_app_keys); i++) { + key = bt_mesh.p_app_keys[i]; + if (key && (key->app_idx == app_idx)) { + return 0; + } + } + + return -ENODEV; +} + +static int provisioner_check_app_key_full(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.p_app_keys); i++) { + if (!bt_mesh.p_app_keys[i]) { + return i; + } + } + + return -ENOMEM; +} + +static int provisioner_check_net_key(const u8_t net_key[16], u16_t *net_idx) +{ + struct bt_mesh_subnet *sub = NULL; + int i; + + if (!net_key) { + return 0; + } + + /* Check if net_key is already existed */ + for (i = 0; i < ARRAY_SIZE(bt_mesh.p_sub); i++) { + sub = bt_mesh.p_sub[i]; + if (sub && (!memcmp(sub->keys[0].net, net_key, 16) || + !memcmp(sub->keys[1].net, net_key, 16))) { + *net_idx = sub->net_idx; + return -EEXIST; + } + } + + return 0; +} + +static int provisioner_check_net_idx(u16_t net_idx, bool exist) +{ + struct bt_mesh_subnet *sub = NULL; + int i; + + if (exist) { + /* Check if net_idx is already existed */ + for (i = 0; i < ARRAY_SIZE(bt_mesh.p_sub); i++) { + sub = bt_mesh.p_sub[i]; + if (sub && (sub->net_idx == net_idx)) { + return -EEXIST; + } + } + return 0; + } + + /* Check if net_idx is not existed */ + for (i = 0; i < ARRAY_SIZE(bt_mesh.p_sub); i++) { + sub = bt_mesh.p_sub[i]; + if (sub && (sub->net_idx == net_idx)) { + return 0; + } + } + + return -ENODEV; +} + +static int provisioner_check_net_key_full(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.p_sub); i++) { + if (!bt_mesh.p_sub[i]) { + return i; + } + } + + return -ENOMEM; +} + +int bt_mesh_provisioner_local_app_key_add(const u8_t app_key[16], u16_t net_idx, u16_t *app_idx) +{ + struct bt_mesh_app_key *key = NULL; + struct bt_mesh_app_keys *keys = NULL; + u8_t p_key[16] = {0}; + int add = -1; + + if (bt_mesh.p_app_idx_next >= 0x1000) { + BT_ERR("%s, No AppKey Index available", __func__); + return -EIO; + } + + if (!app_idx || (*app_idx != 0xFFFF && *app_idx >= 0x1000)) { + BT_ERR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + /* Check if the same application key already exists */ + if (provisioner_check_app_key(app_key, app_idx)) { + BT_WARN("%s, AppKey already exists, AppKey Index updated", __func__); + return 0; + } + + /* Check if the net_idx exists */ + if (provisioner_check_net_idx(net_idx, false)) { + BT_ERR("%s, NetKey Index does not exist", __func__); + return -ENODEV; + } + + /* Check if the same app_idx already exists */ + if (provisioner_check_app_idx(*app_idx, true)) { + BT_ERR("%s, AppKey Index already exists", __func__); + return -EEXIST; + } + + add = provisioner_check_app_key_full(); + if (add < 0) { + BT_ERR("%s, AppKey queue is full", __func__); + return -ENOMEM; + } + + if (!app_key) { + if (bt_mesh_rand(p_key, 16)) { + BT_ERR("%s, Failed to generate AppKey", __func__); + return -EIO; + } + } else { + memcpy(p_key, app_key, 16); + } + + key = osi_calloc(sizeof(struct bt_mesh_app_key)); + if (!key) { + BT_ERR("%s, Failed to allocate memory", __func__); + return -ENOMEM; + } + + keys = &key->keys[0]; + if (bt_mesh_app_id(p_key, &keys->id)) { + BT_ERR("%s, Failed to generate AID", __func__); + osi_free(key); + return -EIO; + } + + memcpy(keys->val, p_key, 16); + key->net_idx = net_idx; + if (*app_idx != 0xFFFF) { + key->app_idx = *app_idx; + } else { + key->app_idx = bt_mesh.p_app_idx_next; + while (1) { + if (provisioner_check_app_idx(key->app_idx, true)) { + key->app_idx = (++bt_mesh.p_app_idx_next); + if (key->app_idx >= 0x1000) { + BT_ERR("%s, No AppKey Index available", __func__); + osi_free(key); + return -EIO; + } + } else { + break; + } + } + *app_idx = key->app_idx; + } + key->updated = false; + + bt_mesh.p_app_keys[add] = key; + + return 0; +} + +const u8_t *bt_mesh_provisioner_local_app_key_get(u16_t net_idx, u16_t app_idx) +{ + struct bt_mesh_app_key *key = NULL; + int i; + + BT_DBG("%s", __func__); + + if (provisioner_check_net_idx(net_idx, false)) { + BT_ERR("%s, NetKey Index does not exist", __func__); + return NULL; + } + + if (provisioner_check_app_idx(app_idx, false)) { + BT_ERR("%s, AppKey Index does not exist", __func__); + return NULL; + } + + for (i = 0; i < ARRAY_SIZE(bt_mesh.p_app_keys); i++) { + key = bt_mesh.p_app_keys[i]; + if (key && key->net_idx == net_idx && + key->app_idx == app_idx) { + if (key->updated) { + return key->keys[1].val; + } + return key->keys[0].val; + } + } + + return NULL; +} + +int bt_mesh_provisioner_local_app_key_delete(u16_t net_idx, u16_t app_idx) +{ + struct bt_mesh_app_key *key = NULL; + int i; + + BT_DBG("%s", __func__); + + if (provisioner_check_net_idx(net_idx, false)) { + BT_ERR("%s, NetKey Index does not exist", __func__); + return -ENODEV; + } + + if (provisioner_check_app_idx(app_idx, false)) { + BT_ERR("%s, AppKey Index does not exist", __func__); + return -ENODEV; + } + + for (i = 0; i < ARRAY_SIZE(bt_mesh.p_app_keys); i++) { + key = bt_mesh.p_app_keys[i]; + if (key && key->net_idx == net_idx && + key->app_idx == app_idx) { + osi_free(bt_mesh.p_app_keys[i]); + bt_mesh.p_app_keys[i] = NULL; + return 0; + } + } + + /* Shall never reach here */ + return -ENODEV; +} + +int bt_mesh_provisioner_local_net_key_add(const u8_t net_key[16], u16_t *net_idx) +{ + struct bt_mesh_subnet *sub = NULL; + u8_t p_key[16] = {0}; + int add = -1; + + if (bt_mesh.p_net_idx_next >= 0x1000) { + BT_ERR("%s, No NetKey Index available", __func__); + return -EIO; + } + + if (!net_idx || (*net_idx != 0xFFFF && *net_idx >= 0x1000)) { + BT_ERR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + /* Check if the same network key already exists */ + if (provisioner_check_net_key(net_key, net_idx)) { + BT_WARN("%s, NetKey already exists, NetKey Index updated", __func__); + return 0; + } + + /* Check if the same net_idx already exists */ + if (provisioner_check_net_idx(*net_idx, true)) { + BT_ERR("%s, NetKey Index already exists", __func__); + return -EEXIST; + } + + add = provisioner_check_net_key_full(); + if (add < 0) { + BT_ERR("%s, NetKey queue is full", __func__); + return -ENOMEM; + } + + if (!net_key) { + if (bt_mesh_rand(p_key, 16)) { + BT_ERR("%s, Failed to generate NetKey", __func__); + return -EIO; + } + } else { + memcpy(p_key, net_key, 16); + } + + sub = osi_calloc(sizeof(struct bt_mesh_subnet)); + if (!sub) { + BT_ERR("%s, Failed to allocate memory", __func__); + return -ENOMEM; + } + + if (bt_mesh_net_keys_create(&sub->keys[0], p_key)) { + BT_ERR("%s, Failed to generate NID", __func__); + osi_free(sub); + return -EIO; + } + + if (*net_idx != 0xFFFF) { + sub->net_idx = *net_idx; + } else { + sub->net_idx = bt_mesh.p_net_idx_next; + while (1) { + if (provisioner_check_net_idx(sub->net_idx, true)) { + sub->net_idx = (++bt_mesh.p_net_idx_next); + if (sub->net_idx >= 0x1000) { + BT_ERR("%s, No NetKey Index available", __func__); + osi_free(sub); + return -EIO; + } + } else { + break; + } + } + *net_idx = sub->net_idx; + } + sub->kr_phase = BLE_MESH_KR_NORMAL; + sub->kr_flag = false; + sub->node_id = BLE_MESH_NODE_IDENTITY_NOT_SUPPORTED; + + bt_mesh.p_sub[add] = sub; + + return 0; +} + +const u8_t *bt_mesh_provisioner_local_net_key_get(u16_t net_idx) +{ + struct bt_mesh_subnet *sub = NULL; + int i; + + BT_DBG("%s", __func__); + + if (provisioner_check_net_idx(net_idx, false)) { + BT_ERR("%s, NetKey Index does not exist", __func__); + return NULL; + } + + for (i = 0; i < ARRAY_SIZE(bt_mesh.p_sub); i++) { + sub = bt_mesh.p_sub[i]; + if (sub && sub->net_idx == net_idx) { + if (sub->kr_flag) { + return sub->keys[1].net; + } + return sub->keys[0].net; + } + } + + return NULL; +} + +int bt_mesh_provisioner_local_net_key_delete(u16_t net_idx) +{ + struct bt_mesh_subnet *sub = NULL; + int i; + + BT_DBG("%s", __func__); + + if (provisioner_check_net_idx(net_idx, false)) { + BT_ERR("%s, NetKey Index does not exist", __func__); + return -ENODEV; + } + + for (i = 0; i < ARRAY_SIZE(bt_mesh.p_sub); i++) { + sub = bt_mesh.p_sub[i]; + if (sub && sub->net_idx == net_idx) { + osi_free(bt_mesh.p_sub[i]); + bt_mesh.p_sub[i] = NULL; + return 0; + } + } + + /* Shall never reach here */ + return -ENODEV; +} + +int bt_mesh_provisioner_get_own_unicast_addr(u16_t *addr, u8_t *elem_num) +{ + if (!addr || !elem_num || !prov || !comp) { + BT_ERR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + *addr = prov->prov_unicast_addr; + *elem_num = comp->elem_count; + + return 0; +} + +int bt_mesh_provisioner_bind_local_model_app_idx(u16_t elem_addr, u16_t mod_id, + u16_t cid, u16_t app_idx) +{ + struct bt_mesh_elem *elem = NULL; + struct bt_mesh_model *model = NULL; + int i; + + if (!comp) { + BT_ERR("%s, NULL composition data", __func__); + return -EINVAL; + } + + for (i = 0; i < comp->elem_count; i++) { + elem = &comp->elem[i]; + if (elem->addr == elem_addr) { + break; + } + } + if (i == comp->elem_count) { + BT_ERR("%s, No element is found", __func__); + return -ENODEV; + } + + if (cid == 0xFFFF) { + model = bt_mesh_model_find(elem, mod_id); + } else { + model = bt_mesh_model_find_vnd(elem, cid, mod_id); + } + if (!model) { + BT_ERR("%s, No model is found", __func__); + return -ENODEV; + } + + if (provisioner_check_app_idx(app_idx, false)) { + BT_ERR("%s, AppKey Index does not exist", __func__); + return -ENODEV; + } + + for (i = 0; i < ARRAY_SIZE(model->keys); i++) { + if (model->keys[i] == app_idx) { + BT_WARN("%s, AppKey Index is already binded with model", __func__); + return 0; + } + } + + for (i = 0; i < ARRAY_SIZE(model->keys); i++) { + if (model->keys[i] == BLE_MESH_KEY_UNUSED) { + model->keys[i] = app_idx; + return 0; + } + } + + BT_ERR("%s, Model AppKey queue is full", __func__); + return -ENOMEM; +} + +int bt_mesh_provisioner_bind_local_app_net_idx(u16_t net_idx, u16_t app_idx) +{ + struct bt_mesh_app_key *key = NULL; + int i; + + BT_DBG("%s", __func__); + + if (provisioner_check_net_idx(net_idx, false)) { + BT_ERR("%s, NetKey Index does not exist", __func__); + return -ENODEV; + } + + if (provisioner_check_app_idx(app_idx, false)) { + BT_ERR("%s, AppKey Index does not exist", __func__); + return -ENODEV; + } + + for (i = 0; i < ARRAY_SIZE(bt_mesh.p_app_keys); i++) { + key = bt_mesh.p_app_keys[i]; + if (!key || (key->app_idx != app_idx)) { + continue; + } + key->net_idx = net_idx; + return 0; + } + + return -ENODEV; +} + +int bt_mesh_provisioner_print_local_element_info(void) +{ + struct bt_mesh_elem *elem = NULL; + struct bt_mesh_model *model = NULL; + int i, j; + + if (!comp) { + BT_ERR("%s, NULL composition data", __func__); + return -EINVAL; + } + + BT_WARN("************************************************"); + BT_WARN("* cid: 0x%04x pid: 0x%04x vid: 0x%04x *", comp->cid, comp->pid, comp->vid); + BT_WARN("* Element Number: 0x%02x *", comp->elem_count); + for (i = 0; i < comp->elem_count; i++) { + elem = &comp->elem[i]; + BT_WARN("* Element %d: 0x%04x *", i, elem->addr); + BT_WARN("* Loc: 0x%04x NumS: 0x%02x NumV: 0x%02x *", elem->loc, elem->model_count, elem->vnd_model_count); + for (j = 0; j < elem->model_count; j++) { + model = &elem->models[j]; + BT_WARN("* sig_model %d: id - 0x%04x *", j, model->id); + } + for (j = 0; j < elem->vnd_model_count; j++) { + model = &elem->vnd_models[j]; + BT_WARN("* vnd_model %d: id - 0x%04x, cid - 0x%04x *", j, model->vnd.id, model->vnd.company); + } + } + BT_WARN("************************************************"); + + return 0; +} + +#endif /* CONFIG_BLE_MESH_PROVISIONER */ + +/* The following APIs are for fast provisioning */ + +#if CONFIG_BLE_MESH_FAST_PROV + +const u8_t *get_fast_prov_device_key(u16_t addr) +{ + struct bt_mesh_node_t *node = NULL; + + BT_DBG("%s", __func__); + + if (!BLE_MESH_ADDR_IS_UNICAST(addr)) { + BT_ERR("%s, Not a unicast address 0x%04x", __func__, addr); + return NULL; + } + + if (addr == bt_mesh_primary_addr()) { + return bt_mesh.dev_key; + } + + for (int i = 0; i < ARRAY_SIZE(mesh_nodes); i++) { + node = mesh_nodes[i]; + if (node && node->unicast_addr == addr) { + return node->dev_key; + } + } + + return NULL; +} + +struct bt_mesh_subnet *get_fast_prov_subnet(u16_t net_idx) +{ + struct bt_mesh_subnet *sub = NULL; + + BT_DBG("%s", __func__); + + for (int i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + sub = &bt_mesh.sub[i]; + if (sub->net_idx == net_idx) { + return sub; + } + } + + for (int i = 0; i < ARRAY_SIZE(bt_mesh.p_sub); i++) { + sub = bt_mesh.p_sub[i]; + if (sub && sub->net_idx == net_idx) { + return sub; + } + } + + return NULL; +} + +struct bt_mesh_app_key *get_fast_prov_app_key(u16_t net_idx, u16_t app_idx) +{ + struct bt_mesh_app_key *key = NULL; + + BT_DBG("%s", __func__); + + for (int i = 0; i < ARRAY_SIZE(bt_mesh.app_keys); i++) { + key = &bt_mesh.app_keys[i]; + if (key->net_idx == net_idx && key->app_idx == app_idx) { + return key; + } + } + + for (int i = 0; i < ARRAY_SIZE(bt_mesh.p_app_keys); i++) { + key = bt_mesh.p_app_keys[i]; + if (key && key->net_idx == net_idx && key->app_idx == app_idx) { + return key; + } + } + + return NULL; +} + +u8_t bt_mesh_set_fast_prov_net_idx(u16_t net_idx) +{ + struct bt_mesh_subnet *sub = NULL; + struct bt_mesh_subnet_keys *key = NULL; + + sub = get_fast_prov_subnet(net_idx); + if (sub) { + key = BLE_MESH_KEY_REFRESH(sub->kr_flag) ? &sub->keys[1] : &sub->keys[0]; + return provisioner_set_fast_prov_net_idx(key->net, net_idx); + } + + /* If net_idx is not found, set net_idx to fast_prov first, + * and wait for primary provisioner to add net_key */ + return provisioner_set_fast_prov_net_idx(NULL, net_idx); +} + +u8_t bt_mesh_add_fast_prov_net_key(const u8_t net_key[16]) +{ + const u8_t *keys = NULL; + u16_t net_idx; + int err; + + net_idx = provisioner_get_fast_prov_net_idx(); + bt_mesh.p_net_idx_next = net_idx; + + err = bt_mesh_provisioner_local_net_key_add(net_key, &net_idx); + if (err) { + return 0x01; /* status: add net_key fail */ + }; + + keys = bt_mesh_provisioner_local_net_key_get(net_idx); + if (!keys) { + return 0x01; /* status: add net_key fail */ + } + + return provisioner_set_fast_prov_net_idx(keys, net_idx); +} + +const u8_t *bt_mesh_get_fast_prov_net_key(u16_t net_idx) +{ + struct bt_mesh_subnet *sub = NULL; + + sub = get_fast_prov_subnet(net_idx); + if (!sub) { + BT_ERR("%s, Failed to get subnet", __func__); + return NULL; + } + + return (sub->kr_flag ? sub->keys[1].net : sub->keys[0].net); +} + +const u8_t *bt_mesh_get_fast_prov_app_key(u16_t net_idx, u16_t app_idx) +{ + struct bt_mesh_app_key *key = NULL; + + key = get_fast_prov_app_key(net_idx, app_idx); + if (!key) { + BT_ERR("%s, Failed to get AppKey", __func__); + return NULL; + } + + return (key->updated ? key->keys[1].val : key->keys[0].val); +} + +#endif /* CONFIG_BLE_MESH_FAST_PROV */ diff --git a/components/bt/ble_mesh/mesh_core/provisioner_main.h b/components/bt/ble_mesh/mesh_core/provisioner_main.h new file mode 100644 index 0000000000..a4585d4009 --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/provisioner_main.h @@ -0,0 +1,122 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#ifndef _PROVISIONER_MAIN_H_ +#define _PROVISIONER_MAIN_H_ + +#include "mesh_util.h" +#include "mesh_kernel.h" +#include "mesh_access.h" +#include "net.h" + +#define MESH_NAME_SIZE 31 + +/* Each node information stored by provisioner */ +struct bt_mesh_node_t { + char node_name[MESH_NAME_SIZE]; /* Node name */ + u8_t dev_uuid[16]; /* Device UUID pointer, stored in provisioner_prov.c */ + u16_t oob_info; /* Node OOB information */ + u16_t unicast_addr; /* Node unicast address */ + u8_t element_num; /* Node element number */ + u16_t net_idx; /* Node provision net_idx */ + u8_t flags; /* Node key refresh flag and iv update flag */ + u32_t iv_index; /* Node IV Index */ + u8_t dev_key[16]; /* Node device key */ +} __packed; + +/* The following APIs are for key init, node provision & node reset. */ + +int provisioner_node_provision(int node_index, const u8_t uuid[16], u16_t oob_info, + u16_t unicast_addr, u8_t element_num, u16_t net_idx, + u8_t flags, u32_t iv_index, const u8_t dev_key[16]); + +int provisioner_node_reset(int node_index); + +int provisioner_upper_reset_all_nodes(void); + +int provisioner_upper_init(void); + +/* The following APIs are for provisioner upper layers internal usage. */ + +const u8_t *provisioner_net_key_get(u16_t net_idx); + +struct bt_mesh_subnet *provisioner_subnet_get(u16_t net_idx); + +bool provisioner_check_msg_dst_addr(u16_t dst_addr); + +const u8_t *provisioner_get_device_key(u16_t dst_addr); + +struct bt_mesh_app_key *provisioner_app_key_find(u16_t app_idx); + +u32_t provisioner_get_prov_node_count(void); + +/* The following APIs are for provisioner application use. */ + +int bt_mesh_provisioner_store_node_info(struct bt_mesh_node_t *node_info); + +int bt_mesh_provisioner_get_all_node_unicast_addr(struct net_buf_simple *buf); + +int bt_mesh_provisioner_set_node_name(int node_index, const char *name); + +const char *bt_mesh_provisioner_get_node_name(int node_index); + +int bt_mesh_provisioner_get_node_index(const char *name); + +struct bt_mesh_node_t *bt_mesh_provisioner_get_node_info(u16_t unicast_addr); + +u32_t bt_mesh_provisioner_get_net_key_count(void); + +u32_t bt_mesh_provisioner_get_app_key_count(void); + +int bt_mesh_provisioner_local_app_key_add(const u8_t app_key[16], u16_t net_idx, u16_t *app_idx); + +const u8_t *bt_mesh_provisioner_local_app_key_get(u16_t net_idx, u16_t app_idx); + +int bt_mesh_provisioner_local_app_key_delete(u16_t net_idx, u16_t app_idx); + +int bt_mesh_provisioner_local_net_key_add(const u8_t net_key[16], u16_t *net_idx); + +const u8_t *bt_mesh_provisioner_local_net_key_get(u16_t net_idx); + +int bt_mesh_provisioner_local_net_key_delete(u16_t net_idx); + +int bt_mesh_provisioner_get_own_unicast_addr(u16_t *addr, u8_t *elem_num); + +/* Provisioner bind local client model with proper appkey index */ +int bt_mesh_provisioner_bind_local_model_app_idx(u16_t elem_addr, u16_t mod_id, + u16_t cid, u16_t app_idx); + +/* This API can be used to change the net_idx binded with the app_idx. */ +int bt_mesh_provisioner_bind_local_app_net_idx(u16_t net_idx, u16_t app_idx); + +/* Provisioner print own element information */ +int bt_mesh_provisioner_print_local_element_info(void); + +/* The following APIs are for fast provisioning */ + +const u8_t *get_fast_prov_device_key(u16_t dst_addr); + +struct bt_mesh_subnet *get_fast_prov_subnet(u16_t net_idx); + +struct bt_mesh_app_key *get_fast_prov_app_key(u16_t net_idx, u16_t app_idx); + +u8_t bt_mesh_set_fast_prov_net_idx(u16_t net_idx); + +u8_t bt_mesh_add_fast_prov_net_key(const u8_t net_key[16]); + +const u8_t *bt_mesh_get_fast_prov_net_key(u16_t net_idx); + +const u8_t *bt_mesh_get_fast_prov_app_key(u16_t net_idx, u16_t app_idx); + +#endif /* _PROVISIONER_MAIN_H_ */ diff --git a/components/bt/ble_mesh/mesh_core/provisioner_prov.c b/components/bt/ble_mesh/mesh_core/provisioner_prov.c new file mode 100644 index 0000000000..a07c8cec87 --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/provisioner_prov.c @@ -0,0 +1,3287 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 + +#include "sdkconfig.h" +#include "osi/allocator.h" +#include "osi/mutex.h" + +#include "mesh_main.h" +#include "mesh_trace.h" +#include "mesh_bearer_adapt.h" + +#include "crypto.h" +#include "adv.h" +#include "mesh.h" +#include "provisioner_prov.h" +#include "provisioner_proxy.h" +#include "provisioner_main.h" + +#if CONFIG_BLE_MESH_PROVISIONER + +/* Service data length has minus 1 type length & 2 uuid length*/ +#define BLE_MESH_PROV_SRV_DATA_LEN 0x12 +#define BLE_MESH_PROXY_SRV_DATA_LEN1 0x09 +#define BLE_MESH_PROXY_SRV_DATA_LEN2 0x11 + +/* 3 transmissions, 20ms interval */ +#define PROV_XMIT BLE_MESH_TRANSMIT(2, 20) + +#define AUTH_METHOD_NO_OOB 0x00 +#define AUTH_METHOD_STATIC 0x01 +#define AUTH_METHOD_OUTPUT 0x02 +#define AUTH_METHOD_INPUT 0x03 + +#define OUTPUT_OOB_BLINK 0x00 +#define OUTPUT_OOB_BEEP 0x01 +#define OUTPUT_OOB_VIBRATE 0x02 +#define OUTPUT_OOB_NUMBER 0x03 +#define OUTPUT_OOB_STRING 0x04 + +#define INPUT_OOB_PUSH 0x00 +#define INPUT_OOB_TWIST 0x01 +#define INPUT_OOB_NUMBER 0x02 +#define INPUT_OOB_STRING 0x03 + +#define PROV_ERR_NONE 0x00 +#define PROV_ERR_NVAL_PDU 0x01 +#define PROV_ERR_NVAL_FMT 0x02 +#define PROV_ERR_UNEXP_PDU 0x03 +#define PROV_ERR_CFM_FAILED 0x04 +#define PROV_ERR_RESOURCES 0x05 +#define PROV_ERR_DECRYPT 0x06 +#define PROV_ERR_UNEXP_ERR 0x07 +#define PROV_ERR_ADDR 0x08 + +#define PROV_INVITE 0x00 +#define PROV_CAPABILITIES 0x01 +#define PROV_START 0x02 +#define PROV_PUB_KEY 0x03 +#define PROV_INPUT_COMPLETE 0x04 +#define PROV_CONFIRM 0x05 +#define PROV_RANDOM 0x06 +#define PROV_DATA 0x07 +#define PROV_COMPLETE 0x08 +#define PROV_FAILED 0x09 + +#define PROV_ALG_P256 0x00 + +#define GPCF(gpc) (gpc & 0x03) +#define GPC_START(last_seg) (((last_seg) << 2) | 0x00) +#define GPC_ACK 0x01 +#define GPC_CONT(seg_id) (((seg_id) << 2) | 0x02) +#define GPC_CTL(op) (((op) << 2) | 0x03) + +#define START_PAYLOAD_MAX 20 +#define CONT_PAYLOAD_MAX 23 + +#define START_LAST_SEG(gpc) (gpc >> 2) +#define CONT_SEG_INDEX(gpc) (gpc >> 2) + +#define BEARER_CTL(gpc) (gpc >> 2) +#define LINK_OPEN 0x00 +#define LINK_ACK 0x01 +#define LINK_CLOSE 0x02 + +#define CLOSE_REASON_SUCCESS 0x00 +#define CLOSE_REASON_TIMEOUT 0x01 +#define CLOSE_REASON_FAILED 0x02 + +#define PROV_AUTH_VAL_SIZE 0x10 +#define PROV_CONF_SALT_SIZE 0x10 +#define PROV_CONF_KEY_SIZE 0x10 +#define PROV_DH_KEY_SIZE 0x20 +#define PROV_CONFIRM_SIZE 0x10 +#define PROV_PROV_SALT_SIZE 0x10 +#define PROV_CONF_INPUTS_SIZE 0x91 + +#define XACT_SEG_DATA(_idx, _seg) (&link[_idx].rx.buf->data[20 + ((_seg - 1) * 23)]) +#define XACT_SEG_RECV(_idx, _seg) (link[_idx].rx.seg &= ~(1 << (_seg))) + +#define XACT_NVAL 0xff + +enum { + REMOTE_PUB_KEY, /* Remote key has been received */ + LOCAL_PUB_KEY, /* Local public key is available */ + LINK_ACTIVE, /* Link has been opened */ + WAIT_GEN_DHKEY, /* Waiting for remote public key to generate DHKey */ + HAVE_DHKEY, /* DHKey has been calcualted */ + SEND_CONFIRM, /* Waiting to send Confirm value */ + WAIT_NUMBER, /* Waiting for number input from user */ + WAIT_STRING, /* Waiting for string input from user */ + TIMEOUT_START, /* Provision timeout timer has started */ + NUM_FLAGS, +}; + +/** Provisioner link structure allocation + * |--------------------------------------------------------| + * | Link(PB-ADV) | Link(PB-GATT) | + * |--------------------------------------------------------| + * |<----------------------Total Link---------------------->| + */ +struct prov_link { + BLE_MESH_ATOMIC_DEFINE(flags, NUM_FLAGS); + u8_t uuid[16]; /* check if device is being provisioned*/ + u16_t oob_info; /* oob info of this device */ + u8_t element_num; /* element num of device */ + u8_t ki_flags; /* Key refresh flag and iv update flag */ + u32_t iv_index; /* IV Index */ + u8_t auth_method; /* choosed authentication method */ + u8_t auth_action; /* choosed authentication action */ + u8_t auth_size; /* choosed authentication size */ + u16_t unicast_addr; /* unicast address assigned for device */ + bt_mesh_addr_t addr; /* Device address */ +#if defined(CONFIG_BLE_MESH_PB_GATT) + bool connecting; /* start connecting with device */ + struct bt_mesh_conn *conn; /* GATT connection */ +#endif + u8_t expect; /* Next expected PDU */ + + u8_t *dhkey; /* Calculated DHKey */ + u8_t *auth; /* Authentication Value */ + + u8_t *conf_salt; /* ConfirmationSalt */ + u8_t *conf_key; /* ConfirmationKey */ + u8_t *conf_inputs; /* ConfirmationInputs */ + + u8_t *rand; /* Local Random */ + u8_t *conf; /* Remote Confirmation */ + + u8_t *prov_salt; /* Provisioning Salt */ + +#if defined(CONFIG_BLE_MESH_PB_ADV) + bool linking; /* Linking is being establishing */ + u16_t send_link_close; /* Link close is being sent flag */ + u32_t link_id; /* Link ID */ + u8_t pending_ack; /* Decide which transaction id ack is pending */ + u8_t expect_ack_for; /* Transaction ACK expected for provisioning pdu */ + u8_t tx_pdu_type; /* The current transmitted Provisioning PDU type */ + + struct { + u8_t trans_id; /* Transaction ID */ + u8_t prev_id; /* Previous Transaction ID */ + u8_t seg; /* Bit-field of unreceived segments */ + u8_t last_seg; /* Last segment (to check length) */ + u8_t fcs; /* Expected FCS value */ + u8_t adv_buf_id; /* index of buf allocated in adv_buf_data */ + struct net_buf_simple *buf; + } rx; + + struct { + /* Start timestamp of the transaction */ + s64_t start; + + /* Transaction id*/ + u8_t trans_id; + + /* Pending outgoing buffer(s) */ + struct net_buf *buf[3]; + + /* Retransmit timer */ + struct k_delayed_work retransmit; + } tx; +#endif + + /** Provision timeout timer. Spec P259 says: The provisioning protocol + * shall have a minimum timeout of 60 seconds that is reset each time + * a provisioning protocol PDU is sent or received. + */ + struct k_delayed_work timeout; +}; + +/* Number of devices can be provisioned at the same time equals to PB-ADV + PB-GATT */ +#define BLE_MESH_PROV_SAME_TIME \ + (CONFIG_BLE_MESH_PBA_SAME_TIME + CONFIG_BLE_MESH_PBG_SAME_TIME) + +static struct prov_link link[BLE_MESH_PROV_SAME_TIME]; + +struct prov_rx { + u32_t link_id; + u8_t xact_id; + u8_t gpc; +}; + +#define BLE_MESH_ALREADY_PROV_NUM (CONFIG_BLE_MESH_MAX_PROV_NODES + 10) + +struct prov_ctx_t { + /* If provisioning random have been generated, set BIT0 to 1 */ + u8_t rand_gen_done; + + /* Provisioner random */ + u8_t random[16]; + + /* Number of provisioned devices */ + u16_t node_count; + + /* Current number of PB-ADV provisioned devices simultaneously */ + u8_t pba_count; + + /* Current number of PB-GATT provisioned devices simultaneously */ + u8_t pbg_count; + + /* Current unicast address going to assigned */ + u16_t current_addr; + + /* Current net_idx going to be used in provisioning data */ + u16_t curr_net_idx; + + /* Current flags going to be used in provisioning data */ + u16_t curr_flags; + + /* Current iv_index going to be used in provisioning data */ + u16_t curr_iv_index; + + /* Offset of the device uuid to be matched, based on zero */ + u8_t match_offset; + + /* Length of the device uuid to be matched (start from the match_offset) */ + u8_t match_length; + + /* Value of the device uuid to be matched */ + u8_t *match_value; + + /* Indicate when received uuid_match adv_pkts, can provision it at once */ + bool prov_after_match; + + /* Mutex used to protect the PB-ADV procedure */ + osi_mutex_t pb_adv_lock; + + /* Mutex used to protect the PB-GATT procedure */ + osi_mutex_t pb_gatt_lock; + + /** This structure is used to store the information of the device which + * provisioner has successfully sent provisioning data to. In this + * structure, we don't care if the device is currently in the mesh + * network, or has been removed, or failed to send provisioning + * complete pdu after receiving the provisioning data pdu. + */ + struct already_prov_info { + u8_t uuid[16]; /* device uuid */ + u8_t element_num; /* element number of the deleted node */ + u16_t unicast_addr; /* Primary unicast address of the deleted node */ + } already_prov[BLE_MESH_ALREADY_PROV_NUM]; +}; + +static struct prov_ctx_t prov_ctx; + +struct prov_node_info { + bool provisioned; /* device provisioned flag */ + bt_mesh_addr_t addr; /* device address */ + u8_t uuid[16]; /* node uuid */ + u16_t oob_info; /* oob info contained in adv pkt */ + u8_t element_num; /* element contained in this node */ + u16_t unicast_addr; /* primary unicast address of this node */ + u16_t net_idx; /* Netkey index got during provisioning */ + u8_t flags; /* Key refresh flag and iv update flag */ + u32_t iv_index; /* IV Index */ +}; + +static struct prov_node_info prov_nodes[CONFIG_BLE_MESH_MAX_PROV_NODES]; + +struct unprov_dev_queue { + bt_mesh_addr_t addr; + u8_t uuid[16]; + u16_t oob_info; + u8_t bearer; + u8_t flags; +} __packed unprov_dev[CONFIG_BLE_MESH_WAIT_FOR_PROV_MAX_DEV_NUM] = { + [0 ... (CONFIG_BLE_MESH_WAIT_FOR_PROV_MAX_DEV_NUM - 1)] = { + .addr.type = 0xff, + .bearer = 0, + .flags = false, + }, +}; + +static unprov_adv_pkt_cb_t notify_unprov_adv_pkt_cb; + +#define BUF_TIMEOUT K_MSEC(400) + +#if defined(CONFIG_BLE_MESH_FAST_PROV) +#define RETRANSMIT_TIMEOUT K_MSEC(360) +#define TRANSACTION_TIMEOUT K_SECONDS(3) +#define PROVISION_TIMEOUT K_SECONDS(6) +#else +#define RETRANSMIT_TIMEOUT K_MSEC(500) +#define TRANSACTION_TIMEOUT K_SECONDS(30) +#define PROVISION_TIMEOUT K_SECONDS(60) +#endif /* CONFIG_BLE_MESH_FAST_PROV */ + +#if defined(CONFIG_BLE_MESH_PB_GATT) +#define PROV_BUF_HEADROOM 5 +#else +#define PROV_BUF_HEADROOM 0 +#endif + +#define PROV_BUF(name, len) \ + NET_BUF_SIMPLE_DEFINE(name, PROV_BUF_HEADROOM + len) + +static const struct bt_mesh_prov *prov; + +#if defined(CONFIG_BLE_MESH_PB_ADV) +static void send_link_open(const u8_t idx); +#endif + +static void prov_gen_dh_key(const u8_t idx); + +static void send_pub_key(const u8_t idx, u8_t oob); + +static void close_link(const u8_t idx, u8_t reason); + +#if defined(CONFIG_BLE_MESH_PB_ADV) +#define ADV_BUF_SIZE 65 + +static struct prov_adv_buf { + struct net_buf_simple buf; +} adv_buf[CONFIG_BLE_MESH_PBA_SAME_TIME]; + +static u8_t adv_buf_data[ADV_BUF_SIZE * CONFIG_BLE_MESH_PBA_SAME_TIME]; +#endif + +#define PROV_FREE_MEM(_idx, member) \ +{ \ + if (link[_idx].member) { \ + osi_free(link[_idx].member); \ + } \ +} + +/* Fast provisioning uses this structure for provisioning data */ +static struct bt_mesh_fast_prov_info { + u16_t net_idx; + const u8_t *net_key; + u8_t flags; + u32_t iv_index; + u16_t unicast_addr_min; + u16_t unicast_addr_max; +} fast_prov_info; + +static bool fast_prov_flag; + +#define FAST_PROV_FLAG_GET() fast_prov_flag + +void provisioner_pbg_count_dec(void) +{ + if (prov_ctx.pbg_count) { + prov_ctx.pbg_count--; + } +} + +void provisioner_pbg_count_inc(void) +{ + prov_ctx.pbg_count++; +} + +void provisioner_clear_link_conn_info(const u8_t addr[6]) +{ +#if defined(CONFIG_BLE_MESH_PB_GATT) + u8_t i; + + if (!addr) { + BT_ERR("%s, Invalid parameter", __func__); + return; + } + + BT_DBG("%s, Clear device %s info", __func__, bt_hex(addr, BLE_MESH_ADDR_LEN)); + + for (i = CONFIG_BLE_MESH_PBA_SAME_TIME; i < BLE_MESH_PROV_SAME_TIME; i++) { + if (!memcmp(link[i].addr.val, addr, BLE_MESH_ADDR_LEN)) { + link[i].connecting = false; + link[i].conn = NULL; + link[i].oob_info = 0x0; + memset(link[i].uuid, 0, 16); + memset(&link[i].addr, 0, sizeof(bt_mesh_addr_t)); + bt_mesh_atomic_test_and_clear_bit(link[i].flags, LINK_ACTIVE); + if (bt_mesh_atomic_test_and_clear_bit(link[i].flags, TIMEOUT_START)) { + k_delayed_work_cancel(&link[i].timeout); + } + return; + } + } + + BT_WARN("%s, Address %s is not found", __func__, bt_hex(addr, BLE_MESH_ADDR_LEN)); +#endif + return; +} + +const struct bt_mesh_prov *provisioner_get_prov_info(void) +{ + return prov; +} + +int provisioner_prov_reset_all_nodes(void) +{ + u16_t i; + + BT_DBG("%s", __func__); + + for (i = 0U; i < ARRAY_SIZE(prov_nodes); i++) { + if (prov_nodes[i].provisioned) { + memset(&prov_nodes[i], 0, sizeof(struct prov_node_info)); + } + } + + prov_ctx.node_count = 0; + + return 0; +} + +static int provisioner_dev_find(const bt_mesh_addr_t *addr, const u8_t uuid[16], u16_t *index) +{ + bool uuid_match = false; + bool addr_match = false; + u8_t zero[16] = {0}; + u16_t i = 0, j = 0; + int comp = 0; + + if (addr) { + comp = memcmp(addr->val, zero, BLE_MESH_ADDR_LEN); + } + + if ((!uuid && (!addr || (comp == 0) || (addr->type > BLE_MESH_ADDR_RANDOM))) || !index) { + return -EINVAL; + } + + /** Note: user may add a device into two unprov_dev array elements, + * one with device address, address type and another only + * with device UUID. We need to take this into consideration. + */ + if (uuid && memcmp(uuid, zero, 16)) { + for (i = 0; i < ARRAY_SIZE(unprov_dev); i++) { + if (!memcmp(unprov_dev[i].uuid, uuid, 16)) { + uuid_match = true; + break; + } + } + } + + if (addr && comp && (addr->type <= BLE_MESH_ADDR_RANDOM)) { + for (j = 0; j < ARRAY_SIZE(unprov_dev); j++) { + if (!memcmp(unprov_dev[j].addr.val, addr->val, BLE_MESH_ADDR_LEN) && + unprov_dev[j].addr.type == addr->type) { + addr_match = true; + break; + } + } + } + + if (!uuid_match && !addr_match) { + BT_DBG("%s, Device does not exist in queue", __func__); + return -ENODEV; + } + + if (uuid_match && addr_match && (i != j)) { + /** + * In this situation, copy address & type into device uuid + * array element, reset another element, rm_flag will be + * decided by uuid element. + */ + unprov_dev[i].addr.type = unprov_dev[j].addr.type; + memcpy(unprov_dev[i].addr.val, unprov_dev[j].addr.val, BLE_MESH_ADDR_LEN); + unprov_dev[i].bearer |= unprov_dev[j].bearer; + memset(&unprov_dev[j], 0x0, sizeof(struct unprov_dev_queue)); + } + + *index = uuid_match ? i : j; + return 0; +} + +static bool is_unprov_dev_being_provision(const u8_t uuid[16]) +{ + u16_t i; + +#if defined(CONFIG_BLE_MESH_FAST_PROV) + /** + * During Fast Provisioning test, we found that if a device has already being + * provisioned, there is still a chance that the Provisioner can receive the + * Unprovisioned Device Beacon from the device (because the device will stop + * Unprovisioned Device Beacon when Transaction ACK for Provisioning Complete + * is received). So in Fast Provisioning the Provisioner should ignore this. + */ + for (i = 0U; i < ARRAY_SIZE(prov_nodes); i++) { + if (prov_nodes[i].provisioned) { + if (!memcmp(prov_nodes[i].uuid, uuid, 16)) { + BT_WARN("Device has already been provisioned"); + return -EALREADY; + } + } + } +#endif + + for (i = 0U; i < BLE_MESH_PROV_SAME_TIME; i++) { +#if defined(CONFIG_BLE_MESH_PB_ADV) && defined(CONFIG_BLE_MESH_PB_GATT) + if (link[i].linking || link[i].connecting || + bt_mesh_atomic_test_bit(link[i].flags, LINK_ACTIVE)) { +#elif defined(CONFIG_BLE_MESH_PB_ADV) && !defined(CONFIG_BLE_MESH_PB_GATT) + if (link[i].linking || bt_mesh_atomic_test_bit(link[i].flags, LINK_ACTIVE)) { +#else + if (link[i].connecting || bt_mesh_atomic_test_bit(link[i].flags, LINK_ACTIVE)) { +#endif + if (!memcmp(link[i].uuid, uuid, 16)) { + BT_DBG("%s, Device is being provisioned", __func__); + return true; + } + } + } + + return false; +} + +static bool is_unprov_dev_uuid_match(const u8_t uuid[16]) +{ + if (prov_ctx.match_length && prov_ctx.match_value) { + if (memcmp(uuid + prov_ctx.match_offset, + prov_ctx.match_value, prov_ctx.match_length)) { + return false; + } + } + + return true; +} + +static int provisioner_check_unprov_dev_info(const u8_t uuid[16]) +{ + u16_t i; + + if (!uuid) { + BT_ERR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + /* Check if the device uuid matches configured value */ + if (is_unprov_dev_uuid_match(uuid) == false) { + BT_DBG("%s, Device uuid is not matched", __func__); + return -EIO; + } + + /* Check if this device is currently being provisioned. + * According to Zephyr's device code, if we connect with + * one device and start to provision it, we may still can + * receive the connectable prov adv pkt from this device. + * Here we check both PB-GATT and PB-ADV link status. + */ + if (is_unprov_dev_being_provision(uuid)) { + return -EALREADY; + } + + /* Check if this device is currently being provisioned. + * According to Zephyr's device code, if we connect with + * one device and start to provision it, we may still can + * receive the connectable prov adv pkt from this device. + * Here we check both PB-GATT and PB-ADV link status. + */ + if (is_unprov_dev_being_provision(uuid)) { + return -EALREADY; + } + + /* Check if the device has already been provisioned */ + for (i = 0U; i < ARRAY_SIZE(prov_nodes); i++) { + if (prov_nodes[i].provisioned) { + if (!memcmp(prov_nodes[i].uuid, uuid, 16)) { + BT_WARN("Provisioned before, start to provision again"); + provisioner_node_reset(i); + memset(&prov_nodes[i], 0, sizeof(struct prov_node_info)); + if (prov_ctx.node_count) { + prov_ctx.node_count--; + } + return 0; + } + } + } + + /* Check if the prov_nodes queue is full */ + if (prov_ctx.node_count == ARRAY_SIZE(prov_nodes)) { + BT_WARN("Current provisioned devices reach max limit"); + return -ENOMEM; + } + + return 0; +} + +#if defined(CONFIG_BLE_MESH_PB_ADV) +static int provisioner_start_prov_pb_adv(const u8_t uuid[16], + const bt_mesh_addr_t *addr, u16_t oob_info) +{ + u8_t zero[6] = {0}; + int addr_cmp; + u8_t i; + + if (!uuid || !addr) { + BT_ERR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + osi_mutex_lock(&prov_ctx.pb_adv_lock, OSI_MUTEX_MAX_TIMEOUT); + + if (is_unprov_dev_being_provision(uuid)) { + osi_mutex_unlock(&prov_ctx.pb_adv_lock); + return -EALREADY; + } + + addr_cmp = memcmp(addr->val, zero, BLE_MESH_ADDR_LEN); + + for (i = 0U; i < CONFIG_BLE_MESH_PBA_SAME_TIME; i++) { + if (!bt_mesh_atomic_test_bit(link[i].flags, LINK_ACTIVE) && !link[i].linking) { + memcpy(link[i].uuid, uuid, 16); + link[i].oob_info = oob_info; + if (addr_cmp && (addr->type <= BLE_MESH_ADDR_RANDOM)) { + link[i].addr.type = addr->type; + memcpy(link[i].addr.val, addr->val, BLE_MESH_ADDR_LEN); + } + send_link_open(i); + osi_mutex_unlock(&prov_ctx.pb_adv_lock); + return 0; + } + } + + BT_ERR("%s, No PB-ADV link is available", __func__); + osi_mutex_unlock(&prov_ctx.pb_adv_lock); + return -ENOMEM; +} +#endif /* CONFIG_BLE_MESH_PB_ADV */ + +#if defined(CONFIG_BLE_MESH_PB_GATT) +static int provisioner_start_prov_pb_gatt(const u8_t uuid[16], + const bt_mesh_addr_t *addr, u16_t oob_info) +{ + u8_t zero[6] = {0}; + int addr_cmp; + u8_t i; + + if (!uuid || !addr) { + BT_ERR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + osi_mutex_lock(&prov_ctx.pb_gatt_lock, OSI_MUTEX_MAX_TIMEOUT); + + if (is_unprov_dev_being_provision(uuid)) { + osi_mutex_unlock(&prov_ctx.pb_gatt_lock); + return -EALREADY; + } + + addr_cmp = memcmp(addr->val, zero, BLE_MESH_ADDR_LEN); + + for (i = CONFIG_BLE_MESH_PBA_SAME_TIME; i < BLE_MESH_PROV_SAME_TIME; i++) { + if (!link[i].connecting && !bt_mesh_atomic_test_bit(link[i].flags, LINK_ACTIVE)) { + memcpy(link[i].uuid, uuid, 16); + link[i].oob_info = oob_info; + if (addr_cmp && (addr->type <= BLE_MESH_ADDR_RANDOM)) { + link[i].addr.type = addr->type; + memcpy(link[i].addr.val, addr->val, BLE_MESH_ADDR_LEN); + } + if (bt_mesh_gattc_conn_create(&link[i].addr, BLE_MESH_UUID_MESH_PROV_VAL)) { + memset(link[i].uuid, 0, 16); + link[i].oob_info = 0x0; + memset(&link[i].addr, 0, sizeof(bt_mesh_addr_t)); + osi_mutex_unlock(&prov_ctx.pb_gatt_lock); + return -EIO; + } + /* If creating connection successfully, set connecting flag to 1 */ + link[i].connecting = true; + osi_mutex_unlock(&prov_ctx.pb_gatt_lock); + return 0; + } + } + + BT_ERR("%s, No PB-GATT link is available", __func__); + osi_mutex_unlock(&prov_ctx.pb_gatt_lock); + return -ENOMEM; +} +#endif /* CONFIG_BLE_MESH_PB_GATT */ + +int bt_mesh_provisioner_add_unprov_dev(struct bt_mesh_unprov_dev_add *add_dev, u8_t flags) +{ + bt_mesh_addr_t add_addr = {0}; + u8_t zero[16] = {0}; + int addr_cmp = 0; + int uuid_cmp = 0; + u16_t i; + int err; + + if (!add_dev) { + BT_ERR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + addr_cmp = memcmp(add_dev->addr, zero, BLE_MESH_ADDR_LEN); + uuid_cmp = memcmp(add_dev->uuid, zero, 16); + + if (add_dev->bearer == 0x0 || ((uuid_cmp == 0) && + ((addr_cmp == 0) || add_dev->addr_type > BLE_MESH_ADDR_RANDOM))) { + BT_ERR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + if ((add_dev->bearer & BLE_MESH_PROV_ADV) && (add_dev->bearer & BLE_MESH_PROV_GATT) && + (flags & START_PROV_NOW)) { + BT_ERR("%s, Can not start PB-ADV & PB-GATT simultaneouly", __func__); + return -EINVAL; + } + + if ((uuid_cmp == 0) && (flags & START_PROV_NOW)) { + BT_ERR("%s, Can not start provisioning with zero uuid", __func__); + return -EINVAL; + } + + if ((add_dev->bearer & BLE_MESH_PROV_GATT) && (flags & START_PROV_NOW) && + ((addr_cmp == 0) || add_dev->addr_type > BLE_MESH_ADDR_RANDOM)) { + BT_ERR("%s, Invalid device address for PB-GATT", __func__); + return -EINVAL; + } + + if (add_dev->bearer & BLE_MESH_PROV_GATT) { +#if !CONFIG_BLE_MESH_PB_GATT + BT_ERR("%s, Not support PB-GATT", __func__); + return -EINVAL; +#endif + } + + if (add_dev->bearer & BLE_MESH_PROV_ADV) { +#if !CONFIG_BLE_MESH_PB_ADV + BT_ERR("%s, Not support PB-ADV", __func__); + return -EINVAL; +#endif + } + + add_addr.type = add_dev->addr_type; + memcpy(add_addr.val, add_dev->addr, BLE_MESH_ADDR_LEN); + + err = provisioner_dev_find(&add_addr, add_dev->uuid, &i); + if (err == -EINVAL) { + BT_ERR("%s, Invalid parameter", __func__); + return err; + } else if (err == 0) { + if (!(add_dev->bearer & unprov_dev[i].bearer)) { + BT_WARN("Add device with only bearer updated"); + unprov_dev[i].bearer |= add_dev->bearer; + } else { + BT_WARN("Device already exists in queue"); + } + goto start; + } + + for (i = 0U; i < ARRAY_SIZE(unprov_dev); i++) { + if (unprov_dev[i].bearer) { + continue; + } + if (addr_cmp && (add_dev->addr_type <= BLE_MESH_ADDR_RANDOM)) { + unprov_dev[i].addr.type = add_dev->addr_type; + memcpy(unprov_dev[i].addr.val, add_dev->addr, BLE_MESH_ADDR_LEN); + } + if (uuid_cmp) { + memcpy(unprov_dev[i].uuid, add_dev->uuid, 16); + } + unprov_dev[i].bearer = add_dev->bearer & BIT_MASK(2); + unprov_dev[i].flags = flags & BIT_MASK(3); + goto start; + } + + /* If queue is full, find flushable device and replace it */ + for (i = 0U; i < ARRAY_SIZE(unprov_dev); i++) { + if (unprov_dev[i].flags & FLUSHABLE_DEV) { + memset(&unprov_dev[i], 0, sizeof(struct unprov_dev_queue)); + if (addr_cmp && (add_dev->addr_type <= BLE_MESH_ADDR_RANDOM)) { + unprov_dev[i].addr.type = add_dev->addr_type; + memcpy(unprov_dev[i].addr.val, add_dev->addr, BLE_MESH_ADDR_LEN); + } + if (uuid_cmp) { + memcpy(unprov_dev[i].uuid, add_dev->uuid, 16); + } + unprov_dev[i].bearer = add_dev->bearer & BIT_MASK(2); + unprov_dev[i].flags = flags & BIT_MASK(3); + goto start; + } + } + + BT_ERR("%s, Unprovisioned device queue is full", __func__); + return -ENOMEM; + +start: + if (!(flags & START_PROV_NOW)) { + return 0; + } + + /* Check if current provisioned node count + active link reach max limit */ + if (prov_ctx.node_count + prov_ctx.pba_count + \ + prov_ctx.pbg_count >= ARRAY_SIZE(prov_nodes)) { + BT_WARN("%s, Node count + active link count reach max limit", __func__); + return -EIO; + } + + if ((err = provisioner_check_unprov_dev_info(add_dev->uuid))) { + return err; + } + + if (add_dev->bearer & BLE_MESH_PROV_ADV) { +#if defined(CONFIG_BLE_MESH_PB_ADV) + if (prov_ctx.pba_count == CONFIG_BLE_MESH_PBA_SAME_TIME) { + BT_WARN("%s, Current PB-ADV links reach max limit", __func__); + return -EIO; + } + if ((err = provisioner_start_prov_pb_adv( + add_dev->uuid, &add_addr, add_dev->oob_info))) { + return err; + } +#endif + } else if (add_dev->bearer & BLE_MESH_PROV_GATT) { +#if defined(CONFIG_BLE_MESH_PB_GATT) + if (prov_ctx.pbg_count == CONFIG_BLE_MESH_PBG_SAME_TIME) { + BT_WARN("%s, Current PB-GATT links reach max limit", __func__); + return -EIO; + } + if ((err = provisioner_start_prov_pb_gatt( + add_dev->uuid, &add_addr, add_dev->oob_info))) { + return err; + } +#endif + } + + return 0; +} + +int bt_mesh_provisioner_delete_device(struct bt_mesh_device_delete *del_dev) +{ + /** + * Three Situations: + * 1. device is not being/been provisioned, just remove from device queue. + * 2. device is being provisioned, need to close link & remove from device queue. + * 3. device is been provisioned, need to send config_node_reset and may need to + * remove from device queue. config _node_reset can be added in function + * provisioner_node_reset() in provisioner_main.c. + */ + bt_mesh_addr_t del_addr = {0}; + u8_t zero[16] = {0}; + bool addr_match = false; + bool uuid_match = false; + int addr_cmp = 0; + int uuid_cmp = 0; + u16_t i; + int err; + + if (!del_dev) { + BT_ERR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + addr_cmp = memcmp(del_dev->addr, zero, BLE_MESH_ADDR_LEN); + uuid_cmp = memcmp(del_dev->uuid, zero, 16); + + if ((uuid_cmp == 0) && ((addr_cmp == 0) || del_dev->addr_type > BLE_MESH_ADDR_RANDOM)) { + BT_ERR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + del_addr.type = del_dev->addr_type; + memcpy(del_addr.val, del_dev->addr, BLE_MESH_ADDR_LEN); + + /* First: find if the device is in the device queue */ + err = provisioner_dev_find(&del_addr, del_dev->uuid, &i); + if (err) { + BT_DBG("%s, Device is not in the queue", __func__); + } else { + memset(&unprov_dev[i], 0x0, sizeof(struct unprov_dev_queue)); + } + + /* Second: find if the device is being provisioned */ + for (i = 0U; i < ARRAY_SIZE(link); i++) { + if (addr_cmp && (del_dev->addr_type <= BLE_MESH_ADDR_RANDOM)) { + if (!memcmp(link[i].addr.val, del_dev->addr, BLE_MESH_ADDR_LEN) && + link[i].addr.type == del_dev->addr_type) { + addr_match = true; + } + } + if (uuid_cmp) { + if (!memcmp(link[i].uuid, del_dev->uuid, 16)) { + uuid_match = true; + } + } + if (addr_match || uuid_match) { + close_link(i, CLOSE_REASON_FAILED); + break; + } + } + + /* Third: find if the device is been provisioned */ + for (i = 0U; i < ARRAY_SIZE(prov_nodes); i++) { + if (addr_cmp && (del_dev->addr_type <= BLE_MESH_ADDR_RANDOM)) { + if (!memcmp(prov_nodes[i].addr.val, del_dev->addr, BLE_MESH_ADDR_LEN) && + prov_nodes[i].addr.type == del_dev->addr_type) { + addr_match = true; + } + } + if (uuid_cmp) { + if (!memcmp(prov_nodes[i].uuid, del_dev->uuid, 16)) { + uuid_match = true; + } + } + if (addr_match || uuid_match) { + memset(&prov_nodes[i], 0, sizeof(struct prov_node_info)); + provisioner_node_reset(i); + if (prov_ctx.node_count) { + prov_ctx.node_count--; + } + break; + } + } + + return 0; +} + +int bt_mesh_provisioner_set_dev_uuid_match(u8_t offset, u8_t length, + const u8_t *match, bool prov_flag) +{ + if (length && (!match || (offset + length > 16))) { + BT_ERR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + if (length && !prov_ctx.match_value) { + prov_ctx.match_value = osi_calloc(16); + if (!prov_ctx.match_value) { + BT_ERR("%s, Failed to allocate memory", __func__); + return -ENOMEM; + } + } + + prov_ctx.match_offset = offset; + prov_ctx.match_length = length; + if (length) { + memcpy(prov_ctx.match_value, match, length); + } + prov_ctx.prov_after_match = prov_flag; + + return 0; +} + +int bt_mesh_prov_adv_pkt_cb_register(unprov_adv_pkt_cb_t cb) +{ + if (!cb) { + BT_ERR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + notify_unprov_adv_pkt_cb = cb; + return 0; +} + +int bt_mesh_provisioner_set_prov_data_info(struct bt_mesh_prov_data_info *info) +{ + const u8_t *key = NULL; + + if (!info || info->flag == 0) { + return -EINVAL; + } + + if (info->flag & NET_IDX_FLAG) { + key = provisioner_net_key_get(info->net_idx); + if (!key) { + BT_ERR("%s, Failed to get NetKey", __func__); + return -EINVAL; + } + prov_ctx.curr_net_idx = info->net_idx; + } else if (info->flag & FLAGS_FLAG) { + prov_ctx.curr_flags = info->flags; + } else if (info->flag & IV_INDEX_FLAG) { + prov_ctx.curr_iv_index = info->iv_index; + } + + return 0; +} + +/* The following APIs are for fast provisioning */ + +void provisioner_set_fast_prov_flag(bool flag) +{ + fast_prov_flag = flag; +} + +u8_t provisioner_set_fast_prov_net_idx(const u8_t *net_key, u16_t net_idx) +{ + fast_prov_info.net_idx = net_idx; + fast_prov_info.net_key = net_key; + + if (!net_key) { + BT_WARN("%s, Wait for NetKey for fast provisioning", __func__); + return 0x01; /*status: wait for net_key */ + } + + return 0x0; /* status: success */ +} + +u16_t provisioner_get_fast_prov_net_idx(void) +{ + return fast_prov_info.net_idx; +} + +u8_t bt_mesh_set_fast_prov_unicast_addr_range(u16_t min, u16_t max) +{ + if (!BLE_MESH_ADDR_IS_UNICAST(min) || !BLE_MESH_ADDR_IS_UNICAST(max)) { + BT_ERR("%s, Not a unicast address", __func__); + return 0x01; /* status: not a unicast address */ + } + + if (min > max) { + BT_ERR("%s, Min bigger than max", __func__); + return 0x02; /* status: min is bigger than max */ + } + + if (min <= fast_prov_info.unicast_addr_max) { + BT_ERR("%s, Address overlap", __func__); + return 0x03; /* status: address overlaps with current value */ + } + + fast_prov_info.unicast_addr_min = min; + fast_prov_info.unicast_addr_max = max; + + prov_ctx.current_addr = fast_prov_info.unicast_addr_min; + + return 0x0; /* status: success */ +} + +void bt_mesh_set_fast_prov_flags_iv_index(u8_t flags, u32_t iv_index) +{ + /* BIT0: Key Refreash flag, BIT1: IV Update flag */ + fast_prov_info.flags = flags & BIT_MASK(2); + fast_prov_info.iv_index = iv_index; +} + +#if defined(CONFIG_BLE_MESH_PB_ADV) +static struct net_buf_simple *bt_mesh_pba_get_buf(const u8_t idx) +{ + struct net_buf_simple *buf = &(adv_buf[idx].buf); + + net_buf_simple_reset(buf); + + return buf; +} +#endif /* CONFIG_BLE_MESH_PB_ADV */ + +static void prov_memory_free(const u8_t idx) +{ + PROV_FREE_MEM(idx, dhkey); + PROV_FREE_MEM(idx, auth); + PROV_FREE_MEM(idx, conf); + PROV_FREE_MEM(idx, conf_salt); + PROV_FREE_MEM(idx, conf_key); + PROV_FREE_MEM(idx, conf_inputs); + PROV_FREE_MEM(idx, prov_salt); +} + +#if defined(CONFIG_BLE_MESH_PB_ADV) +static void buf_sent(int err, void *user_data) +{ + u8_t idx = (int)user_data; + + if (!link[idx].tx.buf[0]) { + return; + } + + k_delayed_work_submit(&link[idx].tx.retransmit, RETRANSMIT_TIMEOUT); +} + +static struct bt_mesh_send_cb buf_sent_cb = { + .end = buf_sent, +}; + +static void free_segments(const u8_t idx) +{ + u8_t i; + + for (i = 0U; i < ARRAY_SIZE(link[idx].tx.buf); i++) { + struct net_buf *buf = link[idx].tx.buf[i]; + + if (!buf) { + break; + } + + link[idx].tx.buf[i] = NULL; + /* Mark as canceled */ + BLE_MESH_ADV(buf)->busy = 0; + /** Change by Espressif. Add this to avoid buf->ref is 2 which will + * cause lack of buf. + */ + if (buf->ref > 1) { + buf->ref = 1; + } + net_buf_unref(buf); + } +} + +static void prov_clear_tx(const u8_t idx) +{ + BT_DBG("%s", __func__); + + k_delayed_work_cancel(&link[idx].tx.retransmit); + + free_segments(idx); +} + +static void reset_link(const u8_t idx, u8_t reason) +{ + prov_clear_tx(idx); + + if (bt_mesh_atomic_test_and_clear_bit(link[idx].flags, TIMEOUT_START)) { + k_delayed_work_cancel(&link[idx].timeout); + } + + if (prov->prov_link_close) { + prov->prov_link_close(BLE_MESH_PROV_ADV, reason); + } + + prov_memory_free(idx); + +#if defined(CONFIG_BLE_MESH_USE_DUPLICATE_SCAN) + /* Remove the link id from exceptional list */ + bt_mesh_update_exceptional_list(BLE_MESH_EXCEP_LIST_REMOVE, + BLE_MESH_EXCEP_INFO_MESH_LINK_ID, &link[idx].link_id); +#endif + + /* Clear everything except the retransmit delayed work config */ + memset(&link[idx], 0, offsetof(struct prov_link, tx.retransmit)); + + link[idx].pending_ack = XACT_NVAL; + link[idx].rx.prev_id = XACT_NVAL; + + if (bt_mesh_pub_key_get()) { + bt_mesh_atomic_set_bit(link[idx].flags, LOCAL_PUB_KEY); + } + + link[idx].rx.buf = bt_mesh_pba_get_buf(idx); + + if (prov_ctx.pba_count) { + prov_ctx.pba_count--; + } +} + +static struct net_buf *adv_buf_create(void) +{ + struct net_buf *buf; + + buf = bt_mesh_adv_create(BLE_MESH_ADV_PROV, PROV_XMIT, BUF_TIMEOUT); + if (!buf) { + BT_ERR("Out of provisioning buffers"); + return NULL; + } + + return buf; +} + +static void ack_complete(u16_t duration, int err, void *user_data) +{ + u8_t idx = (int)user_data; + + BT_DBG("xact %u complete", link[idx].pending_ack); + + link[idx].pending_ack = XACT_NVAL; +} + +static void gen_prov_ack_send(const u8_t idx, u8_t xact_id) +{ + static const struct bt_mesh_send_cb cb = { + .start = ack_complete, + }; + const struct bt_mesh_send_cb *complete; + struct net_buf *buf; + + BT_DBG("xact_id %u", xact_id); + + if (link[idx].pending_ack == xact_id) { + BT_DBG("Not sending duplicate ack"); + return; + } + + buf = adv_buf_create(); + if (!buf) { + return; + } + + if (link[idx].pending_ack == XACT_NVAL) { + link[idx].pending_ack = xact_id; + complete = &cb; + } else { + complete = NULL; + } + + net_buf_add_be32(buf, link[idx].link_id); + net_buf_add_u8(buf, xact_id); + net_buf_add_u8(buf, GPC_ACK); + + bt_mesh_adv_send(buf, complete, (void *)(int)idx); + net_buf_unref(buf); +} + +static void send_reliable(const u8_t idx) +{ + u8_t i; + + link[idx].tx.start = k_uptime_get(); + + for (i = 0U; i < ARRAY_SIZE(link[idx].tx.buf); i++) { + struct net_buf *buf = link[idx].tx.buf[i]; + + if (!buf) { + break; + } + + if (i + 1 < ARRAY_SIZE(link[idx].tx.buf) && link[idx].tx.buf[i + 1]) { + bt_mesh_adv_send(buf, NULL, NULL); + } else { + bt_mesh_adv_send(buf, &buf_sent_cb, (void *)(int)idx); + } + } +} + +static int bearer_ctl_send(const u8_t idx, u8_t op, void *data, u8_t data_len) +{ + struct net_buf *buf; + + BT_DBG("op 0x%02x data_len %u", op, data_len); + + prov_clear_tx(idx); + + buf = adv_buf_create(); + if (!buf) { + return -ENOBUFS; + } + + net_buf_add_be32(buf, link[idx].link_id); + /* Transaction ID, always 0 for Bearer messages */ + net_buf_add_u8(buf, 0x00); + net_buf_add_u8(buf, GPC_CTL(op)); + net_buf_add_mem(buf, data, data_len); + + link[idx].tx.buf[0] = buf; + send_reliable(idx); + + /** We can also use buf->ref and a flag to decide that + * link close has been sent 3 times. + * Here we use another way: use retransmit timer and need + * to make sure the timer is not cancelled during sending + * link close pdu, so we add link[i].tx.id = 0 + */ + if (op == LINK_CLOSE) { + u8_t reason = *(u8_t *)data; + link[idx].send_link_close = ((reason & BIT_MASK(2)) << 1) | BIT(0); + link[idx].tx.trans_id = 0; + } + + return 0; +} + +static void send_link_open(const u8_t idx) +{ + u8_t j; + + /** Generate link ID, and may need to check if this id is + * currently being used, which may will not happen ever. + */ + bt_mesh_rand(&link[idx].link_id, sizeof(u32_t)); + while (1) { + for (j = 0U; j < CONFIG_BLE_MESH_PBA_SAME_TIME; j++) { + if (bt_mesh_atomic_test_bit(link[j].flags, LINK_ACTIVE) || link[j].linking) { + if (link[idx].link_id == link[j].link_id) { + bt_mesh_rand(&link[idx].link_id, sizeof(u32_t)); + break; + } + } + } + if (j == CONFIG_BLE_MESH_PBA_SAME_TIME) { + break; + } + } + +#if defined(CONFIG_BLE_MESH_USE_DUPLICATE_SCAN) + /* Add the link id into exceptional list */ + bt_mesh_update_exceptional_list(BLE_MESH_EXCEP_LIST_ADD, + BLE_MESH_EXCEP_INFO_MESH_LINK_ID, &link[idx].link_id); +#endif + + bearer_ctl_send(idx, LINK_OPEN, link[idx].uuid, 16); + + /* If Provisioner sets LINK_ACTIVE flag once Link Open is sent, we have + * no need to use linking flag (like PB-GATT connecting) to prevent the + * stored device info (UUID, oob_info) being replaced by other received + * unprovisioned device beacons. + * But if Provisioner sets LINK_ACTIVE flag after Link ACK is received, + * we need to use linking flag to prevent device info being replaced. + * Currently we set LINK_ACTIVE flag after sending Link Open. + */ + link[idx].linking = true; + + /* Set LINK_ACTIVE just to be in compatibility with current Zephyr code */ + bt_mesh_atomic_set_bit(link[idx].flags, LINK_ACTIVE); + + if (prov->prov_link_open) { + prov->prov_link_open(BLE_MESH_PROV_ADV); + } + + prov_ctx.pba_count++; +} + +static u8_t last_seg(u8_t len) +{ + if (len <= START_PAYLOAD_MAX) { + return 0; + } + + len -= START_PAYLOAD_MAX; + + return 1 + (len / CONT_PAYLOAD_MAX); +} + +static inline u8_t next_transaction_id(const u8_t idx) +{ + if (link[idx].tx.trans_id > 0x7F) { + link[idx].tx.trans_id = 0x0; + } + return link[idx].tx.trans_id++; +} + +static int prov_send_adv(const u8_t idx, struct net_buf_simple *msg) +{ + struct net_buf *start, *buf; + u8_t seg_len, seg_id; + u8_t xact_id; + s32_t timeout = PROVISION_TIMEOUT; + + BT_DBG("%s, len %u: %s", __func__, msg->len, bt_hex(msg->data, msg->len)); + + prov_clear_tx(idx); + + start = adv_buf_create(); + if (!start) { + return -ENOBUFS; + } + + xact_id = next_transaction_id(idx); + net_buf_add_be32(start, link[idx].link_id); + net_buf_add_u8(start, xact_id); + + net_buf_add_u8(start, GPC_START(last_seg(msg->len))); + net_buf_add_be16(start, msg->len); + net_buf_add_u8(start, bt_mesh_fcs_calc(msg->data, msg->len)); + + link[idx].tx.buf[0] = start; + /* Changed by Espressif, get message type */ + link[idx].tx_pdu_type = msg->data[0]; + + seg_len = MIN(msg->len, START_PAYLOAD_MAX); + BT_DBG("seg 0 len %u: %s", seg_len, bt_hex(msg->data, seg_len)); + net_buf_add_mem(start, msg->data, seg_len); + net_buf_simple_pull(msg, seg_len); + + buf = start; + for (seg_id = 1; msg->len > 0; seg_id++) { + if (seg_id >= ARRAY_SIZE(link[idx].tx.buf)) { + BT_ERR("%s, Too big message", __func__); + free_segments(idx); + return -E2BIG; + } + + buf = adv_buf_create(); + if (!buf) { + free_segments(idx); + return -ENOBUFS; + } + + link[idx].tx.buf[seg_id] = buf; + + seg_len = MIN(msg->len, CONT_PAYLOAD_MAX); + + BT_DBG("seg_id %u len %u: %s", seg_id, seg_len, + bt_hex(msg->data, seg_len)); + + net_buf_add_be32(buf, link[idx].link_id); + net_buf_add_u8(buf, xact_id); + net_buf_add_u8(buf, GPC_CONT(seg_id)); + net_buf_add_mem(buf, msg->data, seg_len); + net_buf_simple_pull(msg, seg_len); + } + + send_reliable(idx); + +#if defined(CONFIG_BLE_MESH_FAST_PROV) + if (link[idx].tx_pdu_type >= PROV_DATA) { + timeout = K_SECONDS(60); + } +#endif + if (!bt_mesh_atomic_test_and_set_bit(link[idx].flags, TIMEOUT_START)) { + k_delayed_work_submit(&link[idx].timeout, timeout); + } + + return 0; +} +#endif /* CONFIG_BLE_MESH_PB_ADV */ + +#if defined(CONFIG_BLE_MESH_PB_GATT) +static int prov_send_gatt(const u8_t idx, struct net_buf_simple *msg) +{ + int err; + + if (!link[idx].conn) { + return -ENOTCONN; + } + + err = provisioner_proxy_send(link[idx].conn, BLE_MESH_PROXY_PROV, msg); + if (err) { + BT_ERR("%s, Failed to send PB-GATT pdu", __func__); + return err; + } + + if (!bt_mesh_atomic_test_and_set_bit(link[idx].flags, TIMEOUT_START)) { + k_delayed_work_submit(&link[idx].timeout, PROVISION_TIMEOUT); + } + + return 0; +} +#endif /* CONFIG_BLE_MESH_PB_GATT */ + +static inline int prov_send(const u8_t idx, struct net_buf_simple *buf) +{ +#if defined(CONFIG_BLE_MESH_PB_ADV) + if (idx < CONFIG_BLE_MESH_PBA_SAME_TIME) { + return prov_send_adv(idx, buf); + } +#endif + +#if defined(CONFIG_BLE_MESH_PB_GATT) + if (idx < BLE_MESH_PROV_SAME_TIME +#if defined(CONFIG_BLE_MESH_PB_ADV) + && idx >= CONFIG_BLE_MESH_PBA_SAME_TIME +#endif + ) { + return prov_send_gatt(idx, buf); + } +#endif + + BT_ERR("%s, Invalid link index %d", __func__, idx); + return -EINVAL; +} + +static void prov_buf_init(struct net_buf_simple *buf, u8_t type) +{ + net_buf_simple_reserve(buf, PROV_BUF_HEADROOM); + net_buf_simple_add_u8(buf, type); +} + +static void prov_invite(const u8_t idx, const u8_t *data) +{ + BT_DBG("%s", __func__); +} + +static void prov_start(const u8_t idx, const u8_t *data) +{ + BT_DBG("%s", __func__); +} + +static void prov_data(const u8_t idx, const u8_t *data) +{ + BT_DBG("%s", __func__); +} + +static void send_invite(const u8_t idx) +{ + PROV_BUF(buf, 2); + + prov_buf_init(&buf, PROV_INVITE); + + net_buf_simple_add_u8(&buf, prov->prov_attention); + + link[idx].conf_inputs[0] = prov->prov_attention; + + if (prov_send(idx, &buf)) { + BT_ERR("%s, Failed to send Provisioning Invite", __func__); + close_link(idx, CLOSE_REASON_FAILED); + return; + } + + link[idx].expect = PROV_CAPABILITIES; +} + +static void prov_capabilities(const u8_t idx, const u8_t *data) +{ + PROV_BUF(buf, 6); + u16_t algorithms, output_action, input_action; + u8_t element_num, pub_key_oob, static_oob, + output_size, input_size; + u8_t auth_method, auth_action, auth_size; + + element_num = data[0]; + BT_DBG("Elements: %u", element_num); + if (!element_num) { + BT_ERR("%s, Invalid element number", __func__); + goto fail; + } + link[idx].element_num = element_num; + + algorithms = sys_get_be16(&data[1]); + BT_DBG("Algorithms: %u", algorithms); + if (algorithms != BIT(PROV_ALG_P256)) { + BT_ERR("%s, Invalid algorithms", __func__); + goto fail; + } + + pub_key_oob = data[3]; + BT_DBG("Public Key Type: 0x%02x", pub_key_oob); + if (pub_key_oob > 0x01) { + BT_ERR("%s, Invalid public key type", __func__); + goto fail; + } + pub_key_oob = ((prov->prov_pub_key_oob && + prov->prov_pub_key_oob_cb) ? pub_key_oob : 0x00); + + static_oob = data[4]; + BT_DBG("Static OOB Type: 0x%02x", static_oob); + if (static_oob > 0x01) { + BT_ERR("%s, Invalid Static OOB type", __func__); + goto fail; + } + static_oob = (prov->prov_static_oob_val ? static_oob : 0x00); + + output_size = data[5]; + BT_DBG("Output OOB Size: %u", output_size); + if (output_size > 0x08) { + BT_ERR("%s, Invalid Output OOB size", __func__); + goto fail; + } + + output_action = sys_get_be16(&data[6]); + BT_DBG("Output OOB Action: 0x%04x", output_action); + if (output_action > 0x1f) { + BT_ERR("%s, Invalid Output OOB action", __func__); + goto fail; + } + + /* Provisioner select output action */ + if (prov->prov_input_num && output_size) { + output_action = __builtin_ctz(output_action); + } else { + output_size = 0x0; + output_action = 0x0; + } + + input_size = data[8]; + BT_DBG("Input OOB Size: %u", input_size); + if (input_size > 0x08) { + BT_ERR("%s, Invalid Input OOB size", __func__); + goto fail; + } + + input_action = sys_get_be16(&data[9]); + BT_DBG("Input OOB Action: 0x%04x", input_action); + if (input_action > 0x0f) { + BT_ERR("%s, Invalid Input OOB action", __func__); + goto fail; + } + + /* Make sure received pdu is ok and cancel the timeout timer */ + if (bt_mesh_atomic_test_and_clear_bit(link[idx].flags, TIMEOUT_START)) { + k_delayed_work_cancel(&link[idx].timeout); + } + + /* Provisioner select input action */ + if (prov->prov_output_num && input_size) { + input_action = __builtin_ctz(input_action); + } else { + input_size = 0x0; + input_action = 0x0; + } + + if (static_oob) { + /* if static oob is valid, just use static oob */ + auth_method = AUTH_METHOD_STATIC; + auth_action = 0x00; + auth_size = 0x00; + } else { + if (!output_size && !input_size) { + auth_method = AUTH_METHOD_NO_OOB; + auth_action = 0x00; + auth_size = 0x00; + } else if (!output_size && input_size) { + auth_method = AUTH_METHOD_INPUT; + auth_action = (u8_t)input_action; + auth_size = input_size; + } else { + auth_method = AUTH_METHOD_OUTPUT; + auth_action = (u8_t)output_action; + auth_size = output_size; + } + } + + /* Store provisioning capbilities value in conf_inputs */ + memcpy(&link[idx].conf_inputs[1], data, 11); + + prov_buf_init(&buf, PROV_START); + net_buf_simple_add_u8(&buf, prov->prov_algorithm); + net_buf_simple_add_u8(&buf, pub_key_oob); + net_buf_simple_add_u8(&buf, auth_method); + net_buf_simple_add_u8(&buf, auth_action); + net_buf_simple_add_u8(&buf, auth_size); + + memcpy(&link[idx].conf_inputs[12], &buf.data[1], 5); + + if (prov_send(idx, &buf)) { + BT_ERR("%s, Failed to send Provisioning Start", __func__); + goto fail; + } + + link[idx].auth_method = auth_method; + link[idx].auth_action = auth_action; + link[idx].auth_size = auth_size; + + /** After prov start sent, use OOB to get remote public key. + * And we just follow the procedure in Figure 5.15 of Section + * 5.4.2.3 of Mesh Profile Spec. + */ + if (pub_key_oob) { + if (prov->prov_pub_key_oob_cb(idx)) { + BT_ERR("%s, Failed to notify input OOB Public Key", __func__); + goto fail; + } + } + + /** If using PB-ADV, need to listen for transaction ack, + * after ack is received, provisioner can send public key. + */ +#if defined(CONFIG_BLE_MESH_PB_ADV) + if (idx < CONFIG_BLE_MESH_PBA_SAME_TIME) { + link[idx].expect_ack_for = PROV_START; + return; + } +#endif /* CONFIG_BLE_MESH_PB_ADV */ + + send_pub_key(idx, pub_key_oob); + return; + +fail: + close_link(idx, CLOSE_REASON_FAILED); + return; +} + +static bt_mesh_output_action_t output_action(u8_t action) +{ + switch (action) { + case OUTPUT_OOB_BLINK: + return BLE_MESH_BLINK; + case OUTPUT_OOB_BEEP: + return BLE_MESH_BEEP; + case OUTPUT_OOB_VIBRATE: + return BLE_MESH_VIBRATE; + case OUTPUT_OOB_NUMBER: + return BLE_MESH_DISPLAY_NUMBER; + case OUTPUT_OOB_STRING: + return BLE_MESH_DISPLAY_STRING; + default: + return BLE_MESH_NO_OUTPUT; + } +} + +static bt_mesh_input_action_t input_action(u8_t action) +{ + switch (action) { + case INPUT_OOB_PUSH: + return BLE_MESH_PUSH; + case INPUT_OOB_TWIST: + return BLE_MESH_TWIST; + case INPUT_OOB_NUMBER: + return BLE_MESH_ENTER_NUMBER; + case INPUT_OOB_STRING: + return BLE_MESH_ENTER_STRING; + default: + return BLE_MESH_NO_INPUT; + } +} + +static int prov_auth(const u8_t idx, u8_t method, u8_t action, u8_t size) +{ + bt_mesh_output_action_t output; + bt_mesh_input_action_t input; + + link[idx].auth = (u8_t *)osi_calloc(PROV_AUTH_VAL_SIZE); + if (!link[idx].auth) { + BT_ERR("%s, Failed to allocate memory", __func__); + close_link(idx, CLOSE_REASON_FAILED); + return -ENOMEM; + } + + switch (method) { + case AUTH_METHOD_NO_OOB: + if (action || size) { + return -EINVAL; + } + memset(link[idx].auth, 0, 16); + return 0; + + case AUTH_METHOD_STATIC: + if (action || size) { + return -EINVAL; + } + memcpy(link[idx].auth + 16 - prov->prov_static_oob_len, + prov->prov_static_oob_val, prov->prov_static_oob_len); + memset(link[idx].auth, 0, 16 - prov->prov_static_oob_len); + return 0; + + case AUTH_METHOD_OUTPUT: + /* Use auth_action to get device output action */ + output = output_action(action); + if (!output) { + return -EINVAL; + } + return prov->prov_input_num(AUTH_METHOD_OUTPUT, output, size, idx); + + case AUTH_METHOD_INPUT: + /* Use auth_action to get device input action */ + input = input_action(action); + if (!input) { + return -EINVAL; + } + + /* Provisioner ouputs number/string and wait for device's Provisioning Input Complete PDU */ + link[idx].expect = PROV_INPUT_COMPLETE; + + if (input == BLE_MESH_ENTER_STRING) { + unsigned char str[9]; + u8_t j; + + bt_mesh_rand(str, size); + /* Normalize to '0' .. '9' & 'A' .. 'Z' */ + for (j = 0; j < size; j++) { + str[j] %= 36; + if (str[j] < 10) { + str[j] += '0'; + } else { + str[j] += 'A' - 10; + } + } + str[size] = '\0'; + + memcpy(link[idx].auth, str, size); + memset(link[idx].auth + size, 0, sizeof(link[idx].auth) - size); + + return prov->prov_output_num(AUTH_METHOD_INPUT, input, str, size, idx); + } else { + u32_t div[8] = { 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 }; + u32_t num; + + bt_mesh_rand(&num, sizeof(num)); + num %= div[size - 1]; + + sys_put_be32(num, &link[idx].auth[12]); + memset(link[idx].auth, 0, 12); + + return prov->prov_output_num(AUTH_METHOD_INPUT, input, &num, size, idx); + } + + default: + return -EINVAL; + } +} + +static void send_confirm(const u8_t idx) +{ + PROV_BUF(buf, 17); + + BT_DBG("ConfInputs[0] %s", bt_hex(link[idx].conf_inputs, 64)); + BT_DBG("ConfInputs[64] %s", bt_hex(link[idx].conf_inputs + 64, 64)); + BT_DBG("ConfInputs[128] %s", bt_hex(link[idx].conf_inputs + 128, 17)); + + link[idx].conf_salt = (u8_t *)osi_calloc(PROV_CONF_SALT_SIZE); + if (!link[idx].conf_salt) { + BT_ERR("%s, Failed to allocate memory", __func__); + goto fail; + } + + link[idx].conf_key = (u8_t *)osi_calloc(PROV_CONF_KEY_SIZE); + if (!link[idx].conf_key) { + BT_ERR("%s, Failed to allocate memory", __func__); + goto fail; + } + + if (bt_mesh_prov_conf_salt(link[idx].conf_inputs, link[idx].conf_salt)) { + BT_ERR("%s, Failed to generate confirmation salt", __func__); + goto fail; + } + + BT_DBG("ConfirmationSalt: %s", bt_hex(link[idx].conf_salt, 16)); + + if (bt_mesh_prov_conf_key(link[idx].dhkey, link[idx].conf_salt, link[idx].conf_key)) { + BT_ERR("%s, Failed to generate confirmation key", __func__); + goto fail; + } + + BT_DBG("ConfirmationKey: %s", bt_hex(link[idx].conf_key, 16)); + + /** Provisioner use the same random number for each provisioning + * device, if different random need to be used, here provisioner + * should allocate memory for rand and call bt_mesh_rand() every time. + */ + if (!(prov_ctx.rand_gen_done & BIT(0))) { + if (bt_mesh_rand(prov_ctx.random, 16)) { + BT_ERR("%s, Failed to generate random number", __func__); + goto fail; + } + link[idx].rand = prov_ctx.random; + prov_ctx.rand_gen_done |= BIT(0); + } else { + /* Provisioner random has already been generated. */ + link[idx].rand = prov_ctx.random; + } + + BT_DBG("LocalRandom: %s", bt_hex(link[idx].rand, 16)); + + prov_buf_init(&buf, PROV_CONFIRM); + + if (bt_mesh_prov_conf(link[idx].conf_key, link[idx].rand, link[idx].auth, + net_buf_simple_add(&buf, 16))) { + BT_ERR("%s, Failed to generate confirmation value", __func__); + goto fail; + } + + if (prov_send(idx, &buf)) { + BT_ERR("%s, Failed to send Provisioning Confirm", __func__); + goto fail; + } + + link[idx].expect = PROV_CONFIRM; + return; + +fail: + close_link(idx, CLOSE_REASON_FAILED); + return; +} + +int bt_mesh_prov_set_oob_input_data(const u8_t idx, const u8_t *val, bool num_flag) +{ + /** This function should be called in the prov_input_num + * callback, after the data output by device has been + * input by provisioner. + * Paramter size is used to indicate the length of data + * indicated by Pointer val, for example, if device output + * data is 12345678(decimal), the data in auth value will + * be 0xBC614E. + * Parameter num_flag is used to indicate whether the value + * input by provisioner is number or string. + */ + if (!link[idx].auth) { + BT_ERR("%s, Link auth is NULL", __func__); + return -EINVAL; + } + + memset(link[idx].auth, 0, 16); + if (num_flag) { + /* Provisioner inputs number */ + memcpy(link[idx].auth + 12, val, sizeof(u32_t)); + } else { + /* Provisioner inputs string */ + memcpy(link[idx].auth, val, link[idx].auth_size); + } + + send_confirm(idx); + return 0; +} + +#if 0 +int bt_mesh_prov_set_oob_output_data(const u8_t idx, u8_t *num, u8_t size, bool num_flag) +{ + /** This function should be called in the prov_output_num + * callback, after the data has been output by provisioner. + * Parameter size is used to indicate the length of data + * indicated by Pointer num, for example, if provisioner + * output data is 12345678(decimal), the data in auth value + * will be 0xBC614E. + * Parameter num_flag is used to indicate whether the value + * output by provisioner is number or string. + */ + if (!link[idx].auth) { + BT_ERR("%s, link auth is NULL", __func__); + return -EINVAL; + } + + if (num_flag) { + /* Provisioner output number */ + memset(link[idx].auth, 0, 16); + memcpy(link[idx].auth + 16 - size, num, size); + } else { + /* Provisioner output string */ + memset(link[idx].auth, 0, 16); + memcpy(link[idx].auth, num, size); + } + + link[idx].expect = PROV_INPUT_COMPLETE; + + return 0; +} +#endif + +int bt_mesh_prov_read_oob_pub_key(const u8_t idx, const u8_t pub_key_x[32], const u8_t pub_key_y[32]) +{ + if (!link[idx].conf_inputs) { + BT_ERR("%s, Link conf_inputs is NULL", __func__); + return -EINVAL; + } + + /* Swap X and Y halves independently to big-endian */ + sys_memcpy_swap(&link[idx].conf_inputs[81], pub_key_x, 32); + sys_memcpy_swap(&link[idx].conf_inputs[81] + 32, pub_key_y, 32); + + bt_mesh_atomic_set_bit(link[idx].flags, REMOTE_PUB_KEY); + + if (bt_mesh_atomic_test_and_clear_bit(link[idx].flags, WAIT_GEN_DHKEY)) { + prov_gen_dh_key(idx); + } + + return 0; +} + +static void prov_dh_key_cb(const u8_t key[32], const u8_t idx) +{ + BT_DBG("%p", key); + + if (!key) { + BT_ERR("%s, Failed to generate DHKey", __func__); + goto fail; + } + + link[idx].dhkey = (u8_t *)osi_calloc(PROV_DH_KEY_SIZE); + if (!link[idx].dhkey) { + BT_ERR("%s, Failed to allocate memory", __func__); + goto fail; + } + sys_memcpy_swap(link[idx].dhkey, key, 32); + + BT_DBG("DHkey: %s", bt_hex(link[idx].dhkey, 32)); + + bt_mesh_atomic_set_bit(link[idx].flags, HAVE_DHKEY); + + /** After dhkey is generated, if auth_method is No OOB or + * Static OOB, provisioner can start to send confirmation. + * If output OOB is used by the device, provisioner need + * to watch out the output number and input it as auth_val. + * If input OOB is used by the device, provisioner need + * to output a value, and wait for prov input complete pdu. + */ + if (prov_auth(idx, link[idx].auth_method, + link[idx].auth_action, link[idx].auth_size) < 0) { + BT_ERR("%s, Failed to authenticate", __func__); + goto fail; + } + if (link[idx].auth_method == AUTH_METHOD_OUTPUT || + link[idx].auth_method == AUTH_METHOD_INPUT) { + return; + } + + if (link[idx].expect != PROV_INPUT_COMPLETE) { + send_confirm(idx); + } + return; + +fail: + close_link(idx, CLOSE_REASON_FAILED); + return; +} + +static void prov_gen_dh_key(const u8_t idx) +{ + u8_t pub_key[64]; + + /* Copy device public key in little-endian for bt_mesh_dh_key_gen(). + * X and Y halves are swapped independently. + */ + sys_memcpy_swap(&pub_key[0], &link[idx].conf_inputs[81], 32); + sys_memcpy_swap(&pub_key[32], &link[idx].conf_inputs[113], 32); + + if (bt_mesh_dh_key_gen(pub_key, prov_dh_key_cb, idx)) { + BT_ERR("%s, Failed to generate DHKey", __func__); + close_link(idx, CLOSE_REASON_FAILED); + return; + } +} + +static void send_pub_key(const u8_t idx, u8_t oob) +{ + PROV_BUF(buf, 65); + const u8_t *key = NULL; + + key = bt_mesh_pub_key_get(); + if (!key) { + BT_ERR("%s, No public key available", __func__); + close_link(idx, CLOSE_REASON_FAILED); + return; + } + + BT_DBG("Local Public Key: %s", bt_hex(key, 64)); + + bt_mesh_atomic_set_bit(link[idx].flags, LOCAL_PUB_KEY); + + prov_buf_init(&buf, PROV_PUB_KEY); + + /* Swap X and Y halves independently to big-endian */ + sys_memcpy_swap(net_buf_simple_add(&buf, 32), key, 32); + sys_memcpy_swap(net_buf_simple_add(&buf, 32), &key[32], 32); + + /* Store provisioner public key value in conf_inputs */ + memcpy(&link[idx].conf_inputs[17], &buf.data[1], 64); + + if (prov_send(idx, &buf)) { + BT_ERR("%s, Failed to send Provisioning Public Key", __func__); + close_link(idx, CLOSE_REASON_FAILED); + return; + } + + if (!oob) { + link[idx].expect = PROV_PUB_KEY; + } else { + /** Have already got device public key. If next is to + * send confirm(not wait for input complete), need to + * wait for transactiona ack for public key then send + * provisioning confirm pdu. + */ +#if defined(CONFIG_BLE_MESH_PB_ADV) + if (idx < CONFIG_BLE_MESH_PBA_SAME_TIME) { + link[idx].expect_ack_for = PROV_PUB_KEY; + return; + } +#endif /* CONFIG_BLE_MESH_PB_ADV */ + + /* If remote public key has been read, then start to generate DHkey, + * otherwise wait for device oob public key. + */ + if (bt_mesh_atomic_test_bit(link[idx].flags, REMOTE_PUB_KEY)) { + prov_gen_dh_key(idx); + } else { + bt_mesh_atomic_set_bit(link[idx].flags, WAIT_GEN_DHKEY); + } + } +} + +static void prov_pub_key(const u8_t idx, const u8_t *data) +{ + BT_DBG("Remote Public Key: %s", bt_hex(data, 64)); + + /* Make sure received pdu is ok and cancel the timeout timer */ + if (bt_mesh_atomic_test_and_clear_bit(link[idx].flags, TIMEOUT_START)) { + k_delayed_work_cancel(&link[idx].timeout); + } + + memcpy(&link[idx].conf_inputs[81], data, 64); + + if (!bt_mesh_atomic_test_bit(link[idx].flags, LOCAL_PUB_KEY)) { + /* Clear retransmit timer */ +#if defined(CONFIG_BLE_MESH_PB_ADV) + prov_clear_tx(idx); +#endif + bt_mesh_atomic_set_bit(link[idx].flags, REMOTE_PUB_KEY); + BT_WARN("%s, Waiting for local public key", __func__); + return; + } + + prov_gen_dh_key(idx); +} + +static void prov_input_complete(const u8_t idx, const u8_t *data) +{ + /* Make sure received pdu is ok and cancel the timeout timer */ + if (bt_mesh_atomic_test_and_clear_bit(link[idx].flags, TIMEOUT_START)) { + k_delayed_work_cancel(&link[idx].timeout); + } + + /* Provisioner receives input complete and send confirm */ + send_confirm(idx); +} + +static void prov_confirm(const u8_t idx, const u8_t *data) +{ + /** + * Zephyr uses PROV_BUF(16). Currently test with PROV_BUF(16) + * and PROV_BUF(17) on branch feature/btdm_ble_mesh_debug both + * work fine. + */ + PROV_BUF(buf, 17); + + BT_DBG("Remote Confirm: %s", bt_hex(data, 16)); + + /* Make sure received pdu is ok and cancel the timeout timer */ + if (bt_mesh_atomic_test_and_clear_bit(link[idx].flags, TIMEOUT_START)) { + k_delayed_work_cancel(&link[idx].timeout); + } + + link[idx].conf = (u8_t *)osi_calloc(PROV_CONFIRM_SIZE); + if (!link[idx].conf) { + BT_ERR("%s, Failed to allocate memory", __func__); + close_link(idx, CLOSE_REASON_FAILED); + return; + } + + memcpy(link[idx].conf, data, 16); + + if (!bt_mesh_atomic_test_bit(link[idx].flags, HAVE_DHKEY)) { +#if defined(CONFIG_BLE_MESH_PB_ADV) + prov_clear_tx(idx); +#endif + bt_mesh_atomic_set_bit(link[idx].flags, SEND_CONFIRM); + } + + prov_buf_init(&buf, PROV_RANDOM); + + net_buf_simple_add_mem(&buf, link[idx].rand, 16); + + if (prov_send(idx, &buf)) { + BT_ERR("%s, Failed to send Provisioning Random", __func__); + close_link(idx, CLOSE_REASON_FAILED); + return; + } + + link[idx].expect = PROV_RANDOM; +} + +static void send_prov_data(const u8_t idx) +{ + PROV_BUF(buf, 34); + const u8_t *netkey = NULL; + bool already_flag = false; + u8_t session_key[16]; + u8_t nonce[13]; + u8_t pdu[25]; + u16_t max_addr; + u16_t j; + int err; + + err = bt_mesh_session_key(link[idx].dhkey, link[idx].prov_salt, session_key); + if (err) { + BT_ERR("%s, Failed to generate session key", __func__); + goto fail; + } + BT_DBG("SessionKey: %s", bt_hex(session_key, 16)); + + err = bt_mesh_prov_nonce(link[idx].dhkey, link[idx].prov_salt, nonce); + if (err) { + BT_ERR("%s, Failed to generate session nonce", __func__); + goto fail; + } + BT_DBG("Nonce: %s", bt_hex(nonce, 13)); + + /* Assign provisioning data for the device. Currently all provisioned devices + * will be added to the primary subnet, and may add an API to choose to which + * subnet will the device be provisioned later. + */ + if (FAST_PROV_FLAG_GET()) { + netkey = fast_prov_info.net_key; + if (!netkey) { + BT_ERR("%s, Failed to get NetKey for fast provisioning", __func__); + goto fail; + } + memcpy(pdu, netkey, 16); + sys_put_be16(fast_prov_info.net_idx, &pdu[16]); + pdu[18] = fast_prov_info.flags; + sys_put_be32(fast_prov_info.iv_index, &pdu[19]); + } else { + netkey = provisioner_net_key_get(prov_ctx.curr_net_idx); + if (!netkey) { + BT_ERR("%s, Failed to get NetKey for provisioning data", __func__); + goto fail; + } + memcpy(pdu, netkey, 16); + sys_put_be16(prov_ctx.curr_net_idx, &pdu[16]); + pdu[18] = prov_ctx.curr_flags; + sys_put_be32(prov_ctx.curr_iv_index, &pdu[19]); + } + + /* 1. The Provisioner must not reuse unicast addresses that have been + * allocated to a device and sent in a Provisioning Data PDU until + * the Provisioner receives an Unprovisioned Device beacon or + * Service Data for the Mesh Provisioning Service from that same + * device, identified using the Device UUID of the device. + * 2. Once the provisioning data for the device has been sent, we will + * add the data sent to this device into the already_prov_info. + * 3. Another situation here is: + * If the device is a re-provisioned one, but the element num has + * changed and is larger than the previous number, here we will + * assign new address for the device. + */ + + /* Check if this device is a re-provisioned device */ + for (j = 0U; j < ARRAY_SIZE(prov_ctx.already_prov); j++) { + if (!memcmp(link[idx].uuid, prov_ctx.already_prov[j].uuid, 16)) { + if (link[idx].element_num <= prov_ctx.already_prov[j].element_num) { + already_flag = true; + sys_put_be16(prov_ctx.already_prov[j].unicast_addr, &pdu[23]); + link[idx].unicast_addr = prov_ctx.already_prov[j].unicast_addr; + break; + } else { + /* TODO: If the device has a larger element number during the + * second provisioning, then if the device is provisioned the + * third time later, already_prov struct will have two elements + * containing the same device UUID but with different element + * number. So we may add a flag to indicate the unicast address + * in the smaller element can be reused by other devices when + * unicast address is exhausted. + */ + } + } + } + + max_addr = FAST_PROV_FLAG_GET() ? fast_prov_info.unicast_addr_max : 0x7FFF; + + if (!already_flag) { + /* If this device to be provisioned is a new device */ + if (!prov_ctx.current_addr) { + BT_ERR("%s, No unicast address can be assigned", __func__); + goto fail; + } + + if (prov_ctx.current_addr + link[idx].element_num - 1 > max_addr) { + BT_ERR("%s, Not enough unicast address for the device", __func__); + goto fail; + } + + sys_put_be16(prov_ctx.current_addr, &pdu[23]); + link[idx].unicast_addr = prov_ctx.current_addr; + } + + prov_buf_init(&buf, PROV_DATA); + + err = bt_mesh_prov_encrypt(session_key, nonce, pdu, net_buf_simple_add(&buf, 33)); + if (err) { + BT_ERR("%s, Failed to encrypt provisioning data", __func__); + goto fail; + } + + if (prov_send(idx, &buf)) { + BT_ERR("%s, Failed to send Provisioning Data", __func__); + goto fail; + } + + /* If provisioning data is sent successfully, add the assigned information + * into the already_prov_info struct if this device is a new one. And if + * sent successfully, update the current_addr in prov_ctx struct. + */ + if (!already_flag) { + for (j = 0U; j < ARRAY_SIZE(prov_ctx.already_prov); j++) { + if (!prov_ctx.already_prov[j].element_num) { + memcpy(prov_ctx.already_prov[j].uuid, link[idx].uuid, 16); + prov_ctx.already_prov[j].element_num = link[idx].element_num; + prov_ctx.already_prov[j].unicast_addr = link[idx].unicast_addr; + break; + } + } + + /* We update the next unicast address to be assigned here because + * if provisioner is provisioning two devices at the same time, we + * need to assign the unicast address for them correctly. Hence we + * should not update the prov_ctx.current_addr after the proper + * provisioning complete pdu is received. + */ + prov_ctx.current_addr += link[idx].element_num; + if (prov_ctx.current_addr > max_addr) { + /* No unicast address will be used for further provisioning */ + prov_ctx.current_addr = 0x0000; + } + } + + if (FAST_PROV_FLAG_GET()) { + link[idx].ki_flags = fast_prov_info.flags; + link[idx].iv_index = fast_prov_info.iv_index; + } else { + link[idx].ki_flags = prov_ctx.curr_flags; + link[idx].iv_index = prov_ctx.curr_iv_index; + } + + link[idx].expect = PROV_COMPLETE; + return; + +fail: + close_link(idx, CLOSE_REASON_FAILED); + return; +} + +static void prov_random(const u8_t idx, const u8_t *data) +{ + u8_t conf_verify[16]; + + BT_DBG("Remote Random: %s", bt_hex(data, 16)); + + if (bt_mesh_prov_conf(link[idx].conf_key, data, link[idx].auth, conf_verify)) { + BT_ERR("%s, Failed to calculate confirmation verification", __func__); + goto fail; + } + + if (memcmp(conf_verify, link[idx].conf, 16)) { + BT_ERR("%s, Invalid confirmation value", __func__); + BT_DBG("Received: %s", bt_hex(link[idx].conf, 16)); + BT_DBG("Calculated: %s", bt_hex(conf_verify, 16)); + goto fail; + } + + /*Verify received confirm is ok and cancel the timeout timer */ + if (bt_mesh_atomic_test_and_clear_bit(link[idx].flags, TIMEOUT_START)) { + k_delayed_work_cancel(&link[idx].timeout); + } + + /** After provisioner receives provisioning random from device, + * and successfully check the confirmation, the following + * should be done: + * 1. osi_calloc memory for prov_salt + * 2. calculate prov_salt + * 3. prepare provisioning data and send + */ + link[idx].prov_salt = (u8_t *)osi_calloc(PROV_PROV_SALT_SIZE); + if (!link[idx].prov_salt) { + BT_ERR("%s, Failed to allocate memory", __func__); + goto fail; + } + + if (bt_mesh_prov_salt(link[idx].conf_salt, link[idx].rand, data, + link[idx].prov_salt)) { + BT_ERR("%s, Failed to generate ProvisioningSalt", __func__); + goto fail; + } + + BT_DBG("ProvisioningSalt: %s", bt_hex(link[idx].prov_salt, 16)); + + send_prov_data(idx); + return; + +fail: + close_link(idx, CLOSE_REASON_FAILED); + return; +} + +static void prov_complete(const u8_t idx, const u8_t *data) +{ + u8_t device_key[16]; + u16_t rm = 0; + u16_t j; + int err; + + /* Make sure received pdu is ok and cancel the timeout timer */ + if (bt_mesh_atomic_test_and_clear_bit(link[idx].flags, TIMEOUT_START)) { + k_delayed_work_cancel(&link[idx].timeout); + } + + /* If provisioning complete is received, the provisioning device + * will be stored into the prov_node_info structure and become a + * node within the mesh network + */ + err = bt_mesh_dev_key(link[idx].dhkey, link[idx].prov_salt, device_key); + if (err) { + BT_ERR("%s, Failed to generate device key", __func__); + close_link(idx, CLOSE_REASON_FAILED); + return; + } + + for (j = 0U; j < ARRAY_SIZE(prov_nodes); j++) { + if (!prov_nodes[j].provisioned) { + prov_nodes[j].provisioned = true; + prov_nodes[j].oob_info = link[idx].oob_info; + prov_nodes[j].element_num = link[idx].element_num; + prov_nodes[j].unicast_addr = link[idx].unicast_addr; + if (FAST_PROV_FLAG_GET()) { + prov_nodes[j].net_idx = fast_prov_info.net_idx; + } else { + prov_nodes[j].net_idx = prov_ctx.curr_net_idx; + } + prov_nodes[j].flags = link[idx].ki_flags; + prov_nodes[j].iv_index = link[idx].iv_index; + prov_nodes[j].addr.type = link[idx].addr.type; + memcpy(prov_nodes[j].addr.val, link[idx].addr.val, BLE_MESH_ADDR_LEN); + memcpy(prov_nodes[j].uuid, link[idx].uuid, 16); + break; + } + } + + if (j == ARRAY_SIZE(prov_nodes)) { + BT_ERR("%s, Provisioned node queue is full", __func__); + close_link(idx, CLOSE_REASON_FAILED); + return; + } + + prov_ctx.node_count++; + + err = provisioner_node_provision(j, prov_nodes[j].uuid, prov_nodes[j].oob_info, + prov_nodes[j].unicast_addr, prov_nodes[j].element_num, + prov_nodes[j].net_idx, prov_nodes[j].flags, + prov_nodes[j].iv_index, device_key); + if (err) { + BT_ERR("%s, Failed to store node info", __func__); + close_link(idx, CLOSE_REASON_FAILED); + return; + } + + if (prov->prov_complete) { + prov->prov_complete(j, prov_nodes[j].uuid, prov_nodes[j].unicast_addr, + prov_nodes[j].element_num, prov_nodes[j].net_idx); + } + + err = provisioner_dev_find(&link[idx].addr, link[idx].uuid, &rm); + if (!err) { + if (unprov_dev[rm].flags & RM_AFTER_PROV) { + memset(&unprov_dev[rm], 0, sizeof(struct unprov_dev_queue)); + } + } else if (err == -ENODEV) { + BT_DBG("%s, Device is not found in queue", __func__); + } else { + BT_WARN("%s, Failed to remove device from queue", __func__); + } + + close_link(idx, CLOSE_REASON_SUCCESS); +} + +static void prov_failed(const u8_t idx, const u8_t *data) +{ + BT_WARN("%s, Error 0x%02x", __func__, data[0]); + + close_link(idx, CLOSE_REASON_FAILED); +} + +static const struct { + void (*func)(const u8_t idx, const u8_t *data); + u16_t len; +} prov_handlers[] = { + { prov_invite, 1 }, + { prov_capabilities, 11 }, + { prov_start, 5 }, + { prov_pub_key, 64 }, + { prov_input_complete, 0 }, + { prov_confirm, 16 }, + { prov_random, 16 }, + { prov_data, 33 }, + { prov_complete, 0 }, + { prov_failed, 1 }, +}; + +static void close_link(const u8_t idx, u8_t reason) +{ +#if defined(CONFIG_BLE_MESH_PB_ADV) + if (idx < CONFIG_BLE_MESH_PBA_SAME_TIME) { + bearer_ctl_send(idx, LINK_CLOSE, &reason, sizeof(reason)); + return; + } +#endif + +#if defined(CONFIG_BLE_MESH_PB_GATT) + if (idx < BLE_MESH_PROV_SAME_TIME +#if defined(CONFIG_BLE_MESH_PB_ADV) + && idx >= CONFIG_BLE_MESH_PBA_SAME_TIME +#endif + ) { + if (link[idx].conn) { + bt_mesh_gattc_disconnect(link[idx].conn); + } + return; + } +#endif + + BT_ERR("%s, Invalid link index %d", __func__, idx); + return; +} + +static void prov_timeout(struct k_work *work) +{ + u8_t idx = (u8_t)work->index; + + BT_DBG("%s", __func__); + + close_link(idx, CLOSE_REASON_TIMEOUT); +} + +#if defined(CONFIG_BLE_MESH_PB_ADV) +static void prov_retransmit(struct k_work *work) +{ + s64_t timeout = TRANSACTION_TIMEOUT; + u8_t idx = (u8_t)work->index; + u8_t i; + + BT_DBG("%s", __func__); + + if (!bt_mesh_atomic_test_bit(link[idx].flags, LINK_ACTIVE)) { + BT_WARN("%s, Link is not active", __func__); + return; + } + +#if defined(CONFIG_BLE_MESH_FAST_PROV) + if (link[idx].tx_pdu_type >= PROV_DATA) { + timeout = K_SECONDS(30); + } +#endif + if (k_uptime_get() - link[idx].tx.start > timeout) { + BT_WARN("Provisioner timeout, giving up transaction"); + reset_link(idx, CLOSE_REASON_TIMEOUT); + return; + } + + if (link[idx].send_link_close & BIT(0)) { + u8_t reason = (link[idx].send_link_close >> 1) & BIT_MASK(2); + u16_t count = (link[idx].send_link_close >> 3); + if (count >= 2) { + reset_link(idx, reason); + return; + } + link[idx].send_link_close += BIT(3); + } + + for (i = 0U; i < ARRAY_SIZE(link[idx].tx.buf); i++) { + struct net_buf *buf = link[idx].tx.buf[i]; + + if (!buf) { + break; + } + + if (BLE_MESH_ADV(buf)->busy) { + continue; + } + + BT_DBG("%u bytes: %s", buf->len, bt_hex(buf->data, buf->len)); + + if (i + 1 < ARRAY_SIZE(link[idx].tx.buf) && link[idx].tx.buf[i + 1]) { + bt_mesh_adv_send(buf, NULL, NULL); + } else { + bt_mesh_adv_send(buf, &buf_sent_cb, (void *)(int)idx); + } + } +} + +static void link_ack(const u8_t idx, struct prov_rx *rx, struct net_buf_simple *buf) +{ + BT_DBG("len %u", buf->len); + + if (buf->len) { + BT_ERR("%s, Invalid Link ACK length", __func__); + close_link(idx, CLOSE_REASON_FAILED); + return; + } + + if (link[idx].expect == PROV_CAPABILITIES) { + BT_WARN("%s, Link ACK is already received", __func__); + return; + } + + link[idx].conf_inputs = (u8_t *)osi_calloc(PROV_CONF_INPUTS_SIZE); + if (!link[idx].conf_inputs) { + BT_ERR("%s, Failed to allocate memory", __func__); + close_link(idx, CLOSE_REASON_FAILED); + return; + } + + send_invite(idx); +} + +static void link_close(const u8_t idx, struct prov_rx *rx, struct net_buf_simple *buf) +{ + u8_t reason; + + BT_DBG("len %u", buf->len); + + reason = net_buf_simple_pull_u8(buf); + + reset_link(idx, reason); +} + +static void gen_prov_ctl(const u8_t idx, struct prov_rx *rx, struct net_buf_simple *buf) +{ + BT_DBG("op 0x%02x len %u", BEARER_CTL(rx->gpc), buf->len); + + switch (BEARER_CTL(rx->gpc)) { + case LINK_OPEN: + break; + + case LINK_ACK: + if (!bt_mesh_atomic_test_bit(link[idx].flags, LINK_ACTIVE)) { + return; + } + link_ack(idx, rx, buf); + break; + + case LINK_CLOSE: + if (!bt_mesh_atomic_test_bit(link[idx].flags, LINK_ACTIVE)) { + return; + } + link_close(idx, rx, buf); + break; + + default: + BT_ERR("%s, Unknown bearer opcode 0x%02x", __func__, BEARER_CTL(rx->gpc)); + return; + } +} + +static void prov_msg_recv(const u8_t idx) +{ + u8_t type = link[idx].rx.buf->data[0]; + + BT_DBG("type 0x%02x len %u", type, link[idx].rx.buf->len); + + /** + * Provisioner first checks information within the received + * Provisioning PDU. If the check succeeds then check fcs. + */ + if (type != PROV_FAILED && type != link[idx].expect) { + BT_ERR("%s, Unexpected msg 0x%02x != 0x%02x", __func__, type, link[idx].expect); + goto fail; + } + + if (type >= 0x0A) { + BT_ERR("%s, Unknown provisioning PDU type 0x%02x", __func__, type); + goto fail; + } + + if (1 + prov_handlers[type].len != link[idx].rx.buf->len) { + BT_ERR("%s, Invalid length %u for type 0x%02x", __func__, link[idx].rx.buf->len, type); + goto fail; + } + + if (!bt_mesh_fcs_check(link[idx].rx.buf, link[idx].rx.fcs)) { + BT_ERR("%s, Incorrect FCS", __func__); + goto fail; + } + + gen_prov_ack_send(idx, link[idx].rx.trans_id); + link[idx].rx.prev_id = link[idx].rx.trans_id; + link[idx].rx.trans_id = 0; + + prov_handlers[type].func(idx, &link[idx].rx.buf->data[1]); + return; + +fail: + close_link(idx, CLOSE_REASON_FAILED); + return; +} + +static void gen_prov_cont(const u8_t idx, struct prov_rx *rx, struct net_buf_simple *buf) +{ + u8_t seg = CONT_SEG_INDEX(rx->gpc); + + BT_DBG("len %u, seg_index %u", buf->len, seg); + + if (!link[idx].rx.seg && link[idx].rx.prev_id == rx->xact_id) { + BT_WARN("%s, Resending ack", __func__); + gen_prov_ack_send(idx, rx->xact_id); + return; + } + + if (rx->xact_id != link[idx].rx.trans_id) { + BT_WARN("%s, Data for unknown transaction (%u != %u)", + __func__, rx->xact_id, link[idx].rx.trans_id); + /** + * If Provisioner receives a Provisioning PDU with a mismatch + * transaction number, it just ignore it. + */ + return; + } + + if (seg > link[idx].rx.last_seg) { + BT_ERR("%s, Invalid segment index %u", __func__, seg); + goto fail; + } else if (seg == link[idx].rx.last_seg) { + u8_t expect_len; + + expect_len = (link[idx].rx.buf->len - 20 - + (23 * (link[idx].rx.last_seg - 1))); + if (expect_len != buf->len) { + BT_ERR("%s, Incorrect last seg len: %u != %u", + __func__, expect_len, buf->len); + goto fail; + } + } + + if (!(link[idx].rx.seg & BIT(seg))) { + BT_WARN("%s, Ignore already received segment", __func__); + return; + } + + memcpy(XACT_SEG_DATA(idx, seg), buf->data, buf->len); + XACT_SEG_RECV(idx, seg); + + if (!link[idx].rx.seg) { + prov_msg_recv(idx); + } + return; + +fail: + close_link(idx, CLOSE_REASON_FAILED); + return; +} + +static void gen_prov_ack(const u8_t idx, struct prov_rx *rx, struct net_buf_simple *buf) +{ + u8_t ack_type, pub_key_oob; + + BT_DBG("len %u", buf->len); + + if (!link[idx].tx.buf[0]) { + return; + } + + if (!link[idx].tx.trans_id) { + return; + } + + if (rx->xact_id == (link[idx].tx.trans_id - 1)) { + prov_clear_tx(idx); + + ack_type = link[idx].expect_ack_for; + switch (ack_type) { + case PROV_START: + pub_key_oob = link[idx].conf_inputs[13]; + send_pub_key(idx, pub_key_oob); + break; + case PROV_PUB_KEY: + prov_gen_dh_key(idx); + break; + default: + break; + } + link[idx].expect_ack_for = 0x00; + } +} + +static void gen_prov_start(const u8_t idx, struct prov_rx *rx, struct net_buf_simple *buf) +{ + if (link[idx].rx.seg) { + BT_WARN("%s, Get Start while there are unreceived segments", __func__); + return; + } + + if (link[idx].rx.prev_id == rx->xact_id) { + BT_WARN("%s, Resending ack", __func__); + gen_prov_ack_send(idx, rx->xact_id); + return; + } + + link[idx].rx.buf->len = net_buf_simple_pull_be16(buf); + link[idx].rx.trans_id = rx->xact_id; + link[idx].rx.fcs = net_buf_simple_pull_u8(buf); + + BT_DBG("len %u last_seg %u total_len %u fcs 0x%02x", buf->len, + START_LAST_SEG(rx->gpc), link[idx].rx.buf->len, link[idx].rx.fcs); + + /* Provisioner can not receive zero-length provisioning pdu */ + if (link[idx].rx.buf->len < 1) { + BT_ERR("%s, Ignoring zero-length provisioning PDU", __func__); + close_link(idx, CLOSE_REASON_FAILED); + return; + } + + if (link[idx].rx.buf->len > link[idx].rx.buf->size) { + BT_ERR("%s, Too large provisioning PDU (%u bytes)", + __func__, link[idx].rx.buf->len); + // close_link(i, CLOSE_REASON_FAILED); + return; + } + + if (START_LAST_SEG(rx->gpc) > 0 && link[idx].rx.buf->len <= 20) { + BT_ERR("%s, Too small total length for multi-segment PDU", __func__); + close_link(idx, CLOSE_REASON_FAILED); + return; + } + + link[idx].rx.seg = (1 << (START_LAST_SEG(rx->gpc) + 1)) - 1; + link[idx].rx.last_seg = START_LAST_SEG(rx->gpc); + memcpy(link[idx].rx.buf->data, buf->data, buf->len); + XACT_SEG_RECV(idx, 0); + + if (!link[idx].rx.seg) { + prov_msg_recv(idx); + } +} + +static const struct { + void (*const func)(const u8_t idx, struct prov_rx *rx, struct net_buf_simple *buf); + const u8_t require_link; + const u8_t min_len; +} gen_prov[] = { + { gen_prov_start, true, 3 }, + { gen_prov_ack, true, 0 }, + { gen_prov_cont, true, 0 }, + { gen_prov_ctl, true, 0 }, +}; + +static void gen_prov_recv(const u8_t idx, struct prov_rx *rx, struct net_buf_simple *buf) +{ + if (buf->len < gen_prov[GPCF(rx->gpc)].min_len) { + BT_ERR("%s, Too short GPC message type %u", __func__, GPCF(rx->gpc)); + close_link(idx, CLOSE_REASON_FAILED); + return; + } + + /** + * require_link can be used combining with link[].linking flag to + * set LINK_ACTIVE status after Link ACK is received. In this case + * there is no need to check LINK_ACTIVE status in find_link(). + */ + if (!bt_mesh_atomic_test_bit(link[idx].flags, LINK_ACTIVE) && + gen_prov[GPCF(rx->gpc)].require_link) { + BT_DBG("Ignoring message that requires active link"); + return; + } + + gen_prov[GPCF(rx->gpc)].func(idx, rx, buf); +} + +static int find_link(u32_t link_id, u8_t *idx) +{ + u8_t i; + + /* link for PB-ADV is from 0 to CONFIG_BLE_MESH_PBA_SAME_TIME */ + for (i = 0U; i < CONFIG_BLE_MESH_PBA_SAME_TIME; i++) { + if (bt_mesh_atomic_test_bit(link[i].flags, LINK_ACTIVE)) { + if (link[i].link_id == link_id) { + if (idx) { + *idx = i; + } + return 0; + } + } + } + + return -1; +} + +void provisioner_pb_adv_recv(struct net_buf_simple *buf) +{ + struct prov_rx rx = {0}; + u8_t idx; + + rx.link_id = net_buf_simple_pull_be32(buf); + if (find_link(rx.link_id, &idx) < 0) { + BT_DBG("%s, Data for unexpected link", __func__); + return; + } + + if (buf->len < 2) { + BT_ERR("%s, Too short provisioning packet (len %u)", __func__, buf->len); + close_link(idx, CLOSE_REASON_FAILED); + return; + } + + rx.xact_id = net_buf_simple_pull_u8(buf); + rx.gpc = net_buf_simple_pull_u8(buf); + + BT_DBG("link_id 0x%08x xact_id %u", rx.link_id, rx.xact_id); + + gen_prov_recv(idx, &rx, buf); +} +#endif /* CONFIG_BLE_MESH_PB_ADV */ + +#if defined(CONFIG_BLE_MESH_PB_GATT) +static struct bt_mesh_conn *find_conn(struct bt_mesh_conn *conn, u8_t *idx) +{ + u8_t i; + + /* link for PB-GATT is from CONFIG_BLE_MESH_PBA_SAME_TIME to BLE_MESH_PROV_SAME_TIME */ + for (i = CONFIG_BLE_MESH_PBA_SAME_TIME; i < BLE_MESH_PROV_SAME_TIME; i++) { + if (bt_mesh_atomic_test_bit(link[i].flags, LINK_ACTIVE)) { + if (link[i].conn == conn) { + if (idx) { + *idx = i; + } + return conn; + } + } + } + + return NULL; +} + +int provisioner_pb_gatt_recv(struct bt_mesh_conn *conn, struct net_buf_simple *buf) +{ + u8_t type; + u8_t idx; + + BT_DBG("%u bytes: %s", buf->len, bt_hex(buf->data, buf->len)); + + if (!find_conn(conn, &idx)) { + BT_ERR("%s, Data for unexpected connection", __func__); + return -ENOTCONN; + } + + if (buf->len < 1) { + BT_ERR("%s, Too short provisioning packet (len %u)", __func__, buf->len); + goto fail; + } + + type = net_buf_simple_pull_u8(buf); + if (type != PROV_FAILED && type != link[idx].expect) { + BT_ERR("%s, Unexpected msg 0x%02x != 0x%02x", __func__, type, link[idx].expect); + goto fail; + } + + if (type >= 0x0A) { + BT_ERR("%s, Unknown provisioning PDU type 0x%02x", __func__, type); + goto fail; + } + + if (prov_handlers[type].len != buf->len) { + BT_ERR("%s, Invalid length %u for type 0x%02x", __func__, buf->len, type); + goto fail; + } + + prov_handlers[type].func(idx, buf->data); + + return 0; + +fail: + /* Mesh Spec Section 5.4.4 Provisioning errors */ + close_link(idx, CLOSE_REASON_FAILED); + return -EINVAL; +} + +int provisioner_set_prov_conn(const u8_t addr[6], struct bt_mesh_conn *conn) +{ + u8_t i; + + if (!addr || !conn) { + BT_ERR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + for (i = CONFIG_BLE_MESH_PBA_SAME_TIME; i < BLE_MESH_PROV_SAME_TIME; i++) { + if (!memcmp(link[i].addr.val, addr, BLE_MESH_ADDR_LEN)) { + link[i].conn = bt_mesh_conn_ref(conn); + return 0; + } + } + + BT_ERR("%s, Address %s is not found", __func__, bt_hex(addr, BLE_MESH_ADDR_LEN)); + return -ENOMEM; +} + +int provisioner_pb_gatt_open(struct bt_mesh_conn *conn, u8_t *addr) +{ + u8_t idx = 0, i; + + BT_DBG("conn %p", conn); + + /** + * Double check if the device is currently being provisioned using PB-ADV. + * Provisioner binds conn with proper device when proxy_prov_connected() + * is invoked, and here after proper GATT procedures are completed, we just + * check if this conn already exists in the proxy servers array. + */ + for (i = CONFIG_BLE_MESH_PBA_SAME_TIME; i < BLE_MESH_PROV_SAME_TIME; i++) { + if (link[i].conn == conn) { + idx = i; + break; + } + } + + if (i == BLE_MESH_PROV_SAME_TIME) { + BT_ERR("%s, Link is not found", __func__); + return -ENOTCONN; + } + +#if defined(CONFIG_BLE_MESH_PB_ADV) + for (i = 0U; i < CONFIG_BLE_MESH_PBA_SAME_TIME; i++) { + if (bt_mesh_atomic_test_bit(link[i].flags, LINK_ACTIVE)) { + if (!memcmp(link[i].uuid, link[idx].uuid, 16)) { + BT_WARN("%s, Provision using PB-GATT & PB-ADV same time", __func__); + close_link(idx, CLOSE_REASON_FAILED); + return -EALREADY; + } + } + } +#endif + + bt_mesh_atomic_set_bit(link[idx].flags, LINK_ACTIVE); + link[idx].conn = bt_mesh_conn_ref(conn); + + /* May use lcd to indicate starting provisioning each device */ + if (prov->prov_link_open) { + prov->prov_link_open(BLE_MESH_PROV_GATT); + } + + link[idx].conf_inputs = (u8_t *)osi_calloc(PROV_CONF_INPUTS_SIZE); + if (!link[idx].conf_inputs) { + /* Disconnect this connection, clear corresponding informations */ + BT_ERR("%s, Failed to allocate memory", __func__); + close_link(idx, CLOSE_REASON_FAILED); + return -ENOMEM; + } + + send_invite(idx); + return 0; +} + +int provisioner_pb_gatt_close(struct bt_mesh_conn *conn, u8_t reason) +{ + u8_t idx; + + BT_DBG("conn %p", conn); + + if (!find_conn(conn, &idx)) { + BT_ERR("%s, Conn %p is not found", __func__, conn); + return -ENOTCONN; + } + + if (bt_mesh_atomic_test_and_clear_bit(link[idx].flags, TIMEOUT_START)) { + k_delayed_work_cancel(&link[idx].timeout); + } + + if (prov->prov_link_close) { + prov->prov_link_close(BLE_MESH_PROV_GATT, reason); + } + + prov_memory_free(idx); + + memset(&link[idx], 0, offsetof(struct prov_link, timeout)); + + if (bt_mesh_pub_key_get()) { + bt_mesh_atomic_set_bit(link[idx].flags, LOCAL_PUB_KEY); + } + + return 0; +} +#endif /* CONFIG_BLE_MESH_PB_GATT */ + +int provisioner_prov_init(const struct bt_mesh_prov *prov_info) +{ + const u8_t *key = NULL; + u8_t i; + + if (!prov_info) { + BT_ERR("%s, No provisioning context provided", __func__); + return -EINVAL; + } + + if (CONFIG_BLE_MESH_PBG_SAME_TIME > BLE_MESH_MAX_CONN) { + BT_ERR("%s, PB-GATT same time exceeds max connection", __func__); + return -EINVAL; + } + + key = bt_mesh_pub_key_get(); + if (!key) { + BT_ERR("%s, Failed to generate Public Key", __func__); + return -EIO; + } + + prov = prov_info; + +#if defined(CONFIG_BLE_MESH_PB_ADV) + for (i = 0U; i < CONFIG_BLE_MESH_PBA_SAME_TIME; i++) { + struct prov_adv_buf *adv = &adv_buf[i]; + adv->buf.size = ADV_BUF_SIZE; + adv->buf.__buf = adv_buf_data + (i * ADV_BUF_SIZE); + + link[i].pending_ack = XACT_NVAL; + k_delayed_work_init(&link[i].tx.retransmit, prov_retransmit); + link[i].tx.retransmit.work.index = (int)i; + link[i].rx.prev_id = XACT_NVAL; + link[i].rx.buf = bt_mesh_pba_get_buf(i); + } +#endif + + for (i = 0U; i < BLE_MESH_PROV_SAME_TIME; i++) { + k_delayed_work_init(&link[i].timeout, prov_timeout); + link[i].timeout.work.index = (int)i; + } + + /* for PB-GATT, use servers[] array in proxy_provisioner.c */ + + prov_ctx.current_addr = prov->prov_start_address; + prov_ctx.curr_net_idx = BLE_MESH_KEY_PRIMARY; + prov_ctx.curr_flags = prov->flags; + prov_ctx.curr_iv_index = prov->iv_index; + + osi_mutex_new(&prov_ctx.pb_adv_lock); + osi_mutex_new(&prov_ctx.pb_gatt_lock); + + return 0; +} + +static bool is_unprov_dev_info_callback_to_app(bt_mesh_prov_bearer_t bearer, + const u8_t uuid[16], const bt_mesh_addr_t *addr, u16_t oob_info) +{ + u16_t index; + + if (prov_ctx.prov_after_match == false) { + u8_t adv_type = (bearer == BLE_MESH_PROV_ADV) ? + BLE_MESH_ADV_NONCONN_IND : BLE_MESH_ADV_IND; + + if (provisioner_dev_find(addr, uuid, &index)) { + BT_DBG("%s, Device is not in queue, notify to upper layer", __func__); + if (notify_unprov_adv_pkt_cb) { + notify_unprov_adv_pkt_cb(addr->val, addr->type, adv_type, uuid, oob_info, bearer); + } + return true; + } + + if (!(unprov_dev[index].bearer & bearer)) { + BT_WARN("Device in queue not support PB-%s", + (bearer == BLE_MESH_PROV_ADV) ? "ADV" : "GATT"); + if (notify_unprov_adv_pkt_cb) { + notify_unprov_adv_pkt_cb(addr->val, addr->type, adv_type, uuid, oob_info, bearer); + } + return true; + } + } + + return false; +} + +void provisioner_unprov_beacon_recv(struct net_buf_simple *buf) +{ +#if defined(CONFIG_BLE_MESH_PB_ADV) + const bt_mesh_addr_t *addr = NULL; + const u8_t *uuid = NULL; + u16_t oob_info; + + if (buf->len != 0x12 && buf->len != 0x16) { + BT_ERR("%s, Invalid Unprovisioned Device Beacon length", __func__); + return; + } + + if (prov_ctx.pba_count == CONFIG_BLE_MESH_PBA_SAME_TIME) { + BT_DBG("Current PB-ADV devices reach max limit"); + return; + } + + addr = bt_mesh_pba_get_addr(); + uuid = buf->data; + net_buf_simple_pull(buf, 16); + /* Mesh beacon uses big-endian to send beacon data */ + oob_info = net_buf_simple_pull_be16(buf); + + if (provisioner_check_unprov_dev_info(uuid)) { + return; + } + + if (is_unprov_dev_info_callback_to_app( + BLE_MESH_PROV_ADV, uuid, addr, oob_info)) { + return; + } + + provisioner_start_prov_pb_adv(uuid, addr, oob_info); +#endif /* CONFIG_BLE_MESH_PB_ADV */ +} + +bool provisioner_flags_match(struct net_buf_simple *buf) +{ + u8_t flags; + + if (buf->len != 1) { + BT_DBG("%s, Unexpected flags length", __func__); + return false; + } + + flags = net_buf_simple_pull_u8(buf); + + BT_DBG("Received adv pkt with flags: 0x%02x", flags); + + /* Flags context will not be checked curently */ + + return true; +} + +u16_t provisioner_srv_uuid_recv(struct net_buf_simple *buf) +{ + u16_t uuid; + + if (buf->len != 2) { + BT_DBG("Length not match mesh service uuid"); + return false; + } + + uuid = net_buf_simple_pull_le16(buf); + + BT_DBG("Received adv pkt with service UUID: %d", uuid); + + if ((uuid != BLE_MESH_UUID_MESH_PROV_VAL) && (uuid != BLE_MESH_UUID_MESH_PROXY_VAL)) { + return false; + } + + return uuid; +} + +static void provisioner_prov_srv_data_recv(struct net_buf_simple *buf, const bt_mesh_addr_t *addr); + +void provisioner_srv_data_recv(struct net_buf_simple *buf, const bt_mesh_addr_t *addr, u16_t uuid) +{ + u16_t uuid_type; + + if (!buf || !addr) { + BT_ERR("%s, Invalid parameter", __func__); + return; + } + + uuid_type = net_buf_simple_pull_le16(buf); + if (uuid_type != uuid) { + BT_DBG("%s, Invalid Mesh Service Data UUID 0x%04x", __func__, uuid_type); + return; + } + + switch (uuid) { + case BLE_MESH_UUID_MESH_PROV_VAL: + if (buf->len != BLE_MESH_PROV_SRV_DATA_LEN) { + BT_WARN("%s, Invalid Mesh Prov Service Data length %d", __func__, buf->len); + return; + } + BT_DBG("Start to deal with Mesh Prov Service Data"); + provisioner_prov_srv_data_recv(buf, addr); + break; + case BLE_MESH_UUID_MESH_PROXY_VAL: + if (buf->len != BLE_MESH_PROXY_SRV_DATA_LEN1 && + buf->len != BLE_MESH_PROXY_SRV_DATA_LEN2) { + BT_ERR("%s, Invalid Mesh Proxy Service Data length %d", __func__, buf->len); + return; + } + BT_DBG("Start to deal with Mesh Proxy Service Data"); + provisioner_proxy_srv_data_recv(buf); + break; + default: + break; + } +} + +static void provisioner_prov_srv_data_recv(struct net_buf_simple *buf, const bt_mesh_addr_t *addr) +{ +#if defined(CONFIG_BLE_MESH_PB_GATT) + const u8_t *uuid = NULL; + u16_t oob_info; + + if (prov_ctx.pbg_count == CONFIG_BLE_MESH_PBG_SAME_TIME) { + BT_DBG("Current PB-GATT devices reach max limit"); + return; + } + + uuid = buf->data; + net_buf_simple_pull(buf, 16); + /* Mesh beacon uses big-endian to send beacon data */ + oob_info = net_buf_simple_pull_be16(buf); + + if (provisioner_check_unprov_dev_info(uuid)) { + return; + } + + if (is_unprov_dev_info_callback_to_app( + BLE_MESH_PROV_GATT, uuid, addr, oob_info)) { + return; + } + + /* Provisioner will copy the device uuid, oob info, etc. into an unused link + * struct, and at this moment the link has not been activated. Even if we + * receive an Unprovisioned Device Beacon and a Connectable Provisioning adv + * pkt from the same device, and store the device info received within each + * adv pkt into two link structs which will has no impact on the provisioning + * of this device, because no matter which link among PB-GATT and PB-ADV is + * activated first, the other one will be dropped finally and the link struct + * occupied by the dropped link will be used by other devices (because the link + * is not activated). + * Use connecting flag to prevent if two devices's adv pkts are both received, + * the previous one info will be replaced by the second one. + */ + provisioner_start_prov_pb_gatt(uuid, addr, oob_info); +#endif /* CONFIG_BLE_MESH_PB_GATT */ +} + +#endif /* CONFIG_BLE_MESH_PROVISIONER */ diff --git a/components/bt/ble_mesh/mesh_core/provisioner_prov.h b/components/bt/ble_mesh/mesh_core/provisioner_prov.h new file mode 100644 index 0000000000..30a2f94d76 --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/provisioner_prov.h @@ -0,0 +1,379 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#ifndef _PROVISIONER_PROV_H_ +#define _PROVISIONER_PROV_H_ + +#include "mesh_bearer_adapt.h" +#include "mesh_main.h" + +#if !CONFIG_BLE_MESH_PROVISIONER + +#define CONFIG_BLE_MESH_PBA_SAME_TIME 0 +#define CONFIG_BLE_MESH_PBG_SAME_TIME 0 + +#else + +#if !defined(CONFIG_BLE_MESH_PB_ADV) +#define CONFIG_BLE_MESH_PBA_SAME_TIME 0 +#endif /* !CONFIG_BLE_MESH_PB_ADV */ + +#if !defined(CONFIG_BLE_MESH_PB_GATT) +#define CONFIG_BLE_MESH_PBG_SAME_TIME 0 +#endif /* !CONFIG_BLE_MESH_PB_GATT */ + +#endif /* !CONFIG_BLE_MESH_PROVISIONER */ + +#define RM_AFTER_PROV BIT(0) +#define START_PROV_NOW BIT(1) +#define FLUSHABLE_DEV BIT(2) + +struct bt_mesh_unprov_dev_add { + u8_t addr[6]; + u8_t addr_type; + u8_t uuid[16]; + u16_t oob_info; + u8_t bearer; +}; + +struct bt_mesh_device_delete { + u8_t addr[6]; + u8_t addr_type; + u8_t uuid[16]; +}; + +#define NET_IDX_FLAG BIT(0) +#define FLAGS_FLAG BIT(1) +#define IV_INDEX_FLAG BIT(2) + +struct bt_mesh_prov_data_info { + union { + u16_t net_idx; + u8_t flags; + u32_t iv_index; + }; + u8_t flag; +}; + +/* The following APIs are for primary provisioner internal use */ + +/** + * @brief This function decrements the current PB-GATT count. + * + * @return None + */ +void provisioner_pbg_count_dec(void); + +/** + * @brief This function increments the current PB-GATT count. + * + * @return None + */ +void provisioner_pbg_count_inc(void); + +/** + * @brief This function clears the part of the link info of the proper device. + * + * @param[in] addr: Remote device address + * + * @return None + */ +void provisioner_clear_link_conn_info(const u8_t addr[6]); + +/** + * @brief This function handles the received PB-ADV PDUs. + * + * @param[in] buf: Pointer to the buffer containing generic provisioning PDUs + * + * @return Zero - success, otherwise - fail + */ +void provisioner_pb_adv_recv(struct net_buf_simple *buf); + +/** + * @brief This function sends provisioning invite to start + * provisioning this unprovisioned device. + * + * @param[in] addr: Remote device address + * @param[in] conn: Pointer to the bt_conn structure + * + * @return Zero - success, otherwise - fail + */ +int provisioner_set_prov_conn(const u8_t addr[6], struct bt_mesh_conn *conn); + +/** + * @brief This function sends provisioning invite to start + * provisioning this unprovisioned device. + * + * @param[in] conn: Pointer to the bt_conn structure + * @param[in] addr: Address of the connected device + * + * @return Zero - success, otherwise - fail + */ +int provisioner_pb_gatt_open(struct bt_mesh_conn *conn, u8_t *addr); + +/** + * @brief This function resets the used information when + * related connection is terminated. + * + * @param[in] conn: Pointer to the bt_conn structure + * @param[in] reason: Connection terminated reason + * + * @return Zero - success, otherwise - fail + */ +int provisioner_pb_gatt_close(struct bt_mesh_conn *conn, u8_t reason); + +/** + * @brief This function handles the received PB-GATT provision + * PDUs. + * + * @param[in] conn: Pointer to the bt_conn structure + * @param[in] buf: Pointer to the buffer containing provision PDUs + * + * @return Zero - success, otherwise - fail + */ +int provisioner_pb_gatt_recv(struct bt_mesh_conn *conn, struct net_buf_simple *buf); + +/** + * @brief This function initializes provisioner's PB-GATT and PB-ADV + * related information. + * + * @param[in] prov_info: Pointer to the application-initialized provisioner info. + * + * @return Zero - success, otherwise - fail + */ +int provisioner_prov_init(const struct bt_mesh_prov *prov_info); + +/** + * @brief This function parses the received unprovisioned device + * beacon advertising packets, and if checked, starts to provision this device + * using PB-ADV bearer. + * + * @param[in] buf: Pointer to the buffer containing unprovisioned device beacon + * + * @return None + */ +void provisioner_unprov_beacon_recv(struct net_buf_simple *buf); + +/** + * @brief This function parses the flags part of the + * received connectable mesh provisioning advertising packets. + * + * @param[in] buf: Pointer to the buffer containing advertising flags part + * + * @return True - success, False - fail + */ +bool provisioner_flags_match(struct net_buf_simple *buf); + +/** + * @brief This function parses the service UUID part of the + * received connectable mesh provisioning advertising packets. + * + * @param[in] buf: Pointer to the buffer containing service UUID part + * + * @return Zero - fail, otherwise - Service UUID(0x1827 or 0x1828) + */ +u16_t provisioner_srv_uuid_recv(struct net_buf_simple *buf); + +/** + * @brief This function parses the service data part of the + * received connectable mesh provisioning advertising packets. + * + * @param[in] buf: Pointer to the buffer containing the remianing service data part + * @param[in] addr: Pointer to the received device address + * @param[in] uuid: Service UUID contained in the service UUID part + * + * @return None + */ +void provisioner_srv_data_recv(struct net_buf_simple *buf, const bt_mesh_addr_t *addr, u16_t uuid); + +/** + * @brief This function gets the bt_mesh_prov pointer. + * + * @return bt_mesh_prov pointer(prov) + */ +const struct bt_mesh_prov *provisioner_get_prov_info(void); + +/** + * @brief This function resets all nodes information in provisioner_prov.c. + * + * @return Zero + */ +int provisioner_prov_reset_all_nodes(void); + +/* The following APIs are for primary provisioner application use */ + +/** @brief Add unprovisioned device info to unprov_dev queue + * + * @param[in] add_dev: Pointer to the structure containing the device information + * @param[in] flags: Flags indicate several operations of the device information + * - Remove device information from queue after it is provisioned (BIT0) + * - Start provisioning as soon as device is added to queue (BIT1) + * - Device can be flushed when device queue is full (BIT2) + * + * @return Zero on success or (negative) error code otherwise. + * + * @Note: 1. Currently address type only supports public address and static random address. + * 2. If device UUID and/or device address and address type already exist in the + * device queue, but the bearer differs from the existing one, add operation + * will also be successful and it will update the provision bearer supported by + * the device. + */ +int bt_mesh_provisioner_add_unprov_dev(struct bt_mesh_unprov_dev_add *add_dev, u8_t flags); + +/** @brief Delete device from queue, reset current provisioning link and reset the node + * + * @param[in] del_dev: Pointer to the structure containing the device information + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_mesh_provisioner_delete_device(struct bt_mesh_device_delete *del_dev); + +/** + * @brief This function sets a part of the device UUID for comparison before + * starting to provision the device. + * + * @param[in] offset: offset of the device UUID to be compared + * @param[in] length: length of the device UUID to be compared + * @param[in] match: value to be compared + * @param[in] prov_flag: flags indicate if uuid_match advertising packets are received, after that + * the device will be provisioned at once or reported to the application layer + * + * @return Zero - success, otherwise - fail + */ +int bt_mesh_provisioner_set_dev_uuid_match(u8_t offset, u8_t length, + const u8_t *match, bool prov_flag); + +/** @brief Callback for provisioner receiving advertising packet from unprovisioned devices which are + * not in the unprovisioned device queue. + * + * Report on the unprovisioned device beacon and mesh provisioning service advertising data to application layer + * + * @param addr Unprovisioned device address pointer + * @param addr_type Unprovisioned device address type + * @param dev_uuid Unprovisioned device device UUID pointer + * @param bearer Advertising packet received from PB-GATT or PB-ADV bearer + * @param adv_type Adv packet type, currently this is not used and we can use bearer to device + * the adv_type(ADV_IND or ADV_NONCONN_IND). This parameter will be used, when + * scan response data will be supported. + * + */ +typedef void (*unprov_adv_pkt_cb_t)(const u8_t addr[6], const u8_t addr_type, + const u8_t adv_type, const u8_t dev_uuid[16], + u16_t oob_info, bt_mesh_prov_bearer_t bearer); + +/** + * @brief This function registers the callback which notifies the application + * layer of the received mesh provisioning or unprovisioned device + * beacon advertizing packets (from devices not in the unprov device queue). + * + * @param[in] cb: Callback of the notifying adv pkts function + * + * @return Zero - success, otherwise - fail + */ +int bt_mesh_prov_adv_pkt_cb_register(unprov_adv_pkt_cb_t cb); + +/** + * @brief This function changes net_idx or flags or iv_index used in provisioning data. + * + * @param[in] info: Pointer of structure containing net_idx or flags or iv_index + * + * @return Zero - success, otherwise - fail + */ +int bt_mesh_provisioner_set_prov_data_info(struct bt_mesh_prov_data_info *info); + +/** + * @brief This function is called to input number/string out-put by unprovisioned device. + * + * @param[in] idx The provisioning link index + * @param[in] val Pointer of the input number/string + * @param[in] num_flag Flag indicates if it is a number or string + * + * @return Zero - success, otherwise - fail + */ +int bt_mesh_prov_set_oob_input_data(const u8_t idx, const u8_t *val, bool num_flag); + +/** + * @brief This function is called to output number/string which will be input by unprovisioned device. + * + * @param[in] idx The provisioning link index + * @param[in] num Pointer of the output number/string + * @param[in] size Size of the output number/string + * @param[in] num_flag Flag indicates if it is a number or string + * + * @return Zero - success, otherwise - fail + */ +#if 0 +int bt_mesh_prov_set_oob_output_data(const u8_t idx, u8_t *num, u8_t size, bool num_flag); +#endif + +/** + * @brief This function is called to read unprovisioned device's oob public key. + * + * @param[in] idx The provisioning link index + * @param[in] pub_key_x Unprovisioned device's Public Key X + * @param[in] pub_key_y Unprovisioned device's Public Key Y + * + * @return Zero - success, otherwise - fail + */ +int bt_mesh_prov_read_oob_pub_key(const u8_t idx, const u8_t pub_key_x[32], const u8_t pub_key_y[32]); + +/* The following APIs are for fast provisioning */ + +/** + * @brief This function is called to set fast_prov_flag. + * + * @param[in] flag: Flag set to fast_prov_flag + * + * @return None + */ +void provisioner_set_fast_prov_flag(bool flag); + +/** + * @brief This function is called to set netkey index used for fast provisioning. + * + * @param[in] net_key: Netkey value + * @param[in] net_idx: Netkey index + * + * @return status for set netkey index msg + */ +u8_t provisioner_set_fast_prov_net_idx(const u8_t *net_key, u16_t net_idx); + +/** + * @brief This function is called to get netkey index used for fast provisioning. + * + * @return net_idx of fast provisioning + */ +u16_t provisioner_get_fast_prov_net_idx(void); + +/** + * @brief This function is called to set unicast address range used for fast provisioning. + * + * @param[in] min: Minimum unicast address + * @param[in] max: Maximum unicast address + * + * @return status for set unicast address range message + */ +u8_t bt_mesh_set_fast_prov_unicast_addr_range(u16_t min, u16_t max); + +/** + * @brief This function is called to set flags & iv_index used for fast provisioning. + * + * @param[in] flags: Key refresh flag and iv update flag + * @param[in] iv_index: IV index + * + * @return None + */ +void bt_mesh_set_fast_prov_flags_iv_index(u8_t flags, u32_t iv_index); + +#endif /* _PROVISIONER_PROV_H_ */ diff --git a/components/bt/ble_mesh/mesh_core/provisioner_proxy.c b/components/bt/ble_mesh/mesh_core/provisioner_proxy.c new file mode 100644 index 0000000000..2961af3c42 --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/provisioner_proxy.c @@ -0,0 +1,608 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 + +#include "sdkconfig.h" + +#include "mesh_bearer_adapt.h" +#include "mesh_trace.h" + +#include "net.h" +#include "beacon.h" +#include "foundation.h" +#include "provisioner_prov.h" +#include "provisioner_proxy.h" +#include "provisioner_beacon.h" + +#if CONFIG_BLE_MESH_PROVISIONER + +#define PDU_TYPE(data) (data[0] & BIT_MASK(6)) +#define PDU_SAR(data) (data[0] >> 6) + +#define SAR_COMPLETE 0x00 +#define SAR_FIRST 0x01 +#define SAR_CONT 0x02 +#define SAR_LAST 0x03 + +#define CFG_FILTER_SET 0x00 +#define CFG_FILTER_ADD 0x01 +#define CFG_FILTER_REMOVE 0x02 +#define CFG_FILTER_STATUS 0x03 + +#define PDU_HDR(sar, type) (sar << 6 | (type & BIT_MASK(6))) + +#define SERVER_BUF_SIZE 68 + +#define ID_TYPE_NET 0x00 +#define ID_TYPE_NODE 0x01 + +#define NODE_ID_LEN 19 +#define NET_ID_LEN 11 + +#define CLOSE_REASON_PROXY 0xFF + +static int conn_count; + +static struct bt_mesh_proxy_server { + struct bt_mesh_conn *conn; + /* Provisioner can use filter to double check the dst within mesh messages */ + u16_t filter[CONFIG_BLE_MESH_PROXY_FILTER_SIZE]; + enum __packed { + NONE, + WHITELIST, + BLACKLIST, + PROV, + } filter_type; + u8_t msg_type; + struct net_buf_simple buf; +} servers[BLE_MESH_MAX_CONN]; + +static u8_t server_buf_data[SERVER_BUF_SIZE * BLE_MESH_MAX_CONN]; + +static struct bt_mesh_proxy_server *find_server(struct bt_mesh_conn *conn) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(servers); i++) { + if (servers[i].conn == conn) { + return &servers[i]; + } + } + + return NULL; +} + +static int filter_status(struct bt_mesh_proxy_server *server, + struct net_buf_simple *buf) +{ + /* TODO: Deal with received proxy configuration status messages */ + return 0; +} + +#if 0 +static void send_filter_set(struct bt_mesh_proxy_server *server, + struct bt_mesh_net_rx *rx, + struct net_buf_simple *buf) +{ + /* TODO: Act as proxy client, send proxy configuration set messages */ +} + +static void send_filter_add(struct bt_mesh_proxy_server *server, + struct bt_mesh_net_rx *rx, + struct net_buf_simple *buf) +{ + /* TODO: Act as proxy client, send proxy configuration add messages */ +} + +static void send_filter_remove(struct bt_mesh_proxy_server *server, + struct bt_mesh_net_rx *rx, + struct net_buf_simple *buf) +{ + /* TODO: Act as proxy client, send proxy configuration remove messages */ +} +#endif + +static void proxy_cfg(struct bt_mesh_proxy_server *server) +{ + NET_BUF_SIMPLE_DEFINE(buf, 29); + struct bt_mesh_net_rx rx; + u8_t opcode; + int err; + + /** In order to deal with proxy configuration messages, provisioner should + * do sth. like create mesh network after each device is provisioned. + */ + err = bt_mesh_net_decode(&server->buf, BLE_MESH_NET_IF_PROXY_CFG, + &rx, &buf); + if (err) { + BT_ERR("%s, Failed to decode Proxy Configuration (err %d)", __func__, err); + return; + } + + /* Remove network headers */ + net_buf_simple_pull(&buf, BLE_MESH_NET_HDR_LEN); + + BT_DBG("%u bytes: %s", buf.len, bt_hex(buf.data, buf.len)); + + if (buf.len < 1) { + BT_WARN("Too short proxy configuration PDU"); + return; + } + + opcode = net_buf_simple_pull_u8(&buf); + switch (opcode) { + case CFG_FILTER_STATUS: + filter_status(server, &buf); + break; + default: + BT_WARN("Unhandled configuration OpCode 0x%02x", opcode); + break; + } +} + +static void proxy_complete_pdu(struct bt_mesh_proxy_server *server) +{ + switch (server->msg_type) { +#if defined(CONFIG_BLE_MESH_GATT_PROXY) + case BLE_MESH_PROXY_NET_PDU: + BT_DBG("Mesh Network PDU"); + bt_mesh_net_recv(&server->buf, 0, BLE_MESH_NET_IF_PROXY); + break; + case BLE_MESH_PROXY_BEACON: + BT_DBG("Mesh Beacon PDU"); + provisioner_beacon_recv(&server->buf); + break; + case BLE_MESH_PROXY_CONFIG: + BT_DBG("Mesh Configuration PDU"); + proxy_cfg(server); + break; +#endif +#if defined(CONFIG_BLE_MESH_PB_GATT) + case BLE_MESH_PROXY_PROV: + BT_DBG("Mesh Provisioning PDU"); + provisioner_pb_gatt_recv(server->conn, &server->buf); + break; +#endif + default: + BT_WARN("Unhandled Message Type 0x%02x", server->msg_type); + break; + } + + net_buf_simple_reset(&server->buf); +} + +#define ATTR_IS_PROV(uuid) (uuid == BLE_MESH_UUID_MESH_PROV_VAL) + +static ssize_t proxy_recv(struct bt_mesh_conn *conn, + const struct bt_mesh_gatt_attr *attr, const void *buf, + u16_t len, u16_t offset, u8_t flags) +{ + struct bt_mesh_proxy_server *server = find_server(conn); + const u8_t *data = buf; + u16_t srvc_uuid = 0; + + if (!server) { + return -ENOTCONN; + } + + if (len < 1) { + BT_WARN("Too small Proxy PDU"); + return -EINVAL; + } + + srvc_uuid = bt_mesh_gattc_get_service_uuid(conn); + if (!srvc_uuid) { + BT_ERR("%s, No service uuid found", __func__); + return -ENOTCONN; + } + + if (ATTR_IS_PROV(srvc_uuid) != (PDU_TYPE(data) == BLE_MESH_PROXY_PROV)) { + BT_WARN("Proxy PDU type doesn't match GATT service uuid"); + return -EINVAL; + } + + if (len - 1 > net_buf_simple_tailroom(&server->buf)) { + BT_WARN("Too big proxy PDU"); + return -EINVAL; + } + + switch (PDU_SAR(data)) { + case SAR_COMPLETE: + if (server->buf.len) { + BT_WARN("Complete PDU while a pending incomplete one"); + return -EINVAL; + } + + server->msg_type = PDU_TYPE(data); + net_buf_simple_add_mem(&server->buf, data + 1, len - 1); + proxy_complete_pdu(server); + break; + + case SAR_FIRST: + if (server->buf.len) { + BT_WARN("First PDU while a pending incomplete one"); + return -EINVAL; + } + + server->msg_type = PDU_TYPE(data); + net_buf_simple_add_mem(&server->buf, data + 1, len - 1); + break; + + case SAR_CONT: + if (!server->buf.len) { + BT_WARN("Continuation with no prior data"); + return -EINVAL; + } + + if (server->msg_type != PDU_TYPE(data)) { + BT_WARN("Unexpected message type in continuation"); + return -EINVAL; + } + + net_buf_simple_add_mem(&server->buf, data + 1, len - 1); + break; + + case SAR_LAST: + if (!server->buf.len) { + BT_WARN("Last SAR PDU with no prior data"); + return -EINVAL; + } + + if (server->msg_type != PDU_TYPE(data)) { + BT_WARN("Unexpected message type in last SAR PDU"); + return -EINVAL; + } + + net_buf_simple_add_mem(&server->buf, data + 1, len - 1); + proxy_complete_pdu(server); + break; + } + + return len; +} + +static void proxy_prov_connected(const u8_t addr[6], struct bt_mesh_conn *conn, int id) +{ + struct bt_mesh_proxy_server *server = NULL; + + conn_count++; + + if (!servers[id].conn) { + server = &servers[id]; + } + + if (!server) { + BT_ERR("%s, No matching Proxy Client objects", __func__); + /** Disconnect current connection, clear part of prov_link + * information, like uuid, dev_addr, linking flag, etc. + */ + + return; + } + + server->conn = bt_mesh_conn_ref(conn); + server->filter_type = NONE; + memset(server->filter, 0, sizeof(server->filter)); + net_buf_simple_reset(&server->buf); + +#if defined(CONFIG_BLE_MESH_PB_GATT) + if (provisioner_set_prov_conn(addr, server->conn)) { + BT_ERR("%s, provisioner_set_prov_conn failed", __func__); + bt_mesh_gattc_disconnect(server->conn); + return; + } +#endif + + bt_mesh_gattc_exchange_mtu(id); +} + +static void proxy_prov_disconnected(struct bt_mesh_conn *conn, u8_t reason) +{ + struct bt_mesh_proxy_server *server = NULL; + int i; + + BT_DBG("conn %p, handle is %d, reason 0x%02x", conn, conn->handle, reason); + + if (conn_count) { + conn_count--; + } + + for (i = 0; i < ARRAY_SIZE(servers); i++) { + server = &servers[i]; + if (server->conn == conn) { + if (IS_ENABLED(CONFIG_BLE_MESH_PB_GATT) && + server->filter_type == PROV) { + provisioner_pb_gatt_close(conn, reason); + } + server->conn = NULL; + break; + } + } +} + +#if defined(CONFIG_BLE_MESH_PB_GATT) +static ssize_t prov_write_ccc_descr(struct bt_mesh_conn *conn, u8_t *addr) +{ + struct bt_mesh_proxy_server *server; + + server = find_server(conn); + + if (!server) { + BT_ERR("%s, No Proxy Server found", __func__); + return -ENOTCONN; + } + + if (server->filter_type == NONE) { + server->filter_type = PROV; + return provisioner_pb_gatt_open(conn, addr); + } + + return -EINVAL; +} + +static ssize_t prov_notification(struct bt_mesh_conn *conn, u8_t *data, u16_t len) +{ + struct bt_mesh_proxy_server *server; + + server = find_server(conn); + + if (!server) { + BT_ERR("%s, No Proxy Server found", __func__); + return -ENOTCONN; + } + + if (server->filter_type == PROV) { + return proxy_recv(conn, NULL, data, len, 0, 0); + } + + return -EINVAL; +} + +int provisioner_pb_gatt_enable(void) +{ + int i; + + BT_DBG("%s", __func__); + + for (i = 0; i < ARRAY_SIZE(servers); i++) { + if (servers[i].conn) { + servers[i].filter_type = PROV; + } + } + + return 0; +} + +int provisioner_pb_gatt_disable(void) +{ + int i; + + BT_DBG("%s", __func__); + + for (i = 0; i < ARRAY_SIZE(servers); i++) { + struct bt_mesh_proxy_server *server = &servers[i]; + + if (server->conn && server->filter_type == PROV) { + bt_mesh_gattc_disconnect(server->conn); + server->filter_type = NONE; + } + } + + return 0; +} + +#endif /* CONFIG_BLE_MESH_PB_GATT */ + +#if defined(CONFIG_BLE_MESH_GATT_PROXY) +static ssize_t proxy_write_ccc_descr(struct bt_mesh_conn *conn) +{ + struct bt_mesh_proxy_server *server; + + server = find_server(conn); + + if (!server) { + BT_ERR("%s, No Proxy Server found", __func__); + return -ENOTCONN; + } + + if (server->filter_type == NONE) { + server->filter_type = WHITELIST; + return 0; + } + + return -EINVAL; +} + +static ssize_t proxy_notification(struct bt_mesh_conn *conn, u8_t *data, u16_t len) +{ + return proxy_recv(conn, NULL, data, len, 0, 0); +} + +/** Currently provisioner does't need bt_mesh_provisioner_proxy_enable() + * and bt_mesh_provisioner_proxy_disable() functions, and once they are + * used, provisioner can be enabled to parse node_id_adv and net_id_adv + * in order to support proxy client role. + * And if gatt_proxy is disabled, provisioner can stop dealing with + * these two kinds of connectable advertising packets. + */ +int bt_mesh_provisioner_proxy_enable(void) +{ + int i; + + BT_DBG("%s", __func__); + + for (i = 0; i < ARRAY_SIZE(servers); i++) { + if (servers[i].conn) { + servers[i].filter_type = WHITELIST; + } + } + + /** TODO: Once at leat one device has been provisioned, provisioner + * can be set to allow receiving and parsing node_id & net_id adv + * packets, and we may use a global flag to indicate this. + */ + + return 0; +} + +static void bt_mesh_proxy_gatt_proxy_disconnect(void) +{ + int i; + + BT_DBG("%s", __func__); + + for (i = 0; i < ARRAY_SIZE(servers); i++) { + struct bt_mesh_proxy_server *server = &servers[i]; + + if (server->conn && (server->filter_type == WHITELIST || + server->filter_type == BLACKLIST)) { + server->filter_type = NONE; + bt_mesh_gattc_disconnect(server->conn); + } + } +} + +int bt_mesh_provisioner_proxy_disable(void) +{ + BT_DBG("%s", __func__); + + /** TODO: Once this function is invoked, provisioner shall stop + * receiving and parsing node_id & net_id adv packets, and if + * proxy connection exists, we should disconnect it. + */ + + bt_mesh_proxy_gatt_proxy_disconnect(); + + return 0; +} + +#endif /* CONFIG_BLE_MESH_GATT_PROXY */ + +static int proxy_send(struct bt_mesh_conn *conn, const void *data, u16_t len) +{ + BT_DBG("%u bytes: %s", len, bt_hex(data, len)); + +#if defined(CONFIG_BLE_MESH_GATT_PROXY) || defined(CONFIG_BLE_MESH_PB_GATT) + return bt_mesh_gattc_write_no_rsp(conn, NULL, data, len); +#endif + + return 0; +} + +static int proxy_prov_segment_and_send(struct bt_mesh_conn *conn, u8_t type, + struct net_buf_simple *msg) +{ + u16_t mtu; + + if (conn == NULL) { + BT_ERR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + BT_DBG("conn %p type 0x%02x len %u: %s", conn, type, msg->len, + bt_hex(msg->data, msg->len)); + + mtu = bt_mesh_gattc_get_mtu_info(conn); + if (!mtu) { + BT_ERR("%s, Conn used to get mtu does not exist", __func__); + return -ENOTCONN; + } + + /* ATT_MTU - OpCode (1 byte) - Handle (2 bytes) */ + mtu -= 3; + if (mtu > msg->len) { + net_buf_simple_push_u8(msg, PDU_HDR(SAR_COMPLETE, type)); + return proxy_send(conn, msg->data, msg->len); + } + + net_buf_simple_push_u8(msg, PDU_HDR(SAR_FIRST, type)); + proxy_send(conn, msg->data, mtu); + net_buf_simple_pull(msg, mtu); + + while (msg->len) { + if (msg->len + 1 < mtu) { + net_buf_simple_push_u8(msg, PDU_HDR(SAR_LAST, type)); + proxy_send(conn, msg->data, msg->len); + break; + } + + net_buf_simple_push_u8(msg, PDU_HDR(SAR_CONT, type)); + proxy_send(conn, msg->data, mtu); + net_buf_simple_pull(msg, mtu); + } + + return 0; +} + +int provisioner_proxy_send(struct bt_mesh_conn *conn, u8_t type, + struct net_buf_simple *msg) +{ + struct bt_mesh_proxy_server *server = find_server(conn); + + if (!server) { + BT_ERR("$%s, No Proxy Server found", __func__); + return -ENOTCONN; + } + + if ((server->filter_type == PROV) != (type == BLE_MESH_PROXY_PROV)) { + BT_ERR("%s, Invalid PDU type for Proxy Client", __func__); + return -EINVAL; + } + + return proxy_prov_segment_and_send(conn, type, msg); +} + +static struct bt_mesh_prov_conn_cb conn_callbacks = { + .connected = proxy_prov_connected, + .disconnected = proxy_prov_disconnected, +#if defined(CONFIG_BLE_MESH_PB_GATT) + .prov_write_descr = prov_write_ccc_descr, + .prov_notify = prov_notification, +#endif /* CONFIG_BLE_MESH_PB_GATT */ +#if defined(CONFIG_BLE_MESH_GATT_PROXY) + .proxy_write_descr = proxy_write_ccc_descr, + .proxy_notify = proxy_notification, +#endif /* CONFIG_BLE_MESH_GATT_PROXY */ +}; + +void provisioner_proxy_srv_data_recv(struct net_buf_simple *buf) +{ + /** TODO: Parse node_id_adv or net_id_adv pkts. Currently we + * don't support this function, and if realized later, proxy + * client need to check if there is server structure left + * before create connection with a server. + * check conn_count & CONFIG_BLE_MESH_PBG_SAME_TIME + */ +} + +int provisioner_proxy_init(void) +{ + int i; + + /* Initialize the server receive buffers */ + for (i = 0; i < ARRAY_SIZE(servers); i++) { + struct bt_mesh_proxy_server *server = &servers[i]; + + server->buf.size = SERVER_BUF_SIZE; + server->buf.__buf = server_buf_data + (i * SERVER_BUF_SIZE); + } + + bt_mesh_gattc_conn_cb_register(&conn_callbacks); + + return 0; +} + +#endif /* CONFIG_BLE_MESH_PROVISIONER */ diff --git a/components/bt/ble_mesh/mesh_core/provisioner_proxy.h b/components/bt/ble_mesh/mesh_core/provisioner_proxy.h new file mode 100644 index 0000000000..5da92f433b --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/provisioner_proxy.h @@ -0,0 +1,89 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#ifndef _PROVISIONER_PROXY_H_ +#define _PROVISIONER_PROXY_H_ + +#include "mesh_buf.h" + +#define BLE_MESH_PROXY_NET_PDU 0x00 +#define BLE_MESH_PROXY_BEACON 0x01 +#define BLE_MESH_PROXY_CONFIG 0x02 +#define BLE_MESH_PROXY_PROV 0x03 + +/** + * @brief This function is called to send proxy protocol messages. + * + * @param[in] conn: Pointer to bt_conn structure + * @param[in] type: Proxy protocol message type + * @param[in] msg: Pointer to the buffer contains sending message. + * + * @return Zero-success, other-fail + */ +int provisioner_proxy_send(struct bt_mesh_conn *conn, u8_t type, struct net_buf_simple *msg); + +/** + * @brief This function is called to parse received node identity and net + * id adv pkts and create connection if deceided to. + * + * @param[in] buf: Pointer to the buffer contains received message. + * + * @return None + */ +void provisioner_proxy_srv_data_recv(struct net_buf_simple *buf); + +/** + * @brief This function is called to initialize proxy provisioner structure + * and register proxy connection related callbacks. + * + * @return Zero-success, other-fail + */ +int provisioner_proxy_init(void); + +/** + * @brief This function is called to enable dealing with proxy provisioning + * messages. + * + * @return Zero-success, other-fail + */ +int provisioner_pb_gatt_enable(void); + +/** + * @brief This function is called to disable dealing with proxy provisioning + * messages and if proxy provisioning connections exist, the connections + * will be disconnected. + * + * @return Zero-success, other-fail + */ +int provisioner_pb_gatt_disable(void); + +/* The following APIs are for application use */ +/** + * @brief This function is called to enable receiving node identity and net + * id adv pkts. + * + * @return Zero-success, other-fail + */ +int bt_mesh_provisioner_proxy_enable(void); + +/** + * @brief This function is called to disable receiving node identity and net + * id adv pkts, and if proxy connections exist, these connections will + * be disconnected. + * + * @return Zero-success, other-fail + */ +int bt_mesh_provisioner_proxy_disable(void); + +#endif /* _PROVISIONER_PROXY_H_ */ diff --git a/components/bt/ble_mesh/mesh_core/proxy.c b/components/bt/ble_mesh/mesh_core/proxy.c new file mode 100644 index 0000000000..fd9807f54b --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/proxy.c @@ -0,0 +1,1393 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include + +#include "sdkconfig.h" +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BLE_MESH_DEBUG_PROXY) + +#include "mesh_buf.h" +#include "mesh_util.h" +#include "mesh_bearer_adapt.h" +#include "mesh_trace.h" + +#include "mesh.h" +#include "adv.h" +#include "net.h" +#include "prov.h" +#include "beacon.h" +#include "foundation.h" +#include "access.h" +#include "proxy.h" + +#if CONFIG_BLE_MESH_NODE + +#define PDU_TYPE(data) (data[0] & BIT_MASK(6)) +#define PDU_SAR(data) (data[0] >> 6) + +#define SAR_COMPLETE 0x00 +#define SAR_FIRST 0x01 +#define SAR_CONT 0x02 +#define SAR_LAST 0x03 + +#define CFG_FILTER_SET 0x00 +#define CFG_FILTER_ADD 0x01 +#define CFG_FILTER_REMOVE 0x02 +#define CFG_FILTER_STATUS 0x03 + +#define PDU_HDR(sar, type) (sar << 6 | (type & BIT_MASK(6))) + +#define CLIENT_BUF_SIZE 68 + +#define ADV_OPT (BLE_MESH_ADV_OPT_CONNECTABLE | BLE_MESH_ADV_OPT_ONE_TIME) + +static const struct bt_mesh_adv_param slow_adv_param = { + .options = ADV_OPT, + .interval_min = BLE_MESH_GAP_ADV_SLOW_INT_MIN, + .interval_max = BLE_MESH_GAP_ADV_SLOW_INT_MAX, +}; + +static const struct bt_mesh_adv_param fast_adv_param = { + .options = ADV_OPT, + .interval_min = BLE_MESH_GAP_ADV_FAST_INT_MIN_0, + .interval_max = BLE_MESH_GAP_ADV_FAST_INT_MAX_0, +}; + +static bool proxy_adv_enabled; + +#if defined(CONFIG_BLE_MESH_GATT_PROXY) +static void proxy_send_beacons(struct k_work *work); +static u16_t proxy_ccc_val; +#endif + +#if defined(CONFIG_BLE_MESH_PB_GATT) +static u16_t prov_ccc_val; +static bool prov_fast_adv; +#endif + +enum { + SAR_TIMER_START, /* Timer for SAR transfer has been started */ + NUM_FLAGS, +}; + +#define PROXY_SAR_TRANS_TIMEOUT K_SECONDS(20) + +static struct bt_mesh_proxy_client { + struct bt_mesh_conn *conn; + u16_t filter[CONFIG_BLE_MESH_PROXY_FILTER_SIZE]; + enum __packed { + NONE, + WHITELIST, + BLACKLIST, + PROV, + } filter_type; + u8_t msg_type; +#if defined(CONFIG_BLE_MESH_GATT_PROXY) + struct k_work send_beacons; +#endif + struct net_buf_simple buf; + /* Proxy Server: 20s timeout for each segmented proxy pdu */ + BLE_MESH_ATOMIC_DEFINE(flags, NUM_FLAGS); + struct k_delayed_work sar_timer; +} clients[BLE_MESH_MAX_CONN] = { + [0 ... (BLE_MESH_MAX_CONN - 1)] = { +#if defined(CONFIG_BLE_MESH_GATT_PROXY) + .send_beacons = _K_WORK_INITIALIZER(proxy_send_beacons), +#endif + }, +}; + +static u8_t client_buf_data[CLIENT_BUF_SIZE * BLE_MESH_MAX_CONN]; + +/* Track which service is enabled */ +static enum { + MESH_GATT_NONE, + MESH_GATT_PROV, + MESH_GATT_PROXY, +} gatt_svc = MESH_GATT_NONE; + +static char device_name[DEVICE_NAME_SIZE] = "ESP-BLE-MESH"; + +int bt_mesh_set_device_name(const char *name) +{ + if (!name) { + BT_ERR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + if (strlen(name) > DEVICE_NAME_SIZE) { + BT_ERR("%s, Too long device name", __func__); + return -EINVAL; + } + + memset(device_name, 0x0, sizeof(device_name)); + memcpy(device_name, name, strlen(name)); + + return 0; +} + +static struct bt_mesh_proxy_client *find_client(struct bt_mesh_conn *conn) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(clients); i++) { + if (clients[i].conn == conn) { + return &clients[i]; + } + } + + return NULL; +} + +#if defined(CONFIG_BLE_MESH_GATT_PROXY) +/* Next subnet in queue to be advertised */ +static int next_idx; + +static int proxy_segment_and_send(struct bt_mesh_conn *conn, u8_t type, + struct net_buf_simple *msg); + +static int filter_set(struct bt_mesh_proxy_client *client, + struct net_buf_simple *buf) +{ + u8_t type; + + if (buf->len < 1) { + BT_WARN("Too short Filter Set message"); + return -EINVAL; + } + + type = net_buf_simple_pull_u8(buf); + BT_DBG("type 0x%02x", type); + + switch (type) { + case 0x00: + (void)memset(client->filter, 0, sizeof(client->filter)); + client->filter_type = WHITELIST; + break; + case 0x01: + (void)memset(client->filter, 0, sizeof(client->filter)); + client->filter_type = BLACKLIST; + break; + default: + BT_WARN("Prohibited Filter Type 0x%02x", type); + return -EINVAL; + } + + return 0; +} + +static void filter_add(struct bt_mesh_proxy_client *client, u16_t addr) +{ + int i; + + BT_DBG("addr 0x%04x", addr); + + if (addr == BLE_MESH_ADDR_UNASSIGNED) { + return; + } + + for (i = 0; i < ARRAY_SIZE(client->filter); i++) { + if (client->filter[i] == addr) { + return; + } + } + + for (i = 0; i < ARRAY_SIZE(client->filter); i++) { + if (client->filter[i] == BLE_MESH_ADDR_UNASSIGNED) { + client->filter[i] = addr; + return; + } + } +} + +static void filter_remove(struct bt_mesh_proxy_client *client, u16_t addr) +{ + int i; + + BT_DBG("addr 0x%04x", addr); + + if (addr == BLE_MESH_ADDR_UNASSIGNED) { + return; + } + + for (i = 0; i < ARRAY_SIZE(client->filter); i++) { + if (client->filter[i] == addr) { + client->filter[i] = BLE_MESH_ADDR_UNASSIGNED; + return; + } + } +} + +static void send_filter_status(struct bt_mesh_proxy_client *client, + struct bt_mesh_net_rx *rx, + struct net_buf_simple *buf) +{ + struct bt_mesh_net_tx tx = { + .sub = rx->sub, + .ctx = &rx->ctx, + .src = bt_mesh_primary_addr(), + }; + u16_t filter_size; + int i, err; + + /* Configuration messages always have dst unassigned */ + tx.ctx->addr = BLE_MESH_ADDR_UNASSIGNED; + + net_buf_simple_reset(buf); + net_buf_simple_reserve(buf, 10); + + net_buf_simple_add_u8(buf, CFG_FILTER_STATUS); + + if (client->filter_type == WHITELIST) { + net_buf_simple_add_u8(buf, 0x00); + } else { + net_buf_simple_add_u8(buf, 0x01); + } + + for (filter_size = 0U, i = 0; i < ARRAY_SIZE(client->filter); i++) { + if (client->filter[i] != BLE_MESH_ADDR_UNASSIGNED) { + filter_size++; + } + } + + net_buf_simple_add_be16(buf, filter_size); + + BT_DBG("%u bytes: %s", buf->len, bt_hex(buf->data, buf->len)); + + err = bt_mesh_net_encode(&tx, buf, true); + if (err) { + BT_ERR("%s, Encoding Proxy cfg message failed (err %d)", __func__, err); + return; + } + + err = proxy_segment_and_send(client->conn, BLE_MESH_PROXY_CONFIG, buf); + if (err) { + BT_ERR("%s, Failed to send proxy cfg message (err %d)", __func__, err); + } +} + +static void proxy_cfg(struct bt_mesh_proxy_client *client) +{ + NET_BUF_SIMPLE_DEFINE(buf, 29); + struct bt_mesh_net_rx rx; + u8_t opcode; + int err; + + err = bt_mesh_net_decode(&client->buf, BLE_MESH_NET_IF_PROXY_CFG, + &rx, &buf); + if (err) { + BT_ERR("%s, Failed to decode Proxy Configuration (err %d)", __func__, err); + return; + } + + /* Remove network headers */ + net_buf_simple_pull(&buf, BLE_MESH_NET_HDR_LEN); + + BT_DBG("%u bytes: %s", buf.len, bt_hex(buf.data, buf.len)); + + if (buf.len < 1) { + BT_WARN("Too short proxy configuration PDU"); + return; + } + + opcode = net_buf_simple_pull_u8(&buf); + switch (opcode) { + case CFG_FILTER_SET: + filter_set(client, &buf); + send_filter_status(client, &rx, &buf); + break; + case CFG_FILTER_ADD: + while (buf.len >= 2) { + u16_t addr; + + addr = net_buf_simple_pull_be16(&buf); + filter_add(client, addr); + } + send_filter_status(client, &rx, &buf); + break; + case CFG_FILTER_REMOVE: + while (buf.len >= 2) { + u16_t addr; + + addr = net_buf_simple_pull_be16(&buf); + filter_remove(client, addr); + } + send_filter_status(client, &rx, &buf); + break; + default: + BT_WARN("Unhandled configuration OpCode 0x%02x", opcode); + break; + } +} + +static int beacon_send(struct bt_mesh_conn *conn, struct bt_mesh_subnet *sub) +{ + NET_BUF_SIMPLE_DEFINE(buf, 23); + + net_buf_simple_reserve(&buf, 1); + bt_mesh_beacon_create(sub, &buf); + + return proxy_segment_and_send(conn, BLE_MESH_PROXY_BEACON, &buf); +} + +static void proxy_send_beacons(struct k_work *work) +{ + struct bt_mesh_proxy_client *client; + int i; + + client = CONTAINER_OF(work, struct bt_mesh_proxy_client, send_beacons); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + struct bt_mesh_subnet *sub = &bt_mesh.sub[i]; + + if (sub->net_idx != BLE_MESH_KEY_UNUSED) { + beacon_send(client->conn, sub); + } + } +} + +void bt_mesh_proxy_beacon_send(struct bt_mesh_subnet *sub) +{ + int i; + + if (!sub) { + /* NULL means we send on all subnets */ + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + if (bt_mesh.sub[i].net_idx != BLE_MESH_KEY_UNUSED) { + bt_mesh_proxy_beacon_send(&bt_mesh.sub[i]); + } + } + + return; + } + + for (i = 0; i < ARRAY_SIZE(clients); i++) { + if (clients[i].conn) { + beacon_send(clients[i].conn, sub); + } + } +} + +void bt_mesh_proxy_identity_start(struct bt_mesh_subnet *sub) +{ + sub->node_id = BLE_MESH_NODE_IDENTITY_RUNNING; + sub->node_id_start = k_uptime_get_32(); + + /* Prioritize the recently enabled subnet */ + next_idx = sub - bt_mesh.sub; +} + +void bt_mesh_proxy_identity_stop(struct bt_mesh_subnet *sub) +{ + sub->node_id = BLE_MESH_NODE_IDENTITY_STOPPED; + sub->node_id_start = 0U; +} + +int bt_mesh_proxy_identity_enable(void) +{ + int i, count = 0; + + BT_DBG("%s", __func__); + + if (!bt_mesh_is_provisioned()) { + return -EAGAIN; + } + + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + struct bt_mesh_subnet *sub = &bt_mesh.sub[i]; + + if (sub->net_idx == BLE_MESH_KEY_UNUSED) { + continue; + } + + if (sub->node_id == BLE_MESH_NODE_IDENTITY_NOT_SUPPORTED) { + continue; + } + + bt_mesh_proxy_identity_start(sub); + count++; + } + + if (count) { + bt_mesh_adv_update(); + } + + return 0; +} + +#endif /* GATT_PROXY */ + +static void proxy_complete_pdu(struct bt_mesh_proxy_client *client) +{ + switch (client->msg_type) { +#if defined(CONFIG_BLE_MESH_GATT_PROXY) + case BLE_MESH_PROXY_NET_PDU: + BT_DBG("Mesh Network PDU"); + bt_mesh_net_recv(&client->buf, 0, BLE_MESH_NET_IF_PROXY); + break; + case BLE_MESH_PROXY_BEACON: + BT_DBG("Mesh Beacon PDU"); + bt_mesh_beacon_recv(&client->buf); + break; + case BLE_MESH_PROXY_CONFIG: + BT_DBG("Mesh Configuration PDU"); + proxy_cfg(client); + break; +#endif +#if defined(CONFIG_BLE_MESH_PB_GATT) + case BLE_MESH_PROXY_PROV: + BT_DBG("Mesh Provisioning PDU"); + bt_mesh_pb_gatt_recv(client->conn, &client->buf); + break; +#endif + default: + BT_WARN("Unhandled Message Type 0x%02x", client->msg_type); + break; + } + + net_buf_simple_reset(&client->buf); +} + +#define ATTR_IS_PROV(attr) (attr->user_data != NULL) + +static ssize_t proxy_recv(struct bt_mesh_conn *conn, + const struct bt_mesh_gatt_attr *attr, const void *buf, + u16_t len, u16_t offset, u8_t flags) +{ + struct bt_mesh_proxy_client *client = find_client(conn); + const u8_t *data = buf; + + if (!client) { + return -ENOTCONN; + } + + if (len < 1) { + BT_WARN("Too small Proxy PDU"); + return -EINVAL; + } + + if (ATTR_IS_PROV(attr) != (PDU_TYPE(data) == BLE_MESH_PROXY_PROV)) { + BT_WARN("Proxy PDU type doesn't match GATT service"); + return -EINVAL; + } + + if (len - 1 > net_buf_simple_tailroom(&client->buf)) { + BT_WARN("Too big proxy PDU"); + return -EINVAL; + } + + switch (PDU_SAR(data)) { + case SAR_COMPLETE: + if (client->buf.len) { + BT_WARN("Complete PDU while a pending incomplete one"); + return -EINVAL; + } + + client->msg_type = PDU_TYPE(data); + net_buf_simple_add_mem(&client->buf, data + 1, len - 1); + proxy_complete_pdu(client); + break; + + case SAR_FIRST: + if (client->buf.len) { + BT_WARN("First PDU while a pending incomplete one"); + return -EINVAL; + } + + if (!bt_mesh_atomic_test_and_set_bit(client->flags, SAR_TIMER_START)) { + k_delayed_work_submit(&client->sar_timer, PROXY_SAR_TRANS_TIMEOUT); + } + + client->msg_type = PDU_TYPE(data); + net_buf_simple_add_mem(&client->buf, data + 1, len - 1); + break; + + case SAR_CONT: + if (!client->buf.len) { + BT_WARN("Continuation with no prior data"); + return -EINVAL; + } + + if (client->msg_type != PDU_TYPE(data)) { + BT_WARN("Unexpected message type in continuation"); + return -EINVAL; + } + + net_buf_simple_add_mem(&client->buf, data + 1, len - 1); + break; + + case SAR_LAST: + if (!client->buf.len) { + BT_WARN("Last SAR PDU with no prior data"); + return -EINVAL; + } + + if (client->msg_type != PDU_TYPE(data)) { + BT_WARN("Unexpected message type in last SAR PDU"); + return -EINVAL; + } + + if (bt_mesh_atomic_test_and_clear_bit(client->flags, SAR_TIMER_START)) { + k_delayed_work_cancel(&client->sar_timer); + } + + net_buf_simple_add_mem(&client->buf, data + 1, len - 1); + proxy_complete_pdu(client); + break; + } + + return len; +} + +static int conn_count; + +static void proxy_connected(struct bt_mesh_conn *conn, u8_t err) +{ + struct bt_mesh_proxy_client *client; + int i; + + BT_DBG("conn %p err 0x%02x", conn, err); + + conn_count++; + + /* Since we use ADV_OPT_ONE_TIME */ + proxy_adv_enabled = false; + + /* Try to re-enable advertising in case it's possible */ + if (conn_count < BLE_MESH_MAX_CONN) { + bt_mesh_adv_update(); + } + + for (client = NULL, i = 0; i < ARRAY_SIZE(clients); i++) { + if (!clients[i].conn) { + client = &clients[i]; + break; + } + } + + if (!client) { + BT_ERR("%s, No free Proxy Client objects", __func__); + return; + } + + client->conn = bt_mesh_conn_ref(conn); + client->filter_type = NONE; + (void)memset(client->filter, 0, sizeof(client->filter)); + net_buf_simple_reset(&client->buf); +} + +static void proxy_disconnected(struct bt_mesh_conn *conn, u8_t reason) +{ + int i; + + BT_DBG("conn %p reason 0x%02x", conn, reason); + + conn_count--; + + for (i = 0; i < ARRAY_SIZE(clients); i++) { + struct bt_mesh_proxy_client *client = &clients[i]; + + if (client->conn == conn) { + if (IS_ENABLED(CONFIG_BLE_MESH_PB_GATT) && + client->filter_type == PROV) { + bt_mesh_pb_gatt_close(conn); + } + + if (bt_mesh_atomic_test_and_clear_bit(client->flags, SAR_TIMER_START)) { + k_delayed_work_cancel(&client->sar_timer); + } + + bt_mesh_conn_unref(client->conn); + client->conn = NULL; + break; + } + } + + bt_mesh_adv_update(); +} + +struct net_buf_simple *bt_mesh_proxy_get_buf(void) +{ + struct net_buf_simple *buf = &clients[0].buf; + + net_buf_simple_reset(buf); + + return buf; +} + +#if defined(CONFIG_BLE_MESH_PB_GATT) +static ssize_t prov_ccc_write(struct bt_mesh_conn *conn, + const struct bt_mesh_gatt_attr *attr, + const void *buf, u16_t len, + u16_t offset, u8_t flags) +{ + struct bt_mesh_proxy_client *client; + u16_t *value = attr->user_data; + + BT_DBG("len %u: %s", len, bt_hex(buf, len)); + + if (len != sizeof(*value)) { + return BLE_MESH_GATT_ERR(BLE_MESH_ATT_ERR_INVALID_ATTRIBUTE_LEN); + } + + *value = sys_get_le16(buf); + if (*value != BLE_MESH_GATT_CCC_NOTIFY) { + BT_WARN("Client wrote 0x%04x instead enabling notify", *value); + return len; + } + + /* If a connection exists there must be a client */ + client = find_client(conn); + __ASSERT(client, "No client for connection"); + + if (client->filter_type == NONE) { + client->filter_type = PROV; + bt_mesh_pb_gatt_open(conn); + } + + return len; +} + +static ssize_t prov_ccc_read(struct bt_mesh_conn *conn, + const struct bt_mesh_gatt_attr *attr, + void *buf, u16_t len, u16_t offset) +{ + u16_t *value = attr->user_data; + + return bt_mesh_gatts_attr_read(conn, attr, buf, len, offset, value, + sizeof(*value)); +} + +/* Mesh Provisioning Service Declaration */ +static struct bt_mesh_gatt_attr prov_attrs[] = { + BLE_MESH_GATT_PRIMARY_SERVICE(BLE_MESH_UUID_MESH_PROV), + + BLE_MESH_GATT_CHARACTERISTIC(BLE_MESH_UUID_MESH_PROV_DATA_IN, + BLE_MESH_GATT_CHRC_WRITE_WITHOUT_RESP), + BLE_MESH_GATT_DESCRIPTOR(BLE_MESH_UUID_MESH_PROV_DATA_IN, BLE_MESH_GATT_PERM_WRITE, + NULL, proxy_recv, (void *)1), + + BLE_MESH_GATT_CHARACTERISTIC(BLE_MESH_UUID_MESH_PROV_DATA_OUT, + BLE_MESH_GATT_CHRC_NOTIFY), + BLE_MESH_GATT_DESCRIPTOR(BLE_MESH_UUID_MESH_PROV_DATA_OUT, BLE_MESH_GATT_PERM_NONE, + NULL, NULL, NULL), + /* Add custom CCC as clients need to be tracked individually */ + BLE_MESH_GATT_DESCRIPTOR(BLE_MESH_UUID_GATT_CCC, + BLE_MESH_GATT_PERM_WRITE | BLE_MESH_GATT_PERM_READ, + prov_ccc_read, prov_ccc_write, &prov_ccc_val), +}; + +struct bt_mesh_gatt_service prov_svc = BLE_MESH_GATT_SERVICE(prov_attrs); + +int bt_mesh_proxy_prov_enable(void) +{ + int i; + + BT_DBG("%s", __func__); + + if (gatt_svc == MESH_GATT_PROV) { + BT_WARN("%s, Already", __func__); + return -EALREADY; + } + + if (gatt_svc != MESH_GATT_NONE) { + BT_WARN("%s, Busy", __func__); + return -EBUSY; + } + + bt_mesh_gatts_service_start(&prov_svc); + gatt_svc = MESH_GATT_PROV; + prov_fast_adv = true; + + for (i = 0; i < ARRAY_SIZE(clients); i++) { + if (clients[i].conn) { + clients[i].filter_type = PROV; + } + } + + + return 0; +} + +int bt_mesh_proxy_prov_disable(bool disconnect) +{ + int i; + + BT_DBG("%s", __func__); + + if (gatt_svc == MESH_GATT_NONE) { + BT_WARN("%s, Already", __func__); + return -EALREADY; + } + + if (gatt_svc != MESH_GATT_PROV) { + BT_WARN("%s, Busy", __func__); + return -EBUSY; + } + + bt_mesh_gatts_service_stop(&prov_svc); + gatt_svc = MESH_GATT_NONE; + + for (i = 0; i < ARRAY_SIZE(clients); i++) { + struct bt_mesh_proxy_client *client = &clients[i]; + + if (!client->conn || client->filter_type != PROV) { + continue; + } + + if (disconnect) { + bt_mesh_gatts_disconnect(client->conn, 0x13); + } else { + bt_mesh_pb_gatt_close(client->conn); + client->filter_type = NONE; + } + } + + bt_mesh_adv_update(); + + return 0; +} + +#endif /* CONFIG_BLE_MESH_PB_GATT */ + +#if defined(CONFIG_BLE_MESH_GATT_PROXY) +static ssize_t proxy_ccc_write(struct bt_mesh_conn *conn, + const struct bt_mesh_gatt_attr *attr, + const void *buf, u16_t len, + u16_t offset, u8_t flags) +{ + struct bt_mesh_proxy_client *client; + u16_t value; + + BT_DBG("len %u: %s", len, bt_hex(buf, len)); + + if (len != sizeof(value)) { + return BLE_MESH_GATT_ERR(BLE_MESH_ATT_ERR_INVALID_ATTRIBUTE_LEN); + } + + value = sys_get_le16(buf); + if (value != BLE_MESH_GATT_CCC_NOTIFY) { + BT_WARN("Client wrote 0x%04x instead enabling notify", value); + return len; + } + + /* If a connection exists there must be a client */ + client = find_client(conn); + __ASSERT(client, "No client for connection"); + + if (client->filter_type == NONE) { + client->filter_type = WHITELIST; + k_work_submit(&client->send_beacons); + } + + return len; +} + +static ssize_t proxy_ccc_read(struct bt_mesh_conn *conn, + const struct bt_mesh_gatt_attr *attr, + void *buf, u16_t len, u16_t offset) +{ + u16_t *value = attr->user_data; + + return bt_mesh_gatts_attr_read(conn, attr, buf, len, offset, value, + sizeof(*value)); +} + +/* Mesh Proxy Service Declaration */ +static struct bt_mesh_gatt_attr proxy_attrs[] = { + BLE_MESH_GATT_PRIMARY_SERVICE(BLE_MESH_UUID_MESH_PROXY), + + BLE_MESH_GATT_CHARACTERISTIC(BLE_MESH_UUID_MESH_PROXY_DATA_IN, + BLE_MESH_GATT_CHRC_WRITE_WITHOUT_RESP), + BLE_MESH_GATT_DESCRIPTOR(BLE_MESH_UUID_MESH_PROXY_DATA_IN, BLE_MESH_GATT_PERM_WRITE, + NULL, proxy_recv, NULL), + + BLE_MESH_GATT_CHARACTERISTIC(BLE_MESH_UUID_MESH_PROXY_DATA_OUT, + BLE_MESH_GATT_CHRC_NOTIFY), + BLE_MESH_GATT_DESCRIPTOR(BLE_MESH_UUID_MESH_PROXY_DATA_OUT, BLE_MESH_GATT_PERM_NONE, + NULL, NULL, NULL), + /* Add custom CCC as clients need to be tracked individually */ + BLE_MESH_GATT_DESCRIPTOR(BLE_MESH_UUID_GATT_CCC, + BLE_MESH_GATT_PERM_READ | BLE_MESH_GATT_PERM_WRITE, + proxy_ccc_read, proxy_ccc_write, &proxy_ccc_val), +}; + +struct bt_mesh_gatt_service proxy_svc = BLE_MESH_GATT_SERVICE(proxy_attrs); + +int bt_mesh_proxy_gatt_enable(void) +{ + int i; + + BT_DBG("%s", __func__); + + if (gatt_svc == MESH_GATT_PROXY) { + BT_WARN("%s, Already", __func__); + return -EALREADY; + } + + if (gatt_svc != MESH_GATT_NONE) { + BT_WARN("%s, Busy", __func__); + return -EBUSY; + } + + bt_mesh_gatts_service_start(&proxy_svc); + gatt_svc = MESH_GATT_PROXY; + + for (i = 0; i < ARRAY_SIZE(clients); i++) { + if (clients[i].conn) { + clients[i].filter_type = WHITELIST; + } + } + + return 0; +} + +void bt_mesh_proxy_gatt_disconnect(void) +{ + int i; + + BT_DBG("%s", __func__); + + for (i = 0; i < ARRAY_SIZE(clients); i++) { + struct bt_mesh_proxy_client *client = &clients[i]; + + if (client->conn && (client->filter_type == WHITELIST || + client->filter_type == BLACKLIST)) { + client->filter_type = NONE; + bt_mesh_gatts_disconnect(client->conn, 0x13); + } + } +} + +int bt_mesh_proxy_gatt_disable(void) +{ + BT_DBG("%s", __func__); + + if (gatt_svc == MESH_GATT_NONE) { + BT_WARN("%s, Already", __func__); + return -EALREADY; + } + + if (gatt_svc != MESH_GATT_PROXY) { + BT_WARN("%s, Busy", __func__); + return -EBUSY; + } + + bt_mesh_proxy_gatt_disconnect(); + + bt_mesh_gatts_service_stop(&proxy_svc); + gatt_svc = MESH_GATT_NONE; + + return 0; +} + +void bt_mesh_proxy_addr_add(struct net_buf_simple *buf, u16_t addr) +{ + struct bt_mesh_proxy_client *client = + CONTAINER_OF(buf, struct bt_mesh_proxy_client, buf); + + BT_DBG("filter_type %u addr 0x%04x", client->filter_type, addr); + + if (client->filter_type == WHITELIST) { + filter_add(client, addr); + } else if (client->filter_type == BLACKLIST) { + filter_remove(client, addr); + } +} + +static bool client_filter_match(struct bt_mesh_proxy_client *client, + u16_t addr) +{ + int i; + + BT_DBG("filter_type %u addr 0x%04x", client->filter_type, addr); + + if (client->filter_type == WHITELIST) { + for (i = 0; i < ARRAY_SIZE(client->filter); i++) { + if (client->filter[i] == addr) { + return true; + } + } + + return false; + } + + if (client->filter_type == BLACKLIST) { + for (i = 0; i < ARRAY_SIZE(client->filter); i++) { + if (client->filter[i] == addr) { + return false; + } + } + + return true; + } + + return false; +} + +bool bt_mesh_proxy_relay(struct net_buf_simple *buf, u16_t dst) +{ + bool relayed = false; + int i; + + BT_DBG("%u bytes to dst 0x%04x", buf->len, dst); + + for (i = 0; i < ARRAY_SIZE(clients); i++) { + struct bt_mesh_proxy_client *client = &clients[i]; + NET_BUF_SIMPLE_DEFINE(msg, 32); + + if (!client->conn) { + continue; + } + + if (!client_filter_match(client, dst)) { + continue; + } + + /* Proxy PDU sending modifies the original buffer, + * so we need to make a copy. + */ + net_buf_simple_reserve(&msg, 1); + net_buf_simple_add_mem(&msg, buf->data, buf->len); + + bt_mesh_proxy_send(client->conn, BLE_MESH_PROXY_NET_PDU, &msg); + relayed = true; + } + + return relayed; +} + +#endif /* CONFIG_BLE_MESH_GATT_PROXY */ + +static int proxy_send(struct bt_mesh_conn *conn, const void *data, u16_t len) +{ + BT_DBG("%u bytes: %s", len, bt_hex(data, len)); + +#if defined(CONFIG_BLE_MESH_GATT_PROXY) + if (gatt_svc == MESH_GATT_PROXY) { + return bt_mesh_gatts_notify(conn, &proxy_attrs[4], data, len); + } +#endif + +#if defined(CONFIG_BLE_MESH_PB_GATT) + if (gatt_svc == MESH_GATT_PROV) { + return bt_mesh_gatts_notify(conn, &prov_attrs[4], data, len); + } +#endif + + return 0; +} + +static int proxy_segment_and_send(struct bt_mesh_conn *conn, u8_t type, + struct net_buf_simple *msg) +{ + u16_t mtu; + + BT_DBG("conn %p type 0x%02x len %u: %s", conn, type, msg->len, + bt_hex(msg->data, msg->len)); + + /* ATT_MTU - OpCode (1 byte) - Handle (2 bytes) */ + mtu = bt_mesh_gatt_get_mtu(conn) - 3; + if (mtu > msg->len) { + net_buf_simple_push_u8(msg, PDU_HDR(SAR_COMPLETE, type)); + return proxy_send(conn, msg->data, msg->len); + } + + net_buf_simple_push_u8(msg, PDU_HDR(SAR_FIRST, type)); + proxy_send(conn, msg->data, mtu); + net_buf_simple_pull(msg, mtu); + + while (msg->len) { + if (msg->len + 1 < mtu) { + net_buf_simple_push_u8(msg, PDU_HDR(SAR_LAST, type)); + proxy_send(conn, msg->data, msg->len); + break; + } + + net_buf_simple_push_u8(msg, PDU_HDR(SAR_CONT, type)); + proxy_send(conn, msg->data, mtu); + net_buf_simple_pull(msg, mtu); + } + + return 0; +} + +int bt_mesh_proxy_send(struct bt_mesh_conn *conn, u8_t type, + struct net_buf_simple *msg) +{ + struct bt_mesh_proxy_client *client = find_client(conn); + + if (!client) { + BT_ERR("%s, No Proxy Client found", __func__); + return -ENOTCONN; + } + + if ((client->filter_type == PROV) != (type == BLE_MESH_PROXY_PROV)) { + BT_ERR("%s, Invalid PDU type for Proxy Client", __func__); + return -EINVAL; + } + + return proxy_segment_and_send(conn, type, msg); +} + +#if defined(CONFIG_BLE_MESH_PB_GATT) +static u8_t prov_svc_data[20] = { 0x27, 0x18, }; + +static const struct bt_mesh_adv_data prov_ad[] = { + BLE_MESH_ADV_DATA_BYTES(BLE_MESH_DATA_FLAGS, (BLE_MESH_AD_GENERAL | BLE_MESH_AD_NO_BREDR)), + BLE_MESH_ADV_DATA_BYTES(BLE_MESH_DATA_UUID16_ALL, 0x27, 0x18), + BLE_MESH_ADV_DATA(BLE_MESH_DATA_SVC_DATA16, prov_svc_data, sizeof(prov_svc_data)), +}; +#endif /* PB_GATT */ + +#if defined(CONFIG_BLE_MESH_GATT_PROXY) + +#define ID_TYPE_NET 0x00 +#define ID_TYPE_NODE 0x01 + +#define NODE_ID_LEN 19 +#define NET_ID_LEN 11 + +#define NODE_ID_TIMEOUT K_SECONDS(CONFIG_BLE_MESH_NODE_ID_TIMEOUT) + +static u8_t proxy_svc_data[NODE_ID_LEN] = { 0x28, 0x18, }; + +static const struct bt_mesh_adv_data node_id_ad[] = { + BLE_MESH_ADV_DATA_BYTES(BLE_MESH_DATA_FLAGS, (BLE_MESH_AD_GENERAL | BLE_MESH_AD_NO_BREDR)), + BLE_MESH_ADV_DATA_BYTES(BLE_MESH_DATA_UUID16_ALL, 0x28, 0x18), + BLE_MESH_ADV_DATA(BLE_MESH_DATA_SVC_DATA16, proxy_svc_data, NODE_ID_LEN), +}; + +static const struct bt_mesh_adv_data net_id_ad[] = { + BLE_MESH_ADV_DATA_BYTES(BLE_MESH_DATA_FLAGS, (BLE_MESH_AD_GENERAL | BLE_MESH_AD_NO_BREDR)), + BLE_MESH_ADV_DATA_BYTES(BLE_MESH_DATA_UUID16_ALL, 0x28, 0x18), + BLE_MESH_ADV_DATA(BLE_MESH_DATA_SVC_DATA16, proxy_svc_data, NET_ID_LEN), +}; + +static int node_id_adv(struct bt_mesh_subnet *sub) +{ + u8_t tmp[16]; + int err; + + BT_DBG("%s", __func__); + + proxy_svc_data[2] = ID_TYPE_NODE; + + err = bt_mesh_rand(proxy_svc_data + 11, 8); + if (err) { + return err; + } + + (void)memset(tmp, 0, 6); + memcpy(tmp + 6, proxy_svc_data + 11, 8); + sys_put_be16(bt_mesh_primary_addr(), tmp + 14); + + err = bt_mesh_encrypt_be(sub->keys[sub->kr_flag].identity, tmp, tmp); + if (err) { + return err; + } + + memcpy(proxy_svc_data + 3, tmp + 8, 8); + + err = bt_le_adv_start(&fast_adv_param, node_id_ad, + ARRAY_SIZE(node_id_ad), NULL, 0); + if (err) { + BT_WARN("Failed to advertise using Node ID (err %d)", err); + return err; + } + + proxy_adv_enabled = true; + + return 0; +} + +static int net_id_adv(struct bt_mesh_subnet *sub) +{ + int err; + + BT_DBG("%s", __func__); + + proxy_svc_data[2] = ID_TYPE_NET; + + BT_DBG("Advertising with NetId %s", + bt_hex(sub->keys[sub->kr_flag].net_id, 8)); + + memcpy(proxy_svc_data + 3, sub->keys[sub->kr_flag].net_id, 8); + + err = bt_le_adv_start(&slow_adv_param, net_id_ad, + ARRAY_SIZE(net_id_ad), NULL, 0); + if (err) { + BT_WARN("Failed to advertise using Network ID (err %d)", err); + return err; + } + + proxy_adv_enabled = true; + + return 0; +} + +static bool advertise_subnet(struct bt_mesh_subnet *sub) +{ + if (sub->net_idx == BLE_MESH_KEY_UNUSED) { + return false; + } + + return (sub->node_id == BLE_MESH_NODE_IDENTITY_RUNNING || + bt_mesh_gatt_proxy_get() == BLE_MESH_GATT_PROXY_ENABLED); +} + +static struct bt_mesh_subnet *next_sub(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + struct bt_mesh_subnet *sub; + + sub = &bt_mesh.sub[(i + next_idx) % ARRAY_SIZE(bt_mesh.sub)]; + if (advertise_subnet(sub)) { + next_idx = (next_idx + 1) % ARRAY_SIZE(bt_mesh.sub); + return sub; + } + } + + return NULL; +} + +static int sub_count(void) +{ + int i, count = 0; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + struct bt_mesh_subnet *sub = &bt_mesh.sub[i]; + + if (advertise_subnet(sub)) { + count++; + } + } + + return count; +} + +static s32_t gatt_proxy_advertise(struct bt_mesh_subnet *sub) +{ + s32_t remaining = K_FOREVER; + int subnet_count; + + BT_DBG("%s", __func__); + + if (conn_count == BLE_MESH_MAX_CONN) { + BT_WARN("Connectable advertising deferred (max connections)"); + return remaining; + } + + if (!sub) { + BT_WARN("No subnets to advertise on"); + return remaining; + } + + if (sub->node_id == BLE_MESH_NODE_IDENTITY_RUNNING) { + u32_t active = k_uptime_get_32() - sub->node_id_start; + + if (active < NODE_ID_TIMEOUT) { + remaining = NODE_ID_TIMEOUT - active; + BT_DBG("Node ID active for %u ms, %d ms remaining", + active, remaining); + node_id_adv(sub); + } else { + bt_mesh_proxy_identity_stop(sub); + BT_DBG("Node ID stopped"); + } + } + + if (sub->node_id == BLE_MESH_NODE_IDENTITY_STOPPED) { + if (bt_mesh_gatt_proxy_get() == BLE_MESH_GATT_PROXY_ENABLED) { + net_id_adv(sub); + } else { + return gatt_proxy_advertise(next_sub()); + } + } + + subnet_count = sub_count(); + BT_DBG("sub_count %u", subnet_count); + if (subnet_count > 1) { + s32_t max_timeout; + + /* We use NODE_ID_TIMEOUT as a starting point since it may + * be less than 60 seconds. Divide this period into at least + * 6 slices, but make sure that a slice is at least one + * second long (to avoid excessive rotation). + */ + max_timeout = NODE_ID_TIMEOUT / MAX(subnet_count, 6); + max_timeout = MAX(max_timeout, K_SECONDS(1)); + + if (remaining > max_timeout || remaining < 0) { + remaining = max_timeout; + } + } + + BT_DBG("Advertising %d ms for net_idx 0x%04x", remaining, sub->net_idx); + + return remaining; +} +#endif /* GATT_PROXY */ + +#if defined(CONFIG_BLE_MESH_PB_GATT) +static size_t gatt_prov_adv_create(struct bt_mesh_adv_data prov_sd[2]) +{ + const struct bt_mesh_prov *prov = bt_mesh_prov_get(); + const char *name = device_name; + size_t name_len = strlen(name); + size_t prov_sd_len = 0; + size_t sd_space = 31; + + memcpy(prov_svc_data + 2, prov->uuid, 16); + sys_put_be16(prov->oob_info, prov_svc_data + 18); + + if (prov->uri) { + size_t uri_len = strlen(prov->uri); + + if (uri_len > 29) { + /* There's no way to shorten an URI */ + BT_WARN("Too long URI to fit advertising packet"); + } else { + prov_sd[0].type = BLE_MESH_DATA_URI; + prov_sd[0].data_len = uri_len; + prov_sd[0].data = (const u8_t *)prov->uri; + sd_space -= 2 + uri_len; + prov_sd_len++; + } + } + + if (sd_space > 2 && name_len > 0) { + sd_space -= 2; + + if (sd_space < name_len) { + prov_sd[prov_sd_len].type = BLE_MESH_DATA_NAME_SHORTENED; + prov_sd[prov_sd_len].data_len = sd_space; + } else { + prov_sd[prov_sd_len].type = BLE_MESH_DATA_NAME_COMPLETE; + prov_sd[prov_sd_len].data_len = name_len; + } + + prov_sd[prov_sd_len].data = (const u8_t *)name; + prov_sd_len++; + } + + return prov_sd_len; +} +#endif /* CONFIG_BLE_MESH_PB_GATT */ + +s32_t bt_mesh_proxy_adv_start(void) +{ + BT_DBG("%s", __func__); + + if (gatt_svc == MESH_GATT_NONE) { + return K_FOREVER; + } + +#if defined(CONFIG_BLE_MESH_PB_GATT) + if (!bt_mesh_is_provisioned()) { + const struct bt_mesh_adv_param *param; + struct bt_mesh_adv_data prov_sd[2]; + size_t prov_sd_len; + + if (prov_fast_adv) { + param = &fast_adv_param; + } else { + param = &slow_adv_param; + } + + prov_sd_len = gatt_prov_adv_create(prov_sd); + + if (bt_le_adv_start(param, prov_ad, ARRAY_SIZE(prov_ad), + prov_sd, prov_sd_len) == 0) { + proxy_adv_enabled = true; + + /* Advertise 60 seconds using fast interval */ + if (prov_fast_adv) { + prov_fast_adv = false; + return K_SECONDS(60); + } + } + } +#endif /* PB_GATT */ + +#if defined(CONFIG_BLE_MESH_GATT_PROXY) + if (bt_mesh_is_provisioned()) { + return gatt_proxy_advertise(next_sub()); + } +#endif /* GATT_PROXY */ + + return K_FOREVER; +} + +void bt_mesh_proxy_adv_stop(void) +{ + int err; + + BT_DBG("adv_enabled %u", proxy_adv_enabled); + + if (!proxy_adv_enabled) { + return; + } + + err = bt_le_adv_stop(); + if (err) { + BT_ERR("%s, Failed to stop advertising (err %d)", __func__, err); + } else { + proxy_adv_enabled = false; + } +} + +static struct bt_mesh_conn_cb conn_callbacks = { + .connected = proxy_connected, + .disconnected = proxy_disconnected, +}; + +static void proxy_recv_timeout(struct k_work *work) +{ + struct bt_mesh_proxy_client *client = NULL; + + BT_DBG("%s", __func__); + + client = CONTAINER_OF(work, struct bt_mesh_proxy_client, sar_timer.work); + if (!client || !client->conn) { + BT_ERR("%s, Invalid proxy client parameter", __func__); + return; + } + + bt_mesh_atomic_clear_bit(client->flags, SAR_TIMER_START); + net_buf_simple_reset(&client->buf); + bt_mesh_gatts_disconnect(client->conn, 0x13); +} + +int bt_mesh_proxy_init(void) +{ + int i; + + /* Initialize the client receive buffers */ + for (i = 0; i < ARRAY_SIZE(clients); i++) { + struct bt_mesh_proxy_client *client = &clients[i]; + + client->buf.size = CLIENT_BUF_SIZE; + client->buf.__buf = client_buf_data + (i * CLIENT_BUF_SIZE); + k_delayed_work_init(&client->sar_timer, proxy_recv_timeout); + } + + bt_mesh_gatts_conn_cb_register(&conn_callbacks); + +#if defined(CONFIG_BLE_MESH_PB_GATT) + const struct bt_mesh_prov *prov = bt_mesh_prov_get(); + __ASSERT(prov && prov->uuid, "%s, Device UUID is not initialized", __func__); +#endif + + return 0; +} + +#endif /* CONFIG_BLE_MESH_NODE */ diff --git a/components/bt/ble_mesh/mesh_core/proxy.h b/components/bt/ble_mesh/mesh_core/proxy.h new file mode 100644 index 0000000000..07120e0950 --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/proxy.h @@ -0,0 +1,51 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _PROXY_H_ +#define _PROXY_H_ + +#include "net.h" +#include "mesh_buf.h" +#include "mesh_bearer_adapt.h" + +#define BLE_MESH_PROXY_NET_PDU 0x00 +#define BLE_MESH_PROXY_BEACON 0x01 +#define BLE_MESH_PROXY_CONFIG 0x02 +#define BLE_MESH_PROXY_PROV 0x03 + +#define DEVICE_NAME_SIZE 29 + +int bt_mesh_set_device_name(const char *name); + +int bt_mesh_proxy_send(struct bt_mesh_conn *conn, u8_t type, + struct net_buf_simple *msg); + +int bt_mesh_proxy_prov_enable(void); +int bt_mesh_proxy_prov_disable(bool disconnect); + +int bt_mesh_proxy_gatt_enable(void); +int bt_mesh_proxy_gatt_disable(void); +void bt_mesh_proxy_gatt_disconnect(void); + +void bt_mesh_proxy_beacon_send(struct bt_mesh_subnet *sub); + +struct net_buf_simple *bt_mesh_proxy_get_buf(void); + +s32_t bt_mesh_proxy_adv_start(void); +void bt_mesh_proxy_adv_stop(void); + +void bt_mesh_proxy_identity_start(struct bt_mesh_subnet *sub); +void bt_mesh_proxy_identity_stop(struct bt_mesh_subnet *sub); + +bool bt_mesh_proxy_relay(struct net_buf_simple *buf, u16_t dst); +void bt_mesh_proxy_addr_add(struct net_buf_simple *buf, u16_t addr); + +int bt_mesh_proxy_init(void); + +#endif /* _PROXY_H_ */ diff --git a/components/bt/ble_mesh/mesh_core/settings.c b/components/bt/ble_mesh/mesh_core/settings.c new file mode 100644 index 0000000000..6606142c2b --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/settings.c @@ -0,0 +1,1578 @@ +/* + * Copyright (c) 2018 Intel Corporation + * Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include "sdkconfig.h" +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BLE_MESH_DEBUG_SETTINGS) + +#include "mesh_types.h" +#include "mesh_util.h" +#include "mesh_access.h" +#include "mesh_main.h" +#include "mesh_buf.h" +#include "mesh_kernel.h" +#include "mesh_trace.h" +#include "mesh_common.h" + +#include "mesh.h" +#include "net.h" +#include "crypto.h" +#include "transport.h" +#include "access.h" +#include "foundation.h" +#include "proxy.h" +#include "cfg_srv.h" + +#include "settings_nvs.h" + +/* BLE Mesh NVS Key and corresponding data struct. + * Note: The length of nvs key must be <= 15. + * "xxxx" (2 octet) means the rpl_src, net_idx, app_idx, model_key, etc. + * Model model_key is a combination "elem_idx << 8 | model_idx". + * key: "mesh/net" -> write/read to set/get NET data + * key: "mesh/iv" -> write/read to set/get IV data + * key: "mesh/seq" -> write/read to set/get SEQ data + * key: "mesh/hb_pub" -> write/read to set/get CFG HB_PUB data + * key: "mesh/cfg" -> write/read to set/get CFG data + * key: "mesh/rpl" -> write/read to set/get all RPL src. + * key: "mesh/rpl/xxxx" -> write/read to set/get the "xxxx" RPL data + * key: "mesh/netkey" -> write/read to set/get all NetKey Indexes + * key: "mesh/nk/xxxx" -> write/read to set/get the "xxxx" NetKey data + * key: "mesh/appkey" -> write/read to set/get all AppKey Indexes + * key: "mesh/ak/xxxx" -> write/read to set/get the "xxxx" AppKey data + * key: "mesh/sig" -> write/read to set/get all SIG MODEL model_keys. + * key: "mesh/s/xxxx/b" -> write/read to set/get SIG MODEL Bind AppKey List + * key: "mesh/s/xxxx/s" -> write/read to set/get SIG MODEL Subscription List + * key: "mesh/s/xxxx/p" -> write/read to set/get SIG MODEL Publication + * key: "mesh/vnd" -> write/read to set/get all VENDOR MODEL model_keys. + * key: "mesh/v/xxxx/b" -> write/read to set/get VENDOR MODEL Bind AppKey List + * key: "mesh/v/xxxx/s" -> write/read to set/get VENDOR MODEL Subscription List + * key: "mesh/v/xxxx/p" -> write/read to set/get VENDOR MODEL Publication + */ + +#if CONFIG_BLE_MESH_SETTINGS + +#define GET_ELEMENT_IDX(x) ((u8_t)((x) >> 8)) +#define GET_MODEL_IDX(x) ((u8_t)(x)) +#define GET_MODEL_KEY(a, b) ((u16_t)(((u16_t)((a) << 8)) | b)) + +/* Tracking of what storage changes are pending for App and Net Keys. We + * track this in a separate array here instead of within the respective + * bt_mesh_app_key and bt_mesh_subnet structs themselves, since once a key + * gets deleted its struct becomes invalid and may be reused for other keys. + */ +static struct key_update { + u16_t key_idx: 12, /* AppKey or NetKey Index */ + valid: 1, /* 1 if this entry is valid, 0 if not */ + app_key: 1, /* 1 if this is an AppKey, 0 if a NetKey */ + clear: 1; /* 1 if key needs clearing, 0 if storing */ +} key_updates[CONFIG_BLE_MESH_APP_KEY_COUNT + CONFIG_BLE_MESH_SUBNET_COUNT]; + +static struct k_delayed_work pending_store; + +/* Mesh network storage information */ +struct net_val { + u16_t primary_addr; + u8_t dev_key[16]; +} __packed; + +/* Sequence number storage */ +struct seq_val { + u8_t val[3]; +} __packed; + +/* Heartbeat Publication storage */ +struct hb_pub_val { + u16_t dst; + u8_t period; + u8_t ttl; + u16_t feat; + u16_t net_idx: 12, + indefinite: 1; +}; + +/* Miscelaneous configuration server model states */ +struct cfg_val { + u8_t net_transmit; + u8_t relay; + u8_t relay_retransmit; + u8_t beacon; + u8_t gatt_proxy; + u8_t frnd; + u8_t default_ttl; +}; + +/* IV Index & IV Update storage */ +struct iv_val { + u32_t iv_index; + u8_t iv_update: 1, + iv_duration: 7; +} __packed; + +/* Replay Protection List storage */ +struct rpl_val { + u32_t seq: 24, + old_iv: 1; +}; + +/* NetKey storage information */ +struct net_key_val { + u8_t kr_flag: 1, + kr_phase: 7; + u8_t val[2][16]; +} __packed; + +/* AppKey storage information */ +struct app_key_val { + u16_t net_idx; + bool updated; + u8_t val[2][16]; +} __packed; + +struct mod_pub_val { + u16_t addr; + u16_t key; + u8_t ttl; + u8_t retransmit; + u8_t period; + u8_t period_div: 4, + cred: 1; +}; + +/* We need this so we don't overwrite app-hardcoded values in case FCB + * contains a history of changes but then has a NULL at the end. + */ +static struct { + bool valid; + struct cfg_val cfg; +} stored_cfg; + +static int net_set(const char *name) +{ + struct net_val net = {0}; + bool exist; + int err; + + BT_DBG("%s", __func__); + + err = bt_mesh_load_core_settings(name, (u8_t *)&net, sizeof(net), &exist); + if (err) { + BT_WARN("%s, Clear NET", __func__); + memset(bt_mesh.dev_key, 0, sizeof(bt_mesh.dev_key)); + bt_mesh_comp_unprovision(); + return 0; + } + + if (exist == false) { + return 0; + } + + memcpy(bt_mesh.dev_key, net.dev_key, sizeof(bt_mesh.dev_key)); + bt_mesh_comp_provision(net.primary_addr); + + BT_DBG("Provisioned with primary address 0x%04x", net.primary_addr); + BT_DBG("Recovered DevKey %s", bt_hex(bt_mesh.dev_key, 16)); + + return 0; +} + +static int iv_set(const char *name) +{ + struct iv_val iv = {0}; + bool exist; + int err; + + BT_DBG("%s", __func__); + + err = bt_mesh_load_core_settings(name, (u8_t *)&iv, sizeof(iv), &exist); + if (err) { + BT_WARN("%s, Clear IV", __func__); + bt_mesh.iv_index = 0U; + bt_mesh_atomic_clear_bit(bt_mesh.flags, BLE_MESH_IVU_IN_PROGRESS); + return 0; + } + + if (exist == false) { + return 0; + } + + bt_mesh.iv_index = iv.iv_index; + bt_mesh_atomic_set_bit_to(bt_mesh.flags, BLE_MESH_IVU_IN_PROGRESS, iv.iv_update); + bt_mesh.ivu_duration = iv.iv_duration; + + BT_DBG("IV Index 0x%04x (IV Update Flag %u) duration %u hours", + iv.iv_index, iv.iv_update, iv.iv_duration); + + return 0; +} + +static int seq_set(const char *name) +{ + struct seq_val seq = {0}; + bool exist; + int err; + + BT_DBG("%s", __func__); + + err = bt_mesh_load_core_settings(name, (u8_t *)&seq, sizeof(seq), &exist); + if (err) { + BT_WARN("%s, Clear SEQ", __func__); + bt_mesh.seq = 0U; + return 0; + } + + if (exist == false) { + return 0; + } + + bt_mesh.seq = ((u32_t)seq.val[0] | ((u32_t)seq.val[1] << 8) | + ((u32_t)seq.val[2] << 16)); + + if (CONFIG_BLE_MESH_SEQ_STORE_RATE > 0) { + /* Make sure we have a large enough sequence number. We + * subtract 1 so that the first transmission causes a write + * to the settings storage. + */ + bt_mesh.seq += (CONFIG_BLE_MESH_SEQ_STORE_RATE - + (bt_mesh.seq % CONFIG_BLE_MESH_SEQ_STORE_RATE)); + bt_mesh.seq--; + } + + BT_DBG("Sequence Number 0x%06x", bt_mesh.seq); + + return 0; +} + +static struct bt_mesh_rpl *rpl_find(u16_t src) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.rpl); i++) { + if (bt_mesh.rpl[i].src == src) { + return &bt_mesh.rpl[i]; + } + } + + return NULL; +} + +static struct bt_mesh_rpl *rpl_alloc(u16_t src) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.rpl); i++) { + if (bt_mesh.rpl[i].src == BLE_MESH_ADDR_UNASSIGNED) { + bt_mesh.rpl[i].src = src; + return &bt_mesh.rpl[i]; + } + } + + return NULL; +} + +static int rpl_set(const char *name) +{ + struct net_buf_simple *buf = NULL; + struct bt_mesh_rpl *entry = NULL; + struct rpl_val rpl = {0}; + char get[16] = {'\0'}; + size_t length; + bool exist; + int err = 0; + int i; + + BT_DBG("%s", __func__); + + buf = bt_mesh_get_core_settings_item(name); + if (!buf) { + return 0; + } + + length = buf->len; + + for (i = 0; i < length / SETTINGS_ITEM_SIZE; i++) { + u16_t src = net_buf_simple_pull_le16(buf); + sprintf(get, "mesh/rpl/%04x", src); + + err = bt_mesh_load_core_settings(get, (u8_t *)&rpl, sizeof(rpl), &exist); + if (err) { + BT_ERR("%s, Failed to load RPL %s, reset RPL", __func__, get); + bt_mesh_rpl_reset(); + goto free; + } + + if (exist == false) { + continue; + } + + entry = rpl_find(src); + if (!entry) { + entry = rpl_alloc(src); + if (!entry) { + BT_ERR("%s, No space for a new RPL 0x%04x", __func__, src); + err = -ENOMEM; + goto free; + } + } + + BT_DBG("RPL 0x%04x: Seq 0x%06x, old_iv %u", src, rpl.seq, rpl.old_iv); + entry->src = src; + entry->seq = rpl.seq; + entry->old_iv = rpl.old_iv; + } + +free: + bt_mesh_free_buf(buf); + return err; +} + +static struct bt_mesh_subnet *subnet_alloc(u16_t net_idx) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + if (bt_mesh.sub[i].net_idx == BLE_MESH_KEY_UNUSED) { + bt_mesh.sub[i].net_idx = net_idx; + return &bt_mesh.sub[i]; + } + } + + return NULL; +} + +static int net_key_set(const char *name) +{ + struct net_buf_simple *buf = NULL; + struct bt_mesh_subnet *sub = NULL; + struct net_key_val key = {0}; + char get[16] = {'\0'}; + size_t length; + bool exist; + int err = 0; + int i; + + BT_DBG("%s", __func__); + + buf = bt_mesh_get_core_settings_item(name); + if (!buf) { + return 0; + } + + length = buf->len; + + for (i = 0; i < length / SETTINGS_ITEM_SIZE; i++) { + u16_t net_idx = net_buf_simple_pull_le16(buf); + sprintf(get, "mesh/nk/%04x", net_idx); + + err = bt_mesh_load_core_settings(get, (u8_t *)&key, sizeof(key), &exist); + if (err) { + BT_ERR("%s, Failed to load NetKey %s", __func__, get); + goto free; + } + + if (exist == false) { + continue; + } + + sub = bt_mesh_subnet_get(net_idx); + if (!sub) { + sub = subnet_alloc(net_idx); + if (!sub) { + BT_ERR("%s, No space for a new subnet 0x%03x", __func__, net_idx); + err = -ENOMEM; + goto free; + } + } + + BT_DBG("NetKeyIndex 0x%03x recovered from storage", net_idx); + sub->net_idx = net_idx; + sub->kr_flag = key.kr_flag; + sub->kr_phase = key.kr_phase; + memcpy(sub->keys[0].net, &key.val[0], 16); + memcpy(sub->keys[1].net, &key.val[1], 16); + } + +free: + bt_mesh_free_buf(buf); + return err; +} + +static int app_key_set(const char *name) +{ + struct bt_mesh_app_key *app = NULL; + struct bt_mesh_subnet *sub = NULL; + struct net_buf_simple *buf = NULL; + struct app_key_val key = {0}; + char get[16] = {'\0'}; + size_t length; + bool exist; + int err = 0; + int i; + + BT_DBG("%s", __func__); + + buf = bt_mesh_get_core_settings_item(name); + if (!buf) { + return 0; + } + + length = buf->len; + + for (i = 0; i < length / SETTINGS_ITEM_SIZE; i++) { + u16_t app_idx = net_buf_simple_pull_le16(buf); + sprintf(get, "mesh/ak/%04x", app_idx); + + err = bt_mesh_load_core_settings(get, (u8_t *)&key, sizeof(key), &exist); + if (err) { + BT_ERR("%s, Failed to load AppKey %s", __func__, get); + goto free; + } + + if (exist == false) { + continue; + } + + sub = bt_mesh_subnet_get(key.net_idx); + if (!sub) { + BT_ERR("%s, Failed to find subnet 0x%03x", __func__, key.net_idx); + err = -ENOENT; + goto free; + } + + app = bt_mesh_app_key_find(app_idx); + if (!app) { + app = bt_mesh_app_key_alloc(app_idx); + if (!app) { + BT_ERR("%s, No space for a new app key 0x%03x", __func__, app_idx); + err = -ENOMEM; + goto free; + } + } + + BT_DBG("AppKeyIndex 0x%03x recovered from storage", app_idx); + app->net_idx = key.net_idx; + app->app_idx = app_idx; + app->updated = key.updated; + memcpy(app->keys[0].val, key.val[0], 16); + memcpy(app->keys[1].val, key.val[1], 16); + bt_mesh_app_id(app->keys[0].val, &app->keys[0].id); + bt_mesh_app_id(app->keys[1].val, &app->keys[1].id); + } + +free: + bt_mesh_free_buf(buf); + return err; +} + +static int hb_pub_set(const char *name) +{ + struct bt_mesh_hb_pub *hb_pub = bt_mesh_hb_pub_get(); + struct hb_pub_val hb_val = {0}; + bool exist; + int err; + + BT_DBG("%s", __func__); + + if (!hb_pub) { + BT_ERR("%s, NULL cfg hb pub", __func__); + return -EINVAL; + } + + err = bt_mesh_load_core_settings(name, (u8_t *)&hb_val, sizeof(hb_val), &exist); + if (err) { + BT_WARN("%s, Cleared heartbeat publication", __func__); + hb_pub->dst = BLE_MESH_ADDR_UNASSIGNED; + hb_pub->count = 0U; + hb_pub->ttl = 0U; + hb_pub->period = 0U; + hb_pub->feat = 0U; + return 0; + } + + if (exist == false) { + return 0; + } + + hb_pub->dst = hb_val.dst; + hb_pub->period = hb_val.period; + hb_pub->ttl = hb_val.ttl; + hb_pub->feat = hb_val.feat; + hb_pub->net_idx = hb_val.net_idx; + if (hb_val.indefinite) { + hb_pub->count = 0xffff; + } else { + hb_pub->count = 0U; + } + + BT_DBG("Restore Heartbeat Publication"); + + return 0; +} + +static int cfg_set(const char *name) +{ + struct bt_mesh_cfg_srv *cfg = bt_mesh_cfg_get(); + struct cfg_val val = {0}; + bool exist; + int err; + + BT_DBG("%s", __func__); + + if (!cfg) { + BT_ERR("%s, NULL cfg", __func__); + stored_cfg.valid = false; + return -EINVAL; + } + + err = bt_mesh_load_core_settings(name, (u8_t *)&val, sizeof(val), &exist); + if (err) { + BT_WARN("%s, Cleared configuration", __func__); + stored_cfg.valid = false; + return 0; + } + + if (exist == false) { + return 0; + } + + stored_cfg.valid = true; + BT_DBG("Restore configuration state"); + return 0; +} + +static int model_set_bind(bool vnd, struct bt_mesh_model *model, u16_t model_key) +{ + char name[16] = {'\0'}; + bool exist; + int i, err; + + /* Start with empty array regardless of cleared or set value */ + for (i = 0; i < ARRAY_SIZE(model->keys); i++) { + model->keys[i] = BLE_MESH_KEY_UNUSED; + } + + sprintf(name, "mesh/%s/%04x/b", vnd ? "v" : "s", model_key); + err = bt_mesh_load_core_settings(name, (u8_t *)model->keys, sizeof(model->keys), &exist); + if (err) { + BT_ERR("%s, Failed to get model bind keys", __func__); + return -EIO; + } + + return 0; +} + +static int model_set_sub(bool vnd, struct bt_mesh_model *model, u16_t model_key) +{ + char name[16] = {'\0'}; + bool exist; + int i, err; + + /* Start with empty array regardless of cleared or set value */ + for (i = 0; i < ARRAY_SIZE(model->groups); i++) { + model->groups[i] = BLE_MESH_ADDR_UNASSIGNED; + } + + sprintf(name, "mesh/%s/%04x/s", vnd ? "v" : "s", model_key); + err = bt_mesh_load_core_settings(name, (u8_t *)model->groups, sizeof(model->groups), &exist); + if (err) { + BT_ERR("%s, Failed to get model subscriptions", __func__); + return -EIO; + } + + return 0; +} + +static int model_set_pub(bool vnd, struct bt_mesh_model *model, u16_t model_key) +{ + struct mod_pub_val pub = {0}; + char name[16] = {'\0'}; + bool exist; + int err; + + if (!model->pub) { + BT_WARN("%s, Model has no publication context", __func__); + return 0; + } + + sprintf(name, "mesh/%s/%04x/p", vnd ? "v" : "s", model_key); + err = bt_mesh_load_core_settings(name, (u8_t *)&pub, sizeof(pub), &exist); + if (err) { + BT_WARN("%s, Cleared model publication", __func__); + model->pub->addr = BLE_MESH_ADDR_UNASSIGNED; + model->pub->key = 0U; + model->pub->cred = 0U; + model->pub->ttl = 0U; + model->pub->period = 0U; + model->pub->retransmit = 0U; + model->pub->count = 0U; + return 0; + } + + if (exist == false) { + return 0; + } + + model->pub->addr = pub.addr; + model->pub->key = pub.key; + model->pub->cred = pub.cred; + model->pub->ttl = pub.ttl; + model->pub->period = pub.period; + model->pub->retransmit = pub.retransmit; + model->pub->count = 0U; + + BT_DBG("Restore model publication, pub_addr 0x%04x app_idx 0x%03x", + pub.addr, pub.key); + + return 0; +} + +static int model_set(bool vnd, const char *name) +{ + struct bt_mesh_model *model = NULL; + struct net_buf_simple *buf = NULL; + u8_t elem_idx, model_idx; + size_t length; + int err = 0; + int i; + + BT_DBG("%s", __func__); + + buf = bt_mesh_get_core_settings_item(name); + if (!buf) { + return 0; + } + + length = buf->len; + + for (i = 0; i < length / SETTINGS_ITEM_SIZE; i++) { + u16_t model_key = net_buf_simple_pull_le16(buf); + elem_idx = GET_ELEMENT_IDX(model_key); + model_idx = GET_MODEL_IDX(model_key); + + model = bt_mesh_model_get(vnd, elem_idx, model_idx); + if (!model) { + BT_ERR("%s, Failed to get %s model, elem_idx %u model_idx %u", + __func__, vnd ? "vnd" : "sig", elem_idx, model_idx); + err = -ENOENT; + goto free; + } + + err = model_set_bind(vnd, model, model_key); + if (err) { + BT_ERR("%s, model_set_bind fail", __func__); + goto free; + } + + err = model_set_sub(vnd, model, model_key); + if (err) { + BT_ERR("%s, model_set_sub fail", __func__); + goto free; + } + + err = model_set_pub(vnd, model, model_key); + if (err) { + BT_ERR("%s, model_set_pub fail", __func__); + goto free; + } + } + +free: + bt_mesh_free_buf(buf); + return err; +} + +static int sig_mod_set(const char *name) +{ + return model_set(false, name); +} + +static int vnd_mod_set(const char *name) +{ + return model_set(true, name); +} + +const struct bt_mesh_setting { + const char *name; + int (*func)(const char *name); +} settings[] = { + { "mesh/net", net_set }, + { "mesh/iv", iv_set }, + { "mesh/seq", seq_set }, + { "mesh/rpl", rpl_set }, + { "mesh/netkey", net_key_set }, + { "mesh/appkey", app_key_set }, + { "mesh/hb_pub", hb_pub_set }, + { "mesh/cfg", cfg_set }, + { "mesh/sig", sig_mod_set }, + { "mesh/vnd", vnd_mod_set }, +}; + +int settings_core_load(void) +{ + int i; + + BT_DBG("%s", __func__); + + for (i = 0; i < ARRAY_SIZE(settings); i++) { + settings[i].func(settings[i].name); + } + + return 0; +} + +static int subnet_init(struct bt_mesh_subnet *sub) +{ + int err; + + err = bt_mesh_net_keys_create(&sub->keys[0], sub->keys[0].net); + if (err) { + BT_ERR("%s, Unable to generate keys for subnet", __func__); + return -EIO; + } + + if (sub->kr_phase != BLE_MESH_KR_NORMAL) { + err = bt_mesh_net_keys_create(&sub->keys[1], sub->keys[1].net); + if (err) { + BT_ERR("%s, Unable to generate keys for subnet", __func__); + (void)memset(&sub->keys[0], 0, sizeof(sub->keys[0])); + return -EIO; + } + } + + if (IS_ENABLED(CONFIG_BLE_MESH_GATT_PROXY)) { + sub->node_id = BLE_MESH_NODE_IDENTITY_STOPPED; + } else { + sub->node_id = BLE_MESH_NODE_IDENTITY_NOT_SUPPORTED; + } + + /* Make sure we have valid beacon data to be sent */ + bt_mesh_net_beacon_update(sub); + + return 0; +} + +static void commit_model(struct bt_mesh_model *model, struct bt_mesh_elem *elem, + bool vnd, bool primary, void *user_data) +{ + if (model->pub && model->pub->update && + model->pub->addr != BLE_MESH_ADDR_UNASSIGNED) { + s32_t ms = bt_mesh_model_pub_period_get(model); + if (ms) { + BT_DBG("Starting publish timer (period %u ms)", ms); + k_delayed_work_submit(&model->pub->timer, ms); + } + } +} + +int settings_core_commit(void) +{ + struct bt_mesh_hb_pub *hb_pub = NULL; + struct bt_mesh_cfg_srv *cfg = NULL; + int i; + + BT_DBG("sub[0].net_idx 0x%03x", bt_mesh.sub[0].net_idx); + + if (bt_mesh.sub[0].net_idx == BLE_MESH_KEY_UNUSED) { + /* Nothing to do since we're not yet provisioned */ + return 0; + } + + if (IS_ENABLED(CONFIG_BLE_MESH_PB_GATT)) { +#if defined(CONFIG_BLE_MESH_NODE) + bt_mesh_proxy_prov_disable(true); +#endif + } + + for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) { + struct bt_mesh_subnet *sub = &bt_mesh.sub[i]; + int err; + + if (sub->net_idx == BLE_MESH_KEY_UNUSED) { + continue; + } + + err = subnet_init(sub); + if (err) { + BT_ERR("%s, Failed to init subnet 0x%03x", __func__, sub->net_idx); + } + } + + if (bt_mesh.ivu_duration < BLE_MESH_IVU_MIN_HOURS) { + k_delayed_work_submit(&bt_mesh.ivu_timer, BLE_MESH_IVU_TIMEOUT); + } + + bt_mesh_model_foreach(commit_model, NULL); + + hb_pub = bt_mesh_hb_pub_get(); + if (hb_pub && hb_pub->dst != BLE_MESH_ADDR_UNASSIGNED && + hb_pub->count && hb_pub->period) { + BT_DBG("Starting heartbeat publication"); + k_work_submit(&hb_pub->timer.work); + } + + cfg = bt_mesh_cfg_get(); + if (cfg && stored_cfg.valid) { + cfg->net_transmit = stored_cfg.cfg.net_transmit; + cfg->relay = stored_cfg.cfg.relay; + cfg->relay_retransmit = stored_cfg.cfg.relay_retransmit; + cfg->beacon = stored_cfg.cfg.beacon; + cfg->gatt_proxy = stored_cfg.cfg.gatt_proxy; + cfg->frnd = stored_cfg.cfg.frnd; + cfg->default_ttl = stored_cfg.cfg.default_ttl; + } + + bt_mesh_atomic_set_bit(bt_mesh.flags, BLE_MESH_VALID); + +#if defined(CONFIG_BLE_MESH_NODE) + bt_mesh_net_start(); +#endif + + return 0; +} + +static void schedule_store(int flag) +{ + s32_t timeout; + + bt_mesh_atomic_set_bit(bt_mesh.flags, flag); + + if (bt_mesh_atomic_test_bit(bt_mesh.flags, BLE_MESH_NET_PENDING) || + bt_mesh_atomic_test_bit(bt_mesh.flags, BLE_MESH_IV_PENDING) || + bt_mesh_atomic_test_bit(bt_mesh.flags, BLE_MESH_SEQ_PENDING)) { + timeout = K_NO_WAIT; + } else if (bt_mesh_atomic_test_bit(bt_mesh.flags, BLE_MESH_RPL_PENDING) && + (CONFIG_BLE_MESH_RPL_STORE_TIMEOUT < + CONFIG_BLE_MESH_STORE_TIMEOUT)) { + timeout = K_SECONDS(CONFIG_BLE_MESH_RPL_STORE_TIMEOUT); + } else { + timeout = K_SECONDS(CONFIG_BLE_MESH_STORE_TIMEOUT); + } + + BT_DBG("Waiting %d seconds", timeout / MSEC_PER_SEC); + + if (timeout) { + k_delayed_work_submit(&pending_store, timeout); + } else { + k_work_submit(&pending_store.work); + } +} + +static void clear_iv(void) +{ + BT_DBG("Clearing IV"); + bt_mesh_save_core_settings("mesh/iv", NULL, 0); +} + +static void clear_net(void) +{ + BT_DBG("Clearing Network"); + bt_mesh_save_core_settings("mesh/net", NULL, 0); +} + +static void store_pending_net(void) +{ + struct net_val net = {0}; + + BT_DBG("addr 0x%04x DevKey %s", bt_mesh_primary_addr(), + bt_hex(bt_mesh.dev_key, 16)); + + net.primary_addr = bt_mesh_primary_addr(); + memcpy(net.dev_key, bt_mesh.dev_key, 16); + + bt_mesh_save_core_settings("mesh/net", (const u8_t *)&net, sizeof(net)); +} + +void bt_mesh_store_net(void) +{ + schedule_store(BLE_MESH_NET_PENDING); +} + +static void store_pending_iv(void) +{ + struct iv_val iv = {0}; + + iv.iv_index = bt_mesh.iv_index; + iv.iv_update = bt_mesh_atomic_test_bit(bt_mesh.flags, BLE_MESH_IVU_IN_PROGRESS); + iv.iv_duration = bt_mesh.ivu_duration; + + bt_mesh_save_core_settings("mesh/iv", (const u8_t *)&iv, sizeof(iv)); +} + +void bt_mesh_store_iv(bool only_duration) +{ + schedule_store(BLE_MESH_IV_PENDING); + + if (!only_duration) { + /* Always update Seq whenever IV changes */ + schedule_store(BLE_MESH_SEQ_PENDING); + } +} + +static void store_pending_seq(void) +{ + struct seq_val seq = {0}; + + seq.val[0] = bt_mesh.seq; + seq.val[1] = bt_mesh.seq >> 8; + seq.val[2] = bt_mesh.seq >> 16; + + bt_mesh_save_core_settings("mesh/seq", (const u8_t *)&seq, sizeof(seq)); +} + +void bt_mesh_store_seq(void) +{ + if (CONFIG_BLE_MESH_SEQ_STORE_RATE && + (bt_mesh.seq % CONFIG_BLE_MESH_SEQ_STORE_RATE)) { + return; + } + + schedule_store(BLE_MESH_SEQ_PENDING); +} + +static void store_rpl(struct bt_mesh_rpl *entry) +{ + struct rpl_val rpl = {0}; + char name[16] = {'\0'}; + int err; + + BT_DBG("src 0x%04x seq 0x%06x old_iv %u", entry->src, entry->seq, entry->old_iv); + + rpl.seq = entry->seq; + rpl.old_iv = entry->old_iv; + + sprintf(name, "mesh/rpl/%04x", entry->src); + err = bt_mesh_save_core_settings(name, (const u8_t *)&rpl, sizeof(rpl)); + if (err) { + BT_ERR("%s, Failed to save RPL %s", __func__, name); + return; + } + + err = bt_mesh_add_core_settings_item("mesh/rpl", entry->src); + if (err) { + BT_ERR("%s, Failed to add 0x%04x to mesh/rpl", __func__, entry->src); + } + + return; +} + +static void clear_rpl(void) +{ + struct net_buf_simple *buf = NULL; + char name[16] = {'\0'}; + size_t length; + u16_t src; + int i; + + BT_DBG("%s", __func__); + + bt_mesh_rpl_clear(); + + buf = bt_mesh_get_core_settings_item("mesh/rpl"); + if (!buf) { + BT_WARN("%s, Erase RPL", __func__); + bt_mesh_save_core_settings("mesh/rpl", NULL, 0); + return; + } + + length = buf->len; + + for (i = 0; i < length / SETTINGS_ITEM_SIZE; i++) { + src = net_buf_simple_pull_le16(buf); + sprintf(name, "mesh/rpl/%04x", src); + bt_mesh_save_core_settings(name, NULL, 0); + } + + bt_mesh_save_core_settings("mesh/rpl", NULL, 0); + + bt_mesh_free_buf(buf); + return; +} + +static void store_pending_rpl(void) +{ + int i; + + BT_DBG("%s", __func__); + + for (i = 0; i < ARRAY_SIZE(bt_mesh.rpl); i++) { + struct bt_mesh_rpl *rpl = &bt_mesh.rpl[i]; + + if (rpl->store) { + rpl->store = false; + store_rpl(rpl); + } + } +} + +static void store_pending_hb_pub(void) +{ + struct bt_mesh_hb_pub *hb_pub = bt_mesh_hb_pub_get(); + struct hb_pub_val val = {0}; + + if (!hb_pub) { + BT_WARN("%s, NULL hb_pub", __func__); + return; + } + + val.indefinite = (hb_pub->count = 0xffff); + val.dst = hb_pub->dst; + val.period = hb_pub->period; + val.ttl = hb_pub->ttl; + val.feat = hb_pub->feat; + val.net_idx = hb_pub->net_idx; + + bt_mesh_save_core_settings("mesh/hb_pub", (const u8_t *)&val, sizeof(val)); +} + +static void store_pending_cfg(void) +{ + struct bt_mesh_cfg_srv *cfg = bt_mesh_cfg_get(); + struct cfg_val val = {0}; + + if (!cfg) { + BT_WARN("%s, NULL cfg", __func__); + return; + } + + val.net_transmit = cfg->net_transmit; + val.relay = cfg->relay; + val.relay_retransmit = cfg->relay_retransmit; + val.beacon = cfg->beacon; + val.gatt_proxy = cfg->gatt_proxy; + val.frnd = cfg->frnd; + val.default_ttl = cfg->default_ttl; + + bt_mesh_save_core_settings("mesh/cfg", (const u8_t *)&val, sizeof(val)); +} + +static void clear_cfg(void) +{ + BT_DBG("Clearing configuration"); + bt_mesh_save_core_settings("mesh/cfg", NULL, 0); +} + +static void clear_app_key(u16_t app_idx) +{ + char name[16] = {'\0'}; + int err; + + BT_DBG("AppKeyIndex 0x%03x", app_idx); + + sprintf(name, "mesh/ak/%04x", app_idx); + bt_mesh_save_core_settings(name, NULL, 0); + + err = bt_mesh_remove_core_settings_item("mesh/appkey", app_idx); + if (err) { + BT_ERR("%s, Failed to remove 0x%04x from mesh/appkey", __func__, app_idx); + } + + return; +} + +static void clear_net_key(u16_t net_idx) +{ + char name[16] = {'\0'}; + int err; + + BT_DBG("NetKeyIndex 0x%03x", net_idx); + + sprintf(name, "mesh/nk/%04x", net_idx); + bt_mesh_save_core_settings(name, NULL, 0); + + err = bt_mesh_remove_core_settings_item("mesh/netkey", net_idx); + if (err) { + BT_ERR("%s, Failed to remove 0x%04x from mesh/netkey", __func__, net_idx); + } + + return; +} + +static void store_net_key(struct bt_mesh_subnet *sub) +{ + struct net_key_val key = {0}; + char name[16] = {'\0'}; + int err; + + BT_DBG("NetKeyIndex 0x%03x NetKey %s", sub->net_idx, + bt_hex(sub->keys[0].net, 16)); + + memcpy(&key.val[0], sub->keys[0].net, 16); + memcpy(&key.val[1], sub->keys[1].net, 16); + key.kr_flag = sub->kr_flag; + key.kr_phase = sub->kr_phase; + + sprintf(name, "mesh/nk/%04x", sub->net_idx); + err = bt_mesh_save_core_settings(name, (const u8_t *)&key, sizeof(key)); + if (err) { + BT_ERR("%s, Failed to save NetKey %s", __func__, name); + return; + } + + err = bt_mesh_add_core_settings_item("mesh/netkey", sub->net_idx); + if (err) { + BT_ERR("%s, Failed to add 0x%04x to mesh/netkey", __func__, sub->net_idx); + } + + return; +} + +static void store_app_key(struct bt_mesh_app_key *app) +{ + struct app_key_val key = {0}; + char name[16] = {'\0'}; + int err; + + key.net_idx = app->net_idx; + key.updated = app->updated; + memcpy(key.val[0], app->keys[0].val, 16); + memcpy(key.val[1], app->keys[1].val, 16); + + sprintf(name, "mesh/ak/%04x", app->app_idx); + err = bt_mesh_save_core_settings(name, (const u8_t *)&key, sizeof(key)); + if (err) { + BT_ERR("%s, Failed to save AppKey %s", __func__, name); + return; + } + + err = bt_mesh_add_core_settings_item("mesh/appkey", app->app_idx); + if (err) { + BT_ERR("%s, Failed to add 0x%04x to mesh/appkey", __func__, app->app_idx); + } + + return; +} + +static void store_pending_keys(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(key_updates); i++) { + struct key_update *update = &key_updates[i]; + + if (!update->valid) { + continue; + } + + if (update->clear) { + if (update->app_key) { + clear_app_key(update->key_idx); + } else { + clear_net_key(update->key_idx); + } + } else { + if (update->app_key) { + struct bt_mesh_app_key *key = NULL; + key = bt_mesh_app_key_find(update->key_idx); + if (key) { + store_app_key(key); + } else { + BT_WARN("AppKeyIndex 0x%03x not found", update->key_idx); + } + } else { + struct bt_mesh_subnet *sub = NULL; + sub = bt_mesh_subnet_get(update->key_idx); + if (sub) { + store_net_key(sub); + } else { + BT_WARN("NetKeyIndex 0x%03x not found", update->key_idx); + } + } + } + + update->valid = 0U; + } +} + +static void store_pending_mod_bind(struct bt_mesh_model *model, bool vnd) +{ + char name[16] = {'\0'}; + u16_t model_key; + int err; + + model_key = GET_MODEL_KEY(model->elem_idx, model->model_idx); + + sprintf(name, "mesh/%s/%04x/b", vnd ? "v" : "s", model_key); + + if (!bt_mesh_atomic_test_bit(bt_mesh.flags, BLE_MESH_VALID)) { + bt_mesh_save_core_settings(name, NULL, 0); + return; + } + + err = bt_mesh_save_core_settings(name, (const u8_t *)model->keys, sizeof(model->keys)); + if (err) { + BT_ERR("%s, Failed to save %s", __func__, name); + return; + } + + err = bt_mesh_add_core_settings_item(vnd ? "mesh/vnd" : "mesh/sig", model_key); + if (err) { + BT_ERR("%s, Failed to add 0x%04x to %s", __func__, model_key, + vnd ? "mesh/vnd" : "mesh/sig"); + } + + return; +} + +static void store_pending_mod_sub(struct bt_mesh_model *model, bool vnd) +{ + char name[16] = {'\0'}; + u16_t model_key; + int err; + + model_key = GET_MODEL_KEY(model->elem_idx, model->model_idx); + + sprintf(name, "mesh/%s/%04x/s", vnd ? "v" : "s", model_key); + + if (!bt_mesh_atomic_test_bit(bt_mesh.flags, BLE_MESH_VALID)) { + bt_mesh_save_core_settings(name, NULL, 0); + return; + } + + err = bt_mesh_save_core_settings(name, (const u8_t *)model->groups, sizeof(model->groups)); + if (err) { + BT_ERR("%s, Failed to save %s", __func__, name); + return; + } + + err = bt_mesh_add_core_settings_item(vnd ? "mesh/vnd" : "mesh/sig", model_key); + if (err) { + BT_ERR("%s, Failed to add 0x%04x to %s", __func__, model_key, + vnd ? "mesh/vnd" : "mesh/sig"); + } + + return; +} + +static void store_pending_mod_pub(struct bt_mesh_model *model, bool vnd) +{ + struct mod_pub_val pub = {0}; + char name[16] = {'\0'}; + u16_t model_key; + int err; + + if (!model->pub) { + BT_WARN("%s, No model publication to store", __func__); + return; + } + + pub.addr = model->pub->addr; + pub.key = model->pub->key; + pub.ttl = model->pub->ttl; + pub.retransmit = model->pub->retransmit; + pub.period = model->pub->period; + pub.period_div = model->pub->period_div; + pub.cred = model->pub->cred; + + model_key = GET_MODEL_KEY(model->elem_idx, model->model_idx); + + sprintf(name, "mesh/%s/%04x/p", vnd ? "v" : "s", model_key); + + if (!bt_mesh_atomic_test_bit(bt_mesh.flags, BLE_MESH_VALID)) { + bt_mesh_save_core_settings(name, NULL, 0); + return; + } + + err = bt_mesh_save_core_settings(name, (const u8_t *)&pub, sizeof(pub)); + if (err) { + BT_ERR("%s, Failed to save %s", __func__, name); + return; + } + + err = bt_mesh_add_core_settings_item(vnd ? "mesh/vnd" : "mesh/sig", model_key); + if (err) { + BT_ERR("%s, Failed to add 0x%04x to %s", __func__, model_key, + vnd ? "mesh/vnd" : "mesh/sig"); + } + + return; +} + +static void store_pending_mod(struct bt_mesh_model *model, + struct bt_mesh_elem *elem, bool vnd, + bool primary, void *user_data) +{ + if (!model->flags) { + return; + } + + if (model->flags & BLE_MESH_MOD_BIND_PENDING) { + model->flags &= ~BLE_MESH_MOD_BIND_PENDING; + store_pending_mod_bind(model, vnd); + } + + if (model->flags & BLE_MESH_MOD_SUB_PENDING) { + model->flags &= ~BLE_MESH_MOD_SUB_PENDING; + store_pending_mod_sub(model, vnd); + } + + if (model->flags & BLE_MESH_MOD_PUB_PENDING) { + model->flags &= ~BLE_MESH_MOD_PUB_PENDING; + store_pending_mod_pub(model, vnd); + } +} + +static void store_pending(struct k_work *work) +{ + BT_DBG("%s", __func__); + + if (bt_mesh_atomic_test_and_clear_bit(bt_mesh.flags, BLE_MESH_RPL_PENDING)) { + if (bt_mesh_atomic_test_bit(bt_mesh.flags, BLE_MESH_VALID)) { + store_pending_rpl(); + } else { + clear_rpl(); + } + } + + if (bt_mesh_atomic_test_and_clear_bit(bt_mesh.flags, BLE_MESH_KEYS_PENDING)) { + store_pending_keys(); + } + + if (bt_mesh_atomic_test_and_clear_bit(bt_mesh.flags, BLE_MESH_NET_PENDING)) { + if (bt_mesh_atomic_test_bit(bt_mesh.flags, BLE_MESH_VALID)) { + store_pending_net(); + } else { + clear_net(); + } + } + + if (bt_mesh_atomic_test_and_clear_bit(bt_mesh.flags, BLE_MESH_IV_PENDING)) { + if (bt_mesh_atomic_test_bit(bt_mesh.flags, BLE_MESH_VALID)) { + store_pending_iv(); + } else { + clear_iv(); + } + } + + if (bt_mesh_atomic_test_and_clear_bit(bt_mesh.flags, BLE_MESH_SEQ_PENDING)) { + store_pending_seq(); + } + + if (bt_mesh_atomic_test_and_clear_bit(bt_mesh.flags, BLE_MESH_HB_PUB_PENDING)) { + store_pending_hb_pub(); + } + + if (bt_mesh_atomic_test_and_clear_bit(bt_mesh.flags, BLE_MESH_CFG_PENDING)) { + if (bt_mesh_atomic_test_bit(bt_mesh.flags, BLE_MESH_VALID)) { + store_pending_cfg(); + } else { + clear_cfg(); + } + } + + if (bt_mesh_atomic_test_and_clear_bit(bt_mesh.flags, BLE_MESH_MOD_PENDING)) { + bt_mesh_model_foreach(store_pending_mod, NULL); + if (!bt_mesh_atomic_test_bit(bt_mesh.flags, BLE_MESH_VALID)) { + bt_mesh_save_core_settings("mesh/sig", NULL, 0); + bt_mesh_save_core_settings("mesh/vnd", NULL, 0); + } + } +} + +void bt_mesh_store_rpl(struct bt_mesh_rpl *entry) +{ + entry->store = true; + schedule_store(BLE_MESH_RPL_PENDING); +} + +static struct key_update *key_update_find(bool app_key, u16_t key_idx, + struct key_update **free_slot) +{ + struct key_update *match = NULL; + int i; + + *free_slot = NULL; + + for (i = 0; i < ARRAY_SIZE(key_updates); i++) { + struct key_update *update = &key_updates[i]; + + if (!update->valid) { + *free_slot = update; + continue; + } + + if (update->app_key != app_key) { + continue; + } + + if (update->key_idx == key_idx) { + match = update; + } + } + + return match; +} + +void bt_mesh_store_subnet(struct bt_mesh_subnet *sub) +{ + struct key_update *free_slot = NULL; + struct key_update *update = NULL; + + BT_DBG("NetKeyIndex 0x%03x", sub->net_idx); + + update = key_update_find(false, sub->net_idx, &free_slot); + if (update) { + update->clear = 0U; + schedule_store(BLE_MESH_KEYS_PENDING); + return; + } + + if (!free_slot) { + store_net_key(sub); + return; + } + + free_slot->valid = 1U; + free_slot->key_idx = sub->net_idx; + free_slot->app_key = 0U; + free_slot->clear = 0U; + + schedule_store(BLE_MESH_KEYS_PENDING); +} + +void bt_mesh_store_app_key(struct bt_mesh_app_key *key) +{ + struct key_update *free_slot = NULL; + struct key_update *update = NULL; + + BT_DBG("AppKeyIndex 0x%03x", key->app_idx); + + update = key_update_find(true, key->app_idx, &free_slot); + if (update) { + update->clear = 0U; + schedule_store(BLE_MESH_KEYS_PENDING); + return; + } + + if (!free_slot) { + store_app_key(key); + return; + } + + free_slot->valid = 1U; + free_slot->key_idx = key->app_idx; + free_slot->app_key = 1U; + free_slot->clear = 0U; + + schedule_store(BLE_MESH_KEYS_PENDING); +} + +void bt_mesh_store_hb_pub(void) +{ + schedule_store(BLE_MESH_HB_PUB_PENDING); +} + +void bt_mesh_store_cfg(void) +{ + schedule_store(BLE_MESH_CFG_PENDING); +} + +void bt_mesh_clear_net(void) +{ + schedule_store(BLE_MESH_NET_PENDING); + schedule_store(BLE_MESH_IV_PENDING); + schedule_store(BLE_MESH_CFG_PENDING); +} + +void bt_mesh_clear_subnet(struct bt_mesh_subnet *sub) +{ + struct key_update *free_slot = NULL; + struct key_update *update = NULL; + + BT_DBG("NetKeyIndex 0x%03x", sub->net_idx); + + update = key_update_find(false, sub->net_idx, &free_slot); + if (update) { + update->clear = 1U; + schedule_store(BLE_MESH_KEYS_PENDING); + return; + } + + if (!free_slot) { + clear_net_key(sub->net_idx); + return; + } + + free_slot->valid = 1U; + free_slot->key_idx = sub->net_idx; + free_slot->app_key = 0U; + free_slot->clear = 1U; + + schedule_store(BLE_MESH_KEYS_PENDING); +} + +void bt_mesh_clear_app_key(struct bt_mesh_app_key *key) +{ + struct key_update *free_slot = NULL; + struct key_update *update = NULL; + + BT_DBG("AppKeyIndex 0x%03x", key->app_idx); + + update = key_update_find(true, key->app_idx, &free_slot); + if (update) { + update->clear = 1U; + schedule_store(BLE_MESH_KEYS_PENDING); + return; + } + + if (!free_slot) { + clear_app_key(key->app_idx); + return; + } + + free_slot->valid = 1U; + free_slot->key_idx = key->app_idx; + free_slot->app_key = 1U; + free_slot->clear = 1U; + + schedule_store(BLE_MESH_KEYS_PENDING); +} + +void bt_mesh_clear_rpl(void) +{ + schedule_store(BLE_MESH_RPL_PENDING); +} + +void bt_mesh_store_mod_bind(struct bt_mesh_model *model) +{ + model->flags |= BLE_MESH_MOD_BIND_PENDING; + schedule_store(BLE_MESH_MOD_PENDING); +} + +void bt_mesh_store_mod_sub(struct bt_mesh_model *model) +{ + model->flags |= BLE_MESH_MOD_SUB_PENDING; + schedule_store(BLE_MESH_MOD_PENDING); +} + +void bt_mesh_store_mod_pub(struct bt_mesh_model *model) +{ + model->flags |= BLE_MESH_MOD_PUB_PENDING; + schedule_store(BLE_MESH_MOD_PENDING); +} + +int settings_core_init(void) +{ + BT_DBG("%s", __func__); + + k_delayed_work_init(&pending_store, store_pending); + + return 0; +} + +int bt_mesh_settings_init(void) +{ + BT_DBG("%s", __func__); + + bt_mesh_settings_foreach(); + + return 0; +} + +#endif /* CONFIG_BLE_MESH_SETTINGS */ diff --git a/components/bt/ble_mesh/mesh_core/settings.h b/components/bt/ble_mesh/mesh_core/settings.h new file mode 100644 index 0000000000..84b5e182b4 --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/settings.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2018 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _SETTINGS_H_ +#define _SETTINGS_H_ + +#include "sdkconfig.h" + +#include "net.h" +#include "mesh_access.h" +#include "mesh_bearer_adapt.h" + +int settings_core_init(void); +int settings_core_load(void); +int settings_core_commit(void); + +void bt_mesh_store_net(void); +void bt_mesh_store_iv(bool only_duration); +void bt_mesh_store_seq(void); +void bt_mesh_store_rpl(struct bt_mesh_rpl *rpl); +void bt_mesh_store_subnet(struct bt_mesh_subnet *sub); +void bt_mesh_store_app_key(struct bt_mesh_app_key *key); +void bt_mesh_store_hb_pub(void); +void bt_mesh_store_cfg(void); +void bt_mesh_store_mod_bind(struct bt_mesh_model *mod); +void bt_mesh_store_mod_sub(struct bt_mesh_model *mod); +void bt_mesh_store_mod_pub(struct bt_mesh_model *mod); + +void bt_mesh_clear_net(void); +void bt_mesh_clear_subnet(struct bt_mesh_subnet *sub); +void bt_mesh_clear_app_key(struct bt_mesh_app_key *key); +void bt_mesh_clear_rpl(void); + +int bt_mesh_settings_init(void); + +#endif /* _SETTINGS_H_ */ diff --git a/components/bt/ble_mesh/mesh_core/settings/settings_nvs.c b/components/bt/ble_mesh/mesh_core/settings/settings_nvs.c new file mode 100644 index 0000000000..1a19bee297 --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/settings/settings_nvs.c @@ -0,0 +1,374 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 +#include +#include + +#include "nvs.h" +#include "sdkconfig.h" + +#include "mesh_util.h" +#include "mesh_types.h" +#include "mesh_common.h" + +#include "settings_nvs.h" +#include "settings.h" + +#if CONFIG_BLE_MESH_SETTINGS + +enum settings_type { + SETTINGS_CORE, + SETTINGS_SERVER, +}; + +struct settings_context { + char *nvs_name; + nvs_handle handle; + + int (*settings_init)(void); + int (*settings_load)(void); + int (*settings_commit)(void); +}; + +static struct settings_context settings_ctx[] = { + [SETTINGS_CORE] = { + .nvs_name = "mesh_core", + .settings_init = settings_core_init, + .settings_load = settings_core_load, + .settings_commit = settings_core_commit, + }, + [SETTINGS_SERVER] = { + .nvs_name = "mesh_server", + .settings_init = NULL, + .settings_load = NULL, + .settings_commit = NULL, + }, +}; + +/* API used to initialize, load and commit BLE Mesh related settings */ + +void bt_mesh_settings_foreach(void) +{ + int i, err; + + for (i = 0; i < ARRAY_SIZE(settings_ctx); i++) { + struct settings_context *ctx = &settings_ctx[i]; + + err = nvs_open(ctx->nvs_name, NVS_READWRITE, &ctx->handle); + if (err != ESP_OK) { + BT_ERR("%s, Open nvs failed, name %s, err %d", __func__, ctx->nvs_name, err); + continue; + } + + if (ctx->settings_init && ctx->settings_init()) { + BT_ERR("%s, Init settings failed, name %s", __func__, ctx->nvs_name); + continue; + } + + if (ctx->settings_load && ctx->settings_load()) { + BT_ERR("%s, Load settings failed, name %s", __func__, ctx->nvs_name); + continue; + } + + if (ctx->settings_commit && ctx->settings_commit()) { + BT_ERR("%s, Commit settings failed, name %s", __func__, ctx->nvs_name); + continue; + } + } +} + +/* API used to get BLE Mesh related nvs handle */ + +static inline nvs_handle settings_get_nvs_handle(enum settings_type type) +{ + return settings_ctx[type].handle; +} + +/* API used to store/erase BLE Mesh related settings */ + +static int settings_save(nvs_handle handle, const char *key, const u8_t *val, size_t len) +{ + int err; + + if (key == NULL) { + BT_ERR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + BT_DBG("%s, nvs %s, key %s", __func__, val ? "set" : "erase", key); + + if (val) { + err = nvs_set_blob(handle, key, val, len); + } else { + err = nvs_erase_key(handle, key); + if (err == ESP_ERR_NVS_NOT_FOUND) { + BT_DBG("%s, %s does not exist", __func__, key); + return 0; + } + } + if (err != ESP_OK) { + BT_ERR("%s, Failed to %s %s data (err %d)", __func__, + val ? "set" : "erase", key, err); + return -EIO; + } + + err = nvs_commit(handle); + if (err != ESP_OK) { + BT_ERR("%s, Failed to commit settings (err %d)", __func__, err); + return -EIO; + } + + return 0; +} + +int bt_mesh_save_core_settings(const char *key, const u8_t *val, size_t len) +{ + nvs_handle handle = settings_get_nvs_handle(SETTINGS_CORE); + return settings_save(handle, key, val, len); +} + +/* API used to load BLE Mesh related settings */ + +static int settings_load(nvs_handle handle, const char *key, + u8_t *buf, size_t buf_len, bool *exist) +{ + int err; + + if (key == NULL || buf == NULL || exist == NULL) { + BT_ERR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + err = nvs_get_blob(handle, key, buf, &buf_len); + if (err != ESP_OK) { + if (err == ESP_ERR_NVS_NOT_FOUND) { + BT_DBG("%s, Settings %s is not found", __func__, key); + *exist = false; + return 0; + } + + BT_ERR("%s, Failed to get %s data (err %d)", __func__, key, err); + return -EIO; + } + + *exist = true; + return 0; +} + +int bt_mesh_load_core_settings(const char *key, u8_t *buf, size_t buf_len, bool *exist) +{ + nvs_handle handle = settings_get_nvs_handle(SETTINGS_CORE); + return settings_load(handle, key, buf, buf_len, exist); +} + +/* API used to get length of BLE Mesh related settings */ + +static size_t settings_get_length(nvs_handle handle, const char *key) +{ + size_t len = 0; + int err; + + if (key == NULL) { + BT_ERR("%s, Invalid parameter", __func__); + return 0; + } + + err = nvs_get_blob(handle, key, NULL, &len); + if (err != ESP_OK) { + if (err != ESP_ERR_NVS_NOT_FOUND) { + BT_ERR("%s, Failed to get %s length (err %d)", __func__, key, err); + } + return 0; + } + + return len; +} + +/* API used to get BLE Mesh related items. Here items mean model key, NetKey/AppKey + * Index, etc. which are going to be used as the prefix of the nvs keys of the BLE + * Mesh settings. + */ + +static struct net_buf_simple *settings_get_item(nvs_handle handle, const char *key) +{ + struct net_buf_simple *buf = NULL; + size_t length; + bool exist; + int err; + + length = settings_get_length(handle, key); + if (!length) { + BT_DBG("%s, Empty %s", __func__, key); + return NULL; + } + + buf = bt_mesh_alloc_buf(length); + if (!buf) { + BT_ERR("%s, Failed to allocate memory", __func__); + /* TODO: in this case, erase all related settings? */ + return NULL; + } + + err = settings_load(handle, key, buf->data, length, &exist); + if (err) { + BT_ERR("%s, Failed to load %s", __func__, key); + /* TODO: in this case, erase all related settings? */ + bt_mesh_free_buf(buf); + return NULL; + } + + if (exist == false) { + bt_mesh_free_buf(buf); + return NULL; + } + + buf->len = length; + return buf; +} + +struct net_buf_simple *bt_mesh_get_core_settings_item(const char *key) +{ + nvs_handle handle = settings_get_nvs_handle(SETTINGS_CORE); + return settings_get_item(handle, key); +} + +/* API used to check if the settings item exists */ + +static bool is_settings_item_exist(struct net_buf_simple *buf, const u16_t val) +{ + struct net_buf_simple_state state = {0}; + size_t length; + int i; + + if (!buf) { + return false; + } + + net_buf_simple_save(buf, &state); + + length = buf->len; + for (i = 0; i < length / SETTINGS_ITEM_SIZE; i++) { + u16_t item = net_buf_simple_pull_le16(buf); + if (item == val) { + net_buf_simple_restore(buf, &state); + return true; + } + } + + net_buf_simple_restore(buf, &state); + return false; +} + +/* API used to add the settings item */ + +static int settings_add_item(nvs_handle handle, const char *key, const u16_t val) +{ + struct net_buf_simple *store = NULL; + struct net_buf_simple *buf = NULL; + size_t length = 0; + int err; + + buf = settings_get_item(handle, key); + + /* Check if val already exists */ + if (is_settings_item_exist(buf, val) == true) { + BT_DBG("%s, 0x%04x already exists", __func__, val); + bt_mesh_free_buf(buf); + return 0; + } + + length = (buf ? buf->len : 0) + sizeof(val); + + store = bt_mesh_alloc_buf(length); + if (!store) { + BT_ERR("%s, Failed to allocate memory", __func__); + bt_mesh_free_buf(buf); + return -ENOMEM; + } + + if (buf) { + net_buf_simple_add_mem(store, buf->data, buf->len); + } + net_buf_simple_add_mem(store, &val, sizeof(val)); + + err = settings_save(handle, key, store->data, store->len); + + bt_mesh_free_buf(store); + bt_mesh_free_buf(buf); + return err; +} + +int bt_mesh_add_core_settings_item(const char *key, const u16_t val) +{ + nvs_handle handle = settings_get_nvs_handle(SETTINGS_CORE); + return settings_add_item(handle, key, val); +} + +/* API used to remove the settings item */ + +static int settings_remove_item(nvs_handle handle, const char *key, const u16_t val) +{ + struct net_buf_simple *store = NULL; + struct net_buf_simple *buf = NULL; + size_t length = 0; + size_t buf_len; + int i, err; + + buf = settings_get_item(handle, key); + + /* Check if val does exist */ + if (is_settings_item_exist(buf, val) == false) { + BT_DBG("%s, 0x%04x does not exist", __func__, val); + bt_mesh_free_buf(buf); + return 0; + } + + length = buf->len - sizeof(val); + if (!length) { + settings_save(handle, key, NULL, 0); + bt_mesh_free_buf(buf); + return 0; + } + + store = bt_mesh_alloc_buf(length); + if (!store) { + BT_ERR("%s, Failed to allocate memory", __func__); + bt_mesh_free_buf(buf); + return -ENOMEM; + } + + buf_len = buf->len; + for (i = 0; i < buf_len / SETTINGS_ITEM_SIZE; i++) { + u16_t item = net_buf_simple_pull_le16(buf); + if (item != val) { + net_buf_simple_add_le16(store, item); + } + } + + err = settings_save(handle, key, store->data, store->len); + + bt_mesh_free_buf(store); + bt_mesh_free_buf(buf); + return err; +} + +int bt_mesh_remove_core_settings_item(const char *key, const u16_t val) +{ + nvs_handle handle = settings_get_nvs_handle(SETTINGS_CORE); + return settings_remove_item(handle, key, val); +} + +#endif /* CONFIG_BLE_MESH_SETTINGS */ diff --git a/components/bt/ble_mesh/mesh_core/settings/settings_nvs.h b/components/bt/ble_mesh/mesh_core/settings/settings_nvs.h new file mode 100644 index 0000000000..b1e929adf8 --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/settings/settings_nvs.h @@ -0,0 +1,44 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#ifndef _BLE_MESH_SETTINGS_NVS_H_ +#define _BLE_MESH_SETTINGS_NVS_H_ + +#include +#include "mesh_types.h" +#include "mesh_buf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define SETTINGS_ITEM_SIZE sizeof(u16_t) + +void bt_mesh_settings_foreach(void); + +int bt_mesh_save_core_settings(const char *key, const u8_t *val, size_t len); + +int bt_mesh_load_core_settings(const char *key, u8_t *buf, size_t buf_len, bool *exist); + +struct net_buf_simple *bt_mesh_get_core_settings_item(const char *key); + +int bt_mesh_add_core_settings_item(const char *key, const u16_t val); + +int bt_mesh_remove_core_settings_item(const char *key, const u16_t val); + +#ifdef __cplusplus +} +#endif + +#endif /* _BLE_MESH_SETTINGS_NVS_H_ */ diff --git a/components/bt/ble_mesh/mesh_core/test.c b/components/bt/ble_mesh/mesh_core/test.c new file mode 100644 index 0000000000..f61beb8720 --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/test.c @@ -0,0 +1,131 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include "mesh_trace.h" +#include "mesh_main.h" +#include "mesh_access.h" + +#include "mesh.h" +#include "test.h" +#include "crypto.h" +#include "net.h" +#include "foundation.h" +#include "access.h" + +#if defined(CONFIG_BLE_MESH_SELF_TEST) + +int bt_mesh_test(void) +{ + return 0; +} +#endif /* #if defined(CONFIG_BLE_MESH_SELF_TEST) */ + +int bt_mesh_device_auto_enter_network(struct bt_mesh_device_network_info *info) +{ + const struct bt_mesh_comp *comp = NULL; + struct bt_mesh_model *model = NULL; + struct bt_mesh_elem *elem = NULL; + struct bt_mesh_app_keys *keys = NULL; + struct bt_mesh_app_key *key = NULL; + struct bt_mesh_subnet *sub = NULL; + int i, j, k; + int err; + + if (info == NULL || !BLE_MESH_ADDR_IS_UNICAST(info->unicast_addr) || + !BLE_MESH_ADDR_IS_GROUP(info->group_addr)) { + return -EINVAL; + } + + /* The device becomes a node and enters the network */ + err = bt_mesh_provision(info->net_key, info->net_idx, info->flags, info->iv_index, + info->unicast_addr, info->dev_key); + if (err) { + BT_ERR("%s, bt_mesh_provision() failed (err %d)", __func__, err); + return err; + } + + /* Adds application key to device */ + sub = bt_mesh_subnet_get(info->net_idx); + if (!sub) { + BT_ERR("%s, Failed to find subnet 0x%04x", __func__, info->net_idx); + return -ENODEV; + } + + for (i = 0; i < ARRAY_SIZE(bt_mesh.app_keys); i++) { + key = &bt_mesh.app_keys[i]; + if (key->net_idx == BLE_MESH_KEY_UNUSED) { + break; + } + } + if (i == ARRAY_SIZE(bt_mesh.app_keys)) { + BT_ERR("%s, Failed to allocate memory, AppKeyIndex 0x%04x", __func__, info->app_idx); + return -ENOMEM; + } + + keys = sub->kr_flag ? &key->keys[1] : &key->keys[0]; + + if (bt_mesh_app_id(info->app_key, &keys->id)) { + BT_ERR("%s, Failed to calculate AID, AppKeyIndex 0x%04x", __func__, info->app_idx); + return -EIO; + } + + key->net_idx = info->net_idx; + key->app_idx = info->app_idx; + memcpy(keys->val, info->app_key, 16); + + /* Binds AppKey with all non-config models, adds group address to all these models */ + comp = bt_mesh_comp_get(); + if (!comp) { + BT_ERR("%s, Composition data is NULL", __func__); + return -ENODEV; + } + + for (i = 0; i < comp->elem_count; i++) { + elem = &comp->elem[i]; + for (j = 0; j < elem->model_count; j++) { + model = &elem->models[j]; + if (model->id == BLE_MESH_MODEL_ID_CFG_SRV || + model->id == BLE_MESH_MODEL_ID_CFG_CLI) { + continue; + } + for (k = 0; k < ARRAY_SIZE(model->keys); k++) { + if (model->keys[k] == BLE_MESH_KEY_UNUSED) { + model->keys[k] = info->app_idx; + break; + } + } + for (k = 0; k < ARRAY_SIZE(model->groups); k++) { + if (model->groups[k] == BLE_MESH_ADDR_UNASSIGNED) { + model->groups[k] = info->group_addr; + break; + } + } + } + for (j = 0; j < elem->vnd_model_count; j++) { + model = &elem->vnd_models[j]; + for (k = 0; k < ARRAY_SIZE(model->keys); k++) { + if (model->keys[k] == BLE_MESH_KEY_UNUSED) { + model->keys[k] = info->app_idx; + break; + } + } + for (k = 0; k < ARRAY_SIZE(model->groups); k++) { + if (model->groups[k] == BLE_MESH_ADDR_UNASSIGNED) { + model->groups[k] = info->group_addr; + break; + } + } + } + } + + return 0; +} diff --git a/components/bt/ble_mesh/mesh_core/test.h b/components/bt/ble_mesh/mesh_core/test.h new file mode 100644 index 0000000000..60e3d0d8f1 --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/test.h @@ -0,0 +1,43 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _BLE_MESH_TEST_H_ +#define _BLE_MESH_TEST_H_ + +#include +#include +#include + +#include "mesh_buf.h" +#include "sdkconfig.h" + +#if defined(CONFIG_BLE_MESH_SELF_TEST) +int bt_mesh_test(void); +#else +static inline int bt_mesh_test(void) +{ + return 0; +} +#endif + +struct bt_mesh_device_network_info { + u8_t net_key[16]; + u16_t net_idx; + u8_t flags; + u32_t iv_index; + u16_t unicast_addr; + u8_t dev_key[16]; + u8_t app_key[16]; + u16_t app_idx; + u16_t group_addr; +}; + +int bt_mesh_device_auto_enter_network(struct bt_mesh_device_network_info *info); + +#endif /* _BLE_MESH_TEST_H_ */ diff --git a/components/bt/ble_mesh/mesh_core/transport.c b/components/bt/ble_mesh/mesh_core/transport.c new file mode 100644 index 0000000000..a456ebbfc8 --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/transport.c @@ -0,0 +1,1681 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include "sdkconfig.h" +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BLE_MESH_DEBUG_TRANS) + +#include "mesh_types.h" +#include "mesh_util.h" +#include "mesh_buf.h" +#include "mesh_trace.h" +#include "mesh_main.h" +#include "settings.h" + +#include "crypto.h" +#include "adv.h" +#include "mesh.h" +#include "net.h" +#include "lpn.h" +#include "friend.h" +#include "access.h" +#include "foundation.h" +#include "settings.h" +#include "transport.h" +#include "mesh_common.h" +#include "model_common.h" +#include "provisioner_main.h" + +/* The transport layer needs at least three buffers for itself to avoid + * deadlocks. Ensure that there are a sufficient number of advertising + * buffers available compared to the maximum supported outgoing segment + * count. + */ +_Static_assert(CONFIG_BLE_MESH_ADV_BUF_COUNT >= (CONFIG_BLE_MESH_TX_SEG_MAX + 3), + "Too small BLE Mesh adv buffer count"); + +#define AID_MASK ((u8_t)(BIT_MASK(6))) + +#define SEG(data) ((data)[0] >> 7) +#define AKF(data) (((data)[0] >> 6) & 0x01) +#define AID(data) ((data)[0] & AID_MASK) +#define ASZMIC(data) (((data)[1] >> 7) & 1) + +#define APP_MIC_LEN(aszmic) ((aszmic) ? 8 : 4) + +#define UNSEG_HDR(akf, aid) ((akf << 6) | (aid & AID_MASK)) +#define SEG_HDR(akf, aid) (UNSEG_HDR(akf, aid) | 0x80) + +#define BLOCK_COMPLETE(seg_n) (u32_t)(((u64_t)1 << (seg_n + 1)) - 1) + +#define SEQ_AUTH(iv_index, seq) (((u64_t)iv_index) << 24 | (u64_t)seq) + +/* Number of retransmit attempts (after the initial transmit) per segment */ +#define SEG_RETRANSMIT_ATTEMPTS 4 + +/* "This timer shall be set to a minimum of 200 + 50 * TTL milliseconds.". + * We use 400 since 300 is a common send duration for standard HCI, and we + * need to have a timeout that's bigger than that. + */ +#define SEG_RETRANSMIT_TIMEOUT(tx) (K_MSEC(400) + 50 * (tx)->ttl) + +/* How long to wait for available buffers before giving up */ +#define BUF_TIMEOUT K_NO_WAIT + +static struct seg_tx { + struct bt_mesh_subnet *sub; + struct net_buf *seg[CONFIG_BLE_MESH_TX_SEG_MAX]; + u64_t seq_auth; + u16_t dst; + u8_t seg_n: 5, /* Last segment index */ + new_key: 1; /* New/old key */ + u8_t nack_count; /* Number of unacked segs */ + u8_t ttl; + const struct bt_mesh_send_cb *cb; + void *cb_data; + struct k_delayed_work retransmit; /* Retransmit timer */ +} seg_tx[CONFIG_BLE_MESH_TX_SEG_MSG_COUNT]; + +static struct seg_rx { + struct bt_mesh_subnet *sub; + u64_t seq_auth; + u8_t seg_n: 5, + ctl: 1, + in_use: 1, + obo: 1; + u8_t hdr; + u8_t ttl; + u16_t src; + u16_t dst; + u32_t block; + u32_t last; + struct k_delayed_work ack; + struct net_buf_simple buf; +} seg_rx[CONFIG_BLE_MESH_RX_SEG_MSG_COUNT] = { + [0 ... (CONFIG_BLE_MESH_RX_SEG_MSG_COUNT - 1)] = { + .buf.size = CONFIG_BLE_MESH_RX_SDU_MAX, + }, +}; + +static u8_t seg_rx_buf_data[(CONFIG_BLE_MESH_RX_SEG_MSG_COUNT * + CONFIG_BLE_MESH_RX_SDU_MAX)]; + +static u16_t hb_sub_dst = BLE_MESH_ADDR_UNASSIGNED; + +void bt_mesh_set_hb_sub_dst(u16_t addr) +{ + hb_sub_dst = addr; +} + +static int send_unseg(struct bt_mesh_net_tx *tx, struct net_buf_simple *sdu, + const struct bt_mesh_send_cb *cb, void *cb_data) +{ + struct net_buf *buf; + + BT_DBG("src 0x%04x dst 0x%04x app_idx 0x%04x sdu_len %u", + tx->src, tx->ctx->addr, tx->ctx->app_idx, sdu->len); + + buf = bt_mesh_adv_create(BLE_MESH_ADV_DATA, tx->xmit, BUF_TIMEOUT); + if (!buf) { + BT_ERR("%s, Out of network buffers", __func__); + return -ENOBUFS; + } + + net_buf_reserve(buf, BLE_MESH_NET_HDR_LEN); + + if (tx->ctx->app_idx == BLE_MESH_KEY_DEV) { + net_buf_add_u8(buf, UNSEG_HDR(0, 0)); + } else { + net_buf_add_u8(buf, UNSEG_HDR(1, tx->aid)); + } + + net_buf_add_mem(buf, sdu->data, sdu->len); + + if (IS_ENABLED(CONFIG_BLE_MESH_NODE) && bt_mesh_is_provisioned()) { + if (IS_ENABLED(CONFIG_BLE_MESH_FRIEND)) { + if (bt_mesh_friend_enqueue_tx(tx, BLE_MESH_FRIEND_PDU_SINGLE, + NULL, &buf->b) && + BLE_MESH_ADDR_IS_UNICAST(tx->ctx->addr)) { + /* PDUs for a specific Friend should only go + * out through the Friend Queue. + */ + net_buf_unref(buf); + return 0; + } + } + } + + return bt_mesh_net_send(tx, buf, cb, cb_data); +} + +bool bt_mesh_tx_in_progress(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(seg_tx); i++) { + if (seg_tx[i].nack_count) { + return true; + } + } + + return false; +} + +static void seg_tx_reset(struct seg_tx *tx) +{ + int i; + + k_delayed_work_cancel(&tx->retransmit); + + tx->cb = NULL; + tx->cb_data = NULL; + tx->seq_auth = 0U; + tx->sub = NULL; + tx->dst = BLE_MESH_ADDR_UNASSIGNED; + + if (!tx->nack_count) { + return; + } + + for (i = 0; i <= tx->seg_n; i++) { + if (!tx->seg[i]) { + continue; + } + + /** Change by Espressif. Add this to avoid buf->ref is 2 which will + * cause lack of buf. + */ + if (tx->seg[i]->ref > 1) { + tx->seg[i]->ref = 1; + } + net_buf_unref(tx->seg[i]); + tx->seg[i] = NULL; + } + + tx->nack_count = 0U; + + if (bt_mesh_atomic_test_and_clear_bit(bt_mesh.flags, BLE_MESH_IVU_PENDING)) { + BT_DBG("Proceding with pending IV Update"); + /* bt_mesh_net_iv_update() will re-enable the flag if this + * wasn't the only transfer. + */ + if (bt_mesh_net_iv_update(bt_mesh.iv_index, false)) { + bt_mesh_net_sec_update(NULL); + } + } +} + +static inline void seg_tx_complete(struct seg_tx *tx, int err) +{ + if (tx->cb && tx->cb->end) { + tx->cb->end(err, tx->cb_data); + } + + seg_tx_reset(tx); +} + +static void seg_first_send_start(u16_t duration, int err, void *user_data) +{ + struct seg_tx *tx = user_data; + + if (tx->cb && tx->cb->start) { + tx->cb->start(duration, err, tx->cb_data); + } +} + +static void seg_send_start(u16_t duration, int err, void *user_data) +{ + struct seg_tx *tx = user_data; + + /* If there's an error in transmitting the 'sent' callback will never + * be called. Make sure that we kick the retransmit timer also in this + * case since otherwise we risk the transmission of becoming stale. + */ + if (err) { + k_delayed_work_submit(&tx->retransmit, + SEG_RETRANSMIT_TIMEOUT(tx)); + } +} + +static void seg_sent(int err, void *user_data) +{ + struct seg_tx *tx = user_data; + + k_delayed_work_submit(&tx->retransmit, + SEG_RETRANSMIT_TIMEOUT(tx)); +} + +static const struct bt_mesh_send_cb first_sent_cb = { + .start = seg_first_send_start, + .end = seg_sent, +}; + +static const struct bt_mesh_send_cb seg_sent_cb = { + .start = seg_send_start, + .end = seg_sent, +}; + +static void seg_tx_send_unacked(struct seg_tx *tx) +{ + int i, err; + + for (i = 0; i <= tx->seg_n; i++) { + struct net_buf *seg = tx->seg[i]; + + if (!seg) { + continue; + } + + if (BLE_MESH_ADV(seg)->busy) { + BT_DBG("Skipping segment that's still advertising"); + continue; + } + + if (!(BLE_MESH_ADV(seg)->seg.attempts--)) { + BT_WARN("Ran out of retransmit attempts"); + seg_tx_complete(tx, -ETIMEDOUT); + return; + } + + BT_DBG("resending %u/%u", i, tx->seg_n); + + err = bt_mesh_net_resend(tx->sub, seg, tx->new_key, + &seg_sent_cb, tx); + if (err) { + BT_ERR("%s, Sending segment failed", __func__); + seg_tx_complete(tx, -EIO); + return; + } + } +} + +static void seg_retransmit(struct k_work *work) +{ + struct seg_tx *tx = CONTAINER_OF(work, struct seg_tx, retransmit); + + seg_tx_send_unacked(tx); +} + +static int send_seg(struct bt_mesh_net_tx *net_tx, struct net_buf_simple *sdu, + const struct bt_mesh_send_cb *cb, void *cb_data) +{ + u8_t seg_hdr, seg_o; + u16_t seq_zero; + struct seg_tx *tx; + int i; + + BT_DBG("src 0x%04x dst 0x%04x app_idx 0x%04x aszmic %u sdu_len %u", + net_tx->src, net_tx->ctx->addr, net_tx->ctx->app_idx, + net_tx->aszmic, sdu->len); + + if (sdu->len < 1) { + BT_ERR("%s, Zero-length SDU not allowed", __func__); + return -EINVAL; + } + + if (sdu->len > BLE_MESH_TX_SDU_MAX) { + BT_ERR("%s, Not enough segment buffers for length %u", __func__, sdu->len); + return -EMSGSIZE; + } + + for (tx = NULL, i = 0; i < ARRAY_SIZE(seg_tx); i++) { + if (!seg_tx[i].nack_count) { + tx = &seg_tx[i]; + break; + } + } + + if (!tx) { + BT_ERR("%s, No multi-segment message contexts available", __func__); + return -EBUSY; + } + + if (net_tx->ctx->app_idx == BLE_MESH_KEY_DEV) { + seg_hdr = SEG_HDR(0, 0); + } else { + seg_hdr = SEG_HDR(1, net_tx->aid); + } + + seg_o = 0U; + tx->dst = net_tx->ctx->addr; + tx->seg_n = (sdu->len - 1) / 12U; + tx->nack_count = tx->seg_n + 1; + tx->seq_auth = SEQ_AUTH(BLE_MESH_NET_IVI_TX, bt_mesh.seq); + tx->sub = net_tx->sub; + tx->new_key = net_tx->sub->kr_flag; + tx->cb = cb; + tx->cb_data = cb_data; + + if (net_tx->ctx->send_ttl == BLE_MESH_TTL_DEFAULT) { + tx->ttl = bt_mesh_default_ttl_get(); + } else { + tx->ttl = net_tx->ctx->send_ttl; + } + + seq_zero = tx->seq_auth & 0x1fff; + + BT_DBG("SeqZero 0x%04x", seq_zero); + + for (seg_o = 0U; sdu->len; seg_o++) { + struct net_buf *seg; + u16_t len; + int err; + + seg = bt_mesh_adv_create(BLE_MESH_ADV_DATA, net_tx->xmit, + BUF_TIMEOUT); + if (!seg) { + BT_ERR("%s, Out of segment buffers", __func__); + seg_tx_reset(tx); + return -ENOBUFS; + } + + BLE_MESH_ADV(seg)->seg.attempts = SEG_RETRANSMIT_ATTEMPTS; + + net_buf_reserve(seg, BLE_MESH_NET_HDR_LEN); + + net_buf_add_u8(seg, seg_hdr); + net_buf_add_u8(seg, (net_tx->aszmic << 7) | seq_zero >> 6); + net_buf_add_u8(seg, (((seq_zero & 0x3f) << 2) | + (seg_o >> 3))); + net_buf_add_u8(seg, ((seg_o & 0x07) << 5) | tx->seg_n); + + len = MIN(sdu->len, 12); + net_buf_add_mem(seg, sdu->data, len); + net_buf_simple_pull(sdu, len); + + tx->seg[seg_o] = net_buf_ref(seg); + + if (IS_ENABLED(CONFIG_BLE_MESH_NODE) && bt_mesh_is_provisioned()) { + if (IS_ENABLED(CONFIG_BLE_MESH_FRIEND)) { + enum bt_mesh_friend_pdu_type type; + + if (seg_o == tx->seg_n) { + type = BLE_MESH_FRIEND_PDU_COMPLETE; + } else { + type = BLE_MESH_FRIEND_PDU_PARTIAL; + } + + if (bt_mesh_friend_enqueue_tx(net_tx, type, + &tx->seq_auth, + &seg->b) && + BLE_MESH_ADDR_IS_UNICAST(net_tx->ctx->addr)) { + /* PDUs for a specific Friend should only go + * out through the Friend Queue. + */ + net_buf_unref(seg); + return 0; + } + } + } + + BT_DBG("Sending %u/%u", seg_o, tx->seg_n); + + err = bt_mesh_net_send(net_tx, seg, + seg_o ? &seg_sent_cb : &first_sent_cb, + tx); + if (err) { + BT_ERR("%s, Sending segment failed", __func__); + seg_tx_reset(tx); + return err; + } + } + + if (IS_ENABLED(CONFIG_BLE_MESH_NODE) && bt_mesh_is_provisioned()) { + if (IS_ENABLED(CONFIG_BLE_MESH_LOW_POWER) && + bt_mesh_lpn_established()) { + bt_mesh_lpn_poll(); + } + } + + return 0; +} + +struct bt_mesh_app_key *bt_mesh_app_key_find(u16_t app_idx) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bt_mesh.app_keys); i++) { + struct bt_mesh_app_key *key = &bt_mesh.app_keys[i]; + + if (key->net_idx != BLE_MESH_KEY_UNUSED && + key->app_idx == app_idx) { + return key; + } + } + + return NULL; +} + +int bt_mesh_trans_send(struct bt_mesh_net_tx *tx, struct net_buf_simple *msg, + const struct bt_mesh_send_cb *cb, void *cb_data) +{ + const u8_t *key = NULL; + u8_t *ad, role; + int err; + + if (net_buf_simple_tailroom(msg) < 4) { + BT_ERR("%s, Insufficient tailroom for Transport MIC", __func__); + return -EINVAL; + } + + if (msg->len > 11) { + tx->ctx->send_rel = 1U; + } + + BT_DBG("net_idx 0x%04x app_idx 0x%04x dst 0x%04x", tx->sub->net_idx, + tx->ctx->app_idx, tx->ctx->addr); + BT_DBG("len %u: %s", msg->len, bt_hex(msg->data, msg->len)); + + role = bt_mesh_get_model_role(tx->ctx->model, tx->ctx->srv_send); + if (role == ROLE_NVAL) { + BT_ERR("%s, Failed to get model role", __func__); + return -EINVAL; + } + + if (tx->ctx->app_idx == BLE_MESH_KEY_DEV) { +#if CONFIG_BLE_MESH_NODE && !CONFIG_BLE_MESH_PROVISIONER + if (role == NODE) { + if (!bt_mesh_is_provisioner_en()) { + key = bt_mesh.dev_key; + } + } +#endif + +#if !CONFIG_BLE_MESH_NODE && CONFIG_BLE_MESH_PROVISIONER + if (role == PROVISIONER) { + if (bt_mesh_is_provisioner_en()) { + key = provisioner_get_device_key(tx->ctx->addr); + } + } +#endif + +#if CONFIG_BLE_MESH_NODE && CONFIG_BLE_MESH_PROVISIONER + if (role == NODE) { + key = bt_mesh.dev_key; + } else if (role == PROVISIONER) { + if (bt_mesh_is_provisioner_en()) { + key = provisioner_get_device_key(tx->ctx->addr); + } + } else if (role == FAST_PROV) { +#if CONFIG_BLE_MESH_FAST_PROV + key = get_fast_prov_device_key(tx->ctx->addr); +#endif + } +#endif + + if (!key) { + BT_ERR("%s, Failed to get Device Key", __func__); + return -EINVAL; + } + + tx->aid = 0U; + } else { + struct bt_mesh_app_key *app_key = NULL; + +#if CONFIG_BLE_MESH_NODE && !CONFIG_BLE_MESH_PROVISIONER + if (role == NODE) { + if (!bt_mesh_is_provisioner_en()) { + app_key = bt_mesh_app_key_find(tx->ctx->app_idx); + } + } +#endif + +#if !CONFIG_BLE_MESH_NODE && CONFIG_BLE_MESH_PROVISIONER + if (role == PROVISIONER) { + if (bt_mesh_is_provisioner_en()) { + app_key = provisioner_app_key_find(tx->ctx->app_idx); + } + } +#endif + +#if CONFIG_BLE_MESH_NODE && CONFIG_BLE_MESH_PROVISIONER + if (role == NODE) { + app_key = bt_mesh_app_key_find(tx->ctx->app_idx); + } else if (role == PROVISIONER) { + if (bt_mesh_is_provisioner_en()) { + app_key = provisioner_app_key_find(tx->ctx->app_idx); + } + } else if (role == FAST_PROV) { +#if CONFIG_BLE_MESH_FAST_PROV + app_key = get_fast_prov_app_key(tx->ctx->net_idx, tx->ctx->app_idx); +#endif + } +#endif + + if (!app_key) { + BT_ERR("%s, Failed to get AppKey", __func__); + return -EINVAL; + } + + if (tx->sub->kr_phase == BLE_MESH_KR_PHASE_2 && + app_key->updated) { + key = app_key->keys[1].val; + tx->aid = app_key->keys[1].id; + } else { + key = app_key->keys[0].val; + tx->aid = app_key->keys[0].id; + } + } + + if (!tx->ctx->send_rel || net_buf_simple_tailroom(msg) < 8) { + tx->aszmic = 0U; + } else { + tx->aszmic = 1U; + } + + if (BLE_MESH_ADDR_IS_VIRTUAL(tx->ctx->addr)) { + ad = bt_mesh_label_uuid_get(tx->ctx->addr); + } else { + ad = NULL; + } + + err = bt_mesh_app_encrypt(key, tx->ctx->app_idx == BLE_MESH_KEY_DEV, + tx->aszmic, msg, ad, tx->src, + tx->ctx->addr, bt_mesh.seq, + BLE_MESH_NET_IVI_TX); + if (err) { + return err; + } + + if (tx->ctx->send_rel) { + err = send_seg(tx, msg, cb, cb_data); + } else { + err = send_unseg(tx, msg, cb, cb_data); + } + + return err; +} + +int bt_mesh_trans_resend(struct bt_mesh_net_tx *tx, struct net_buf_simple *msg, + const struct bt_mesh_send_cb *cb, void *cb_data) +{ + struct net_buf_simple_state state; + int err; + + net_buf_simple_save(msg, &state); + + if (tx->ctx->send_rel || msg->len > 15) { + err = send_seg(tx, msg, cb, cb_data); + } else { + err = send_unseg(tx, msg, cb, cb_data); + } + + net_buf_simple_restore(msg, &state); + + return err; +} + +static bool is_replay(struct bt_mesh_net_rx *rx) +{ + int i; + + /* Don't bother checking messages from ourselves */ + if (rx->net_if == BLE_MESH_NET_IF_LOCAL) { + return false; + } + + for (i = 0; i < ARRAY_SIZE(bt_mesh.rpl); i++) { + struct bt_mesh_rpl *rpl = &bt_mesh.rpl[i]; + + /* Empty slot */ + if (!rpl->src) { + rpl->src = rx->ctx.addr; + rpl->seq = rx->seq; + rpl->old_iv = rx->old_iv; + + if (IS_ENABLED(CONFIG_BLE_MESH_SETTINGS)) { + bt_mesh_store_rpl(rpl); + } + + return false; + } + + /* Existing slot for given address */ + if (rpl->src == rx->ctx.addr) { + if (rx->old_iv && !rpl->old_iv) { + return true; + } + +#if !CONFIG_BLE_MESH_PATCH_FOR_SLAB_APP_1_1_0 + if ((!rx->old_iv && rpl->old_iv) || + rpl->seq < rx->seq) { +#else /* CONFIG_BLE_MESH_PATCH_FOR_SLAB_APP_1_1_0 */ + /** + * Added 10 here to fix the bug of Silicon Lab Android App 1.1.0 when + * reconnection will cause its sequence number recounting from 0. + */ + if ((!rx->old_iv && rpl->old_iv) || + (rpl->seq < rx->seq) || (rpl->seq > rx->seq + 10)) { +#endif /* #if !CONFIG_BLE_MESH_PATCH_FOR_SLAB_APP_1_1_0 */ + rpl->seq = rx->seq; + rpl->old_iv = rx->old_iv; + + if (IS_ENABLED(CONFIG_BLE_MESH_SETTINGS)) { + bt_mesh_store_rpl(rpl); + } + + return false; + } else { + return true; + } + } + } + + BT_ERR("%s, RPL is full!", __func__); + return true; +} + +static int sdu_recv(struct bt_mesh_net_rx *rx, u32_t seq, u8_t hdr, + u8_t aszmic, struct net_buf_simple *buf) +{ + struct net_buf_simple *sdu = NULL; + u32_t array_size = 0; + u8_t *ad; + u16_t i; + int err; + + BT_DBG("ASZMIC %u AKF %u AID 0x%02x", aszmic, AKF(&hdr), AID(&hdr)); + BT_DBG("len %u: %s", buf->len, bt_hex(buf->data, buf->len)); + + if (buf->len < 1 + APP_MIC_LEN(aszmic)) { + BT_ERR("%s, Too short SDU + MIC", __func__); + return -EINVAL; + } + + if (IS_ENABLED(CONFIG_BLE_MESH_FRIEND) && !rx->local_match) { + BT_DBG("Ignoring PDU for LPN 0x%04x of this Friend", + rx->ctx.recv_dst); + return 0; + } + + if (BLE_MESH_ADDR_IS_VIRTUAL(rx->ctx.recv_dst)) { + ad = bt_mesh_label_uuid_get(rx->ctx.recv_dst); + } else { + ad = NULL; + } + + /* Adjust the length to not contain the MIC at the end */ + buf->len -= APP_MIC_LEN(aszmic); + + /* Use bt_mesh_alloc_buf() instead of NET_BUF_SIMPLE_DEFINE to avoid + * causing btu task stackoverflow. + */ + sdu = bt_mesh_alloc_buf(CONFIG_BLE_MESH_RX_SDU_MAX - 4); + if (!sdu) { + BT_ERR("%s, Failed to allocate memory", __func__); + return -ENOMEM; + } + + if (!AKF(&hdr)) { +#if CONFIG_BLE_MESH_NODE && !CONFIG_BLE_MESH_PROVISIONER + if (!bt_mesh_is_provisioner_en()) { + array_size = 1; + } +#endif + +#if !CONFIG_BLE_MESH_NODE && CONFIG_BLE_MESH_PROVISIONER + if (bt_mesh_is_provisioner_en()) { + array_size = 1; + } +#endif + +#if CONFIG_BLE_MESH_NODE && CONFIG_BLE_MESH_PROVISIONER + array_size = 1; + if (bt_mesh_is_provisioner_en()) { + array_size += 1; + } +#endif + + for (i = 0; i < array_size; i++) { + const u8_t *dev_key = NULL; + +#if CONFIG_BLE_MESH_NODE && !CONFIG_BLE_MESH_PROVISIONER + if (!bt_mesh_is_provisioner_en()) { + dev_key = bt_mesh.dev_key; + } +#endif + +#if !CONFIG_BLE_MESH_NODE && CONFIG_BLE_MESH_PROVISIONER + if (bt_mesh_is_provisioner_en()) { + dev_key = provisioner_get_device_key(rx->ctx.addr); + } +#endif + +#if CONFIG_BLE_MESH_NODE && CONFIG_BLE_MESH_PROVISIONER + if (i < 1) { + dev_key = bt_mesh.dev_key; + } else { + dev_key = provisioner_get_device_key(rx->ctx.addr); + } +#endif + + if (!dev_key) { + BT_DBG("%s, NULL Device Key", __func__); + continue; + } + + net_buf_simple_reset(sdu); + err = bt_mesh_app_decrypt(dev_key, true, aszmic, buf, + sdu, ad, rx->ctx.addr, + rx->ctx.recv_dst, seq, + BLE_MESH_NET_IVI_RX(rx)); + if (err) { + continue; + } + + rx->ctx.app_idx = BLE_MESH_KEY_DEV; + bt_mesh_model_recv(rx, sdu); + + bt_mesh_free_buf(sdu); + return 0; + } + + BT_WARN("%s, Unable to decrypt with DevKey", __func__); + bt_mesh_free_buf(sdu); + return -ENODEV; + } + +#if CONFIG_BLE_MESH_NODE && !CONFIG_BLE_MESH_PROVISIONER + if (!bt_mesh_is_provisioner_en()) { + array_size = ARRAY_SIZE(bt_mesh.app_keys); + } +#endif + +#if !CONFIG_BLE_MESH_NODE && CONFIG_BLE_MESH_PROVISIONER + if (bt_mesh_is_provisioner_en()) { + array_size = ARRAY_SIZE(bt_mesh.p_app_keys); + } +#endif + +#if CONFIG_BLE_MESH_NODE && CONFIG_BLE_MESH_PROVISIONER + array_size = ARRAY_SIZE(bt_mesh.app_keys); + if (bt_mesh_is_provisioner_en()) { + array_size += ARRAY_SIZE(bt_mesh.p_app_keys); + } +#endif + + for (i = 0; i < array_size; i++) { + struct bt_mesh_app_key *key = NULL; + struct bt_mesh_app_keys *keys = NULL; + +#if CONFIG_BLE_MESH_NODE && !CONFIG_BLE_MESH_PROVISIONER + if (!bt_mesh_is_provisioner_en()) { + key = &bt_mesh.app_keys[i]; + } +#endif + +#if !CONFIG_BLE_MESH_NODE && CONFIG_BLE_MESH_PROVISIONER + if (bt_mesh_is_provisioner_en()) { + key = bt_mesh.p_app_keys[i]; + } +#endif + +#if CONFIG_BLE_MESH_NODE && CONFIG_BLE_MESH_PROVISIONER + if (i < ARRAY_SIZE(bt_mesh.app_keys)) { + key = &bt_mesh.app_keys[i]; + } else { + key = bt_mesh.p_app_keys[i - ARRAY_SIZE(bt_mesh.app_keys)]; + } +#endif + + if (!key) { + BT_DBG("%s, NULL AppKey", __func__); + continue; + } + + /* Make sure that this AppKey matches received net_idx */ + if (key->net_idx != rx->sub->net_idx) { + continue; + } + + if (rx->new_key && key->updated) { + keys = &key->keys[1]; + } else { + keys = &key->keys[0]; + } + + /* Check that the AppKey ID matches */ + if (AID(&hdr) != keys->id) { + continue; + } + + net_buf_simple_reset(sdu); + err = bt_mesh_app_decrypt(keys->val, false, aszmic, buf, + sdu, ad, rx->ctx.addr, + rx->ctx.recv_dst, seq, + BLE_MESH_NET_IVI_RX(rx)); + if (err) { + BT_DBG("Unable to decrypt with AppKey 0x%03x", + key->app_idx); + continue; + } + + rx->ctx.app_idx = key->app_idx; + bt_mesh_model_recv(rx, sdu); + + bt_mesh_free_buf(sdu); + return 0; + } + + BT_WARN("%s, No matching AppKey", __func__); + bt_mesh_free_buf(sdu); + return -EINVAL; +} + +static struct seg_tx *seg_tx_lookup(u16_t seq_zero, u8_t obo, u16_t addr) +{ + struct seg_tx *tx; + int i; + + for (i = 0; i < ARRAY_SIZE(seg_tx); i++) { + tx = &seg_tx[i]; + + if ((tx->seq_auth & 0x1fff) != seq_zero) { + continue; + } + + if (tx->dst == addr) { + return tx; + } + + /* If the expected remote address doesn't match, + * but the OBO flag is set and this is the first + * acknowledgement, assume it's a Friend that's + * responding and therefore accept the message. + */ + if (obo && tx->nack_count == tx->seg_n + 1) { + tx->dst = addr; + return tx; + } + } + + return NULL; +} + +static int trans_ack(struct bt_mesh_net_rx *rx, u8_t hdr, + struct net_buf_simple *buf, u64_t *seq_auth) +{ + struct seg_tx *tx; + unsigned int bit; + u32_t ack; + u16_t seq_zero; + u8_t obo; + + if (buf->len < 6) { + BT_ERR("%s, Too short ack message", __func__); + return -EINVAL; + } + + seq_zero = net_buf_simple_pull_be16(buf); + obo = seq_zero >> 15; + seq_zero = (seq_zero >> 2) & 0x1fff; + + if (IS_ENABLED(CONFIG_BLE_MESH_FRIEND) && rx->friend_match) { + BT_DBG("Ack for LPN 0x%04x of this Friend", rx->ctx.recv_dst); + /* Best effort - we don't have enough info for true SeqAuth */ + *seq_auth = SEQ_AUTH(BLE_MESH_NET_IVI_RX(rx), seq_zero); + return 0; + } + + ack = net_buf_simple_pull_be32(buf); + + BT_DBG("OBO %u seq_zero 0x%04x ack 0x%08x", obo, seq_zero, ack); + + tx = seg_tx_lookup(seq_zero, obo, rx->ctx.addr); + if (!tx) { + BT_WARN("No matching TX context for ack"); + return -EINVAL; + } + + *seq_auth = tx->seq_auth; + + if (!ack) { + BT_WARN("SDU canceled"); + seg_tx_complete(tx, -ECANCELED); + return 0; + } + + if (find_msb_set(ack) - 1 > tx->seg_n) { + BT_ERR("%s, Too large segment number in ack", __func__); + return -EINVAL; + } + + k_delayed_work_cancel(&tx->retransmit); + + while ((bit = find_lsb_set(ack))) { + if (tx->seg[bit - 1]) { + BT_DBG("seg %u/%u acked", bit - 1, tx->seg_n); + net_buf_unref(tx->seg[bit - 1]); + tx->seg[bit - 1] = NULL; + tx->nack_count--; + } + + ack &= ~BIT(bit - 1); + } + + if (tx->nack_count) { + seg_tx_send_unacked(tx); + } else { + BT_DBG("SDU TX complete"); + seg_tx_complete(tx, 0); + } + + return 0; +} + +static int trans_heartbeat(struct bt_mesh_net_rx *rx, + struct net_buf_simple *buf) +{ + u8_t init_ttl, hops; + u16_t feat; + + if (buf->len < 3) { + BT_ERR("%s, Too short heartbeat message", __func__); + return -EINVAL; + } + + if (rx->ctx.recv_dst != hb_sub_dst) { + BT_WARN("Ignoring heartbeat to non-subscribed destination"); + return 0; + } + + init_ttl = (net_buf_simple_pull_u8(buf) & 0x7f); + feat = net_buf_simple_pull_be16(buf); + + hops = (init_ttl - rx->ctx.recv_ttl + 1); + + BT_DBG("src 0x%04x TTL %u InitTTL %u (%u hop%s) feat 0x%04x", + rx->ctx.addr, rx->ctx.recv_ttl, init_ttl, hops, + (hops == 1U) ? "" : "s", feat); + + bt_mesh_heartbeat(rx->ctx.addr, rx->ctx.recv_dst, hops, feat); + + return 0; +} + +static int ctl_recv(struct bt_mesh_net_rx *rx, u8_t hdr, + struct net_buf_simple *buf, u64_t *seq_auth) +{ + u8_t ctl_op = TRANS_CTL_OP(&hdr); + + BT_DBG("OpCode 0x%02x len %u", ctl_op, buf->len); + + switch (ctl_op) { + case TRANS_CTL_OP_ACK: + return trans_ack(rx, hdr, buf, seq_auth); + case TRANS_CTL_OP_HEARTBEAT: + return trans_heartbeat(rx, buf); + } + + /* Only acks and heartbeats may need processing without local_match */ + if (!rx->local_match) { + return 0; + } + + if (IS_ENABLED(CONFIG_BLE_MESH_NODE) && bt_mesh_is_provisioned()) { + if (IS_ENABLED(CONFIG_BLE_MESH_FRIEND) && !bt_mesh_lpn_established()) { + switch (ctl_op) { + case TRANS_CTL_OP_FRIEND_POLL: + return bt_mesh_friend_poll(rx, buf); + case TRANS_CTL_OP_FRIEND_REQ: + return bt_mesh_friend_req(rx, buf); + case TRANS_CTL_OP_FRIEND_CLEAR: + return bt_mesh_friend_clear(rx, buf); + case TRANS_CTL_OP_FRIEND_CLEAR_CFM: + return bt_mesh_friend_clear_cfm(rx, buf); + case TRANS_CTL_OP_FRIEND_SUB_ADD: + return bt_mesh_friend_sub_add(rx, buf); + case TRANS_CTL_OP_FRIEND_SUB_REM: + return bt_mesh_friend_sub_rem(rx, buf); + } + } + +#if defined(CONFIG_BLE_MESH_LOW_POWER) + if (ctl_op == TRANS_CTL_OP_FRIEND_OFFER) { + return bt_mesh_lpn_friend_offer(rx, buf); + } + + if (rx->ctx.addr == bt_mesh.lpn.frnd) { + if (ctl_op == TRANS_CTL_OP_FRIEND_CLEAR_CFM) { + return bt_mesh_lpn_friend_clear_cfm(rx, buf); + } + + if (!rx->friend_cred) { + BT_WARN("Message from friend with wrong credentials"); + return -EINVAL; + } + + switch (ctl_op) { + case TRANS_CTL_OP_FRIEND_UPDATE: + return bt_mesh_lpn_friend_update(rx, buf); + case TRANS_CTL_OP_FRIEND_SUB_CFM: + return bt_mesh_lpn_friend_sub_cfm(rx, buf); + } + } +#endif /* CONFIG_BLE_MESH_LOW_POWER */ + } + + BT_WARN("Unhandled TransOpCode 0x%02x", ctl_op); + + return -ENOENT; +} + +static int trans_unseg(struct net_buf_simple *buf, struct bt_mesh_net_rx *rx, + u64_t *seq_auth) +{ + u8_t hdr; + + BT_DBG("AFK %u AID 0x%02x", AKF(buf->data), AID(buf->data)); + + if (buf->len < 1) { + BT_ERR("%s, Too small unsegmented PDU", __func__); + return -EINVAL; + } + + if (rx->local_match && is_replay(rx)) { + BT_WARN("Replay: src 0x%04x dst 0x%04x seq 0x%06x", + rx->ctx.addr, rx->ctx.recv_dst, rx->seq); + return -EINVAL; + } + + hdr = net_buf_simple_pull_u8(buf); + + if (rx->ctl) { + return ctl_recv(rx, hdr, buf, seq_auth); + } else { + /* SDUs must match a local element or an LPN of this Friend. */ + if (!rx->local_match && !rx->friend_match) { + return 0; + } + + return sdu_recv(rx, rx->seq, hdr, 0, buf); + } +} + +static inline s32_t ack_timeout(struct seg_rx *rx) +{ + s32_t to; + u8_t ttl; + + if (rx->ttl == BLE_MESH_TTL_DEFAULT) { + ttl = bt_mesh_default_ttl_get(); + } else { + ttl = rx->ttl; + } + + /* The acknowledgment timer shall be set to a minimum of + * 150 + 50 * TTL milliseconds. + */ + to = K_MSEC(150 + (ttl * 50U)); + + /* 100 ms for every not yet received segment */ + to += K_MSEC(((rx->seg_n + 1) - popcount(rx->block)) * 100U); + + /* Make sure we don't send more frequently than the duration for + * each packet (default is 300ms). + */ + return MAX(to, K_MSEC(400)); +} + +int bt_mesh_ctl_send(struct bt_mesh_net_tx *tx, u8_t ctl_op, void *data, + size_t data_len, u64_t *seq_auth, + const struct bt_mesh_send_cb *cb, void *cb_data) +{ + struct net_buf *buf; + + BT_DBG("src 0x%04x dst 0x%04x ttl 0x%02x ctl 0x%02x", tx->src, + tx->ctx->addr, tx->ctx->send_ttl, ctl_op); + BT_DBG("len %u: %s", data_len, bt_hex(data, data_len)); + + buf = bt_mesh_adv_create(BLE_MESH_ADV_DATA, tx->xmit, BUF_TIMEOUT); + if (!buf) { + BT_ERR("%s, Out of transport buffers", __func__); + return -ENOBUFS; + } + + net_buf_reserve(buf, BLE_MESH_NET_HDR_LEN); + + net_buf_add_u8(buf, TRANS_CTL_HDR(ctl_op, 0)); + + net_buf_add_mem(buf, data, data_len); + + if (IS_ENABLED(CONFIG_BLE_MESH_FRIEND)) { + if (bt_mesh_friend_enqueue_tx(tx, BLE_MESH_FRIEND_PDU_SINGLE, + seq_auth, &buf->b) && + BLE_MESH_ADDR_IS_UNICAST(tx->ctx->addr)) { + /* PDUs for a specific Friend should only go + * out through the Friend Queue. + */ + net_buf_unref(buf); + return 0; + } + } + + return bt_mesh_net_send(tx, buf, cb, cb_data); +} + +static int send_ack(struct bt_mesh_subnet *sub, u16_t src, u16_t dst, + u8_t ttl, u64_t *seq_auth, u32_t block, u8_t obo) +{ + struct bt_mesh_msg_ctx ctx = { + .net_idx = sub->net_idx, + .app_idx = BLE_MESH_KEY_UNUSED, + .addr = dst, + .send_ttl = ttl, + }; + struct bt_mesh_net_tx tx = { + .sub = sub, + .ctx = &ctx, + .src = obo ? bt_mesh_primary_addr() : src, + .xmit = bt_mesh_net_transmit_get(), + }; + u16_t seq_zero = *seq_auth & 0x1fff; + u8_t buf[6]; + + BT_DBG("SeqZero 0x%04x Block 0x%08x OBO %u", seq_zero, block, obo); + + if (bt_mesh_lpn_established()) { + BT_WARN("Not sending ack when LPN is enabled"); + return 0; + } + + /* This can happen if the segmented message was destined for a group + * or virtual address. + */ + if (!BLE_MESH_ADDR_IS_UNICAST(src)) { + BT_WARN("Not sending ack for non-unicast address"); + return 0; + } + + sys_put_be16(((seq_zero << 2) & 0x7ffc) | (obo << 15), buf); + sys_put_be32(block, &buf[2]); + + return bt_mesh_ctl_send(&tx, TRANS_CTL_OP_ACK, buf, sizeof(buf), + NULL, NULL, NULL); +} + +static void seg_rx_reset(struct seg_rx *rx, bool full_reset) +{ + BT_DBG("rx %p", rx); + + k_delayed_work_cancel(&rx->ack); + + if (IS_ENABLED(CONFIG_BLE_MESH_FRIEND) && rx->obo && + rx->block != BLOCK_COMPLETE(rx->seg_n)) { + BT_WARN("Clearing incomplete buffers from Friend queue"); + bt_mesh_friend_clear_incomplete(rx->sub, rx->src, rx->dst, + &rx->seq_auth); + } + + rx->in_use = 0U; + + /* We don't always reset these values since we need to be able to + * send an ack if we receive a segment after we've already received + * the full SDU. + */ + if (full_reset) { + rx->seq_auth = 0U; + rx->sub = NULL; + rx->src = BLE_MESH_ADDR_UNASSIGNED; + rx->dst = BLE_MESH_ADDR_UNASSIGNED; + } +} + +static void seg_ack(struct k_work *work) +{ + struct seg_rx *rx = CONTAINER_OF(work, struct seg_rx, ack); + + BT_DBG("rx %p", rx); + + if (k_uptime_get_32() - rx->last > K_SECONDS(60)) { + BT_WARN("Incomplete timer expired"); + seg_rx_reset(rx, false); + return; + } + + send_ack(rx->sub, rx->dst, rx->src, rx->ttl, &rx->seq_auth, + rx->block, rx->obo); + + k_delayed_work_submit(&rx->ack, ack_timeout(rx)); +} + +static inline u8_t seg_len(bool ctl) +{ + if (ctl) { + return 8; + } else { + return 12; + } +} + +static inline bool sdu_len_is_ok(bool ctl, u8_t seg_n) +{ + return ((seg_n * seg_len(ctl) + 1) <= CONFIG_BLE_MESH_RX_SDU_MAX); +} + +static struct seg_rx *seg_rx_find(struct bt_mesh_net_rx *net_rx, + const u64_t *seq_auth) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(seg_rx); i++) { + struct seg_rx *rx = &seg_rx[i]; + + if (rx->src != net_rx->ctx.addr || + rx->dst != net_rx->ctx.recv_dst) { + continue; + } + + /* Return newer RX context in addition to an exact match, so + * the calling function can properly discard an old SeqAuth. + * Note: in Zephyr v1.14.0, ">=" is used here which does not + * seem to be a right operation, hence we still use the original + * "==" here. + */ + if (rx->seq_auth == *seq_auth) { + return rx; + } + + if (rx->in_use) { + BT_WARN("Duplicate SDU from src 0x%04x", + net_rx->ctx.addr); + + /* Clear out the old context since the sender + * has apparently started sending a new SDU. + */ + seg_rx_reset(rx, true); + + /* Return non-match so caller can re-allocate */ + return NULL; + } + } + + return NULL; +} + +static bool seg_rx_is_valid(struct seg_rx *rx, struct bt_mesh_net_rx *net_rx, + const u8_t *hdr, u8_t seg_n) +{ + if (rx->hdr != *hdr || rx->seg_n != seg_n) { + BT_ERR("%s, Invalid segment for ongoing session", __func__); + return false; + } + + if (rx->src != net_rx->ctx.addr || rx->dst != net_rx->ctx.recv_dst) { + BT_ERR("%s, Invalid source or destination for segment", __func__); + return false; + } + + if (rx->ctl != net_rx->ctl) { + BT_ERR("%s, Inconsistent CTL in segment", __func__); + return false; + } + + return true; +} + +static struct seg_rx *seg_rx_alloc(struct bt_mesh_net_rx *net_rx, + const u8_t *hdr, const u64_t *seq_auth, + u8_t seg_n) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(seg_rx); i++) { + struct seg_rx *rx = &seg_rx[i]; + + if (rx->in_use) { + continue; + } + + rx->in_use = 1U; + net_buf_simple_reset(&rx->buf); + rx->sub = net_rx->sub; + rx->ctl = net_rx->ctl; + rx->seq_auth = *seq_auth; + rx->seg_n = seg_n; + rx->hdr = *hdr; + rx->ttl = net_rx->ctx.send_ttl; + rx->src = net_rx->ctx.addr; + rx->dst = net_rx->ctx.recv_dst; + rx->block = 0U; + + BT_DBG("New RX context. Block Complete 0x%08x", + BLOCK_COMPLETE(seg_n)); + + return rx; + } + + return NULL; +} + +static int trans_seg(struct net_buf_simple *buf, struct bt_mesh_net_rx *net_rx, + enum bt_mesh_friend_pdu_type *pdu_type, u64_t *seq_auth) +{ + struct seg_rx *rx; + u8_t *hdr = buf->data; + u16_t seq_zero; + u8_t seg_n; + u8_t seg_o; + int err; + + if (buf->len < 5) { + BT_ERR("%s, Too short segmented message (len %u)", __func__, buf->len); + return -EINVAL; + } + + BT_DBG("ASZMIC %u AKF %u AID 0x%02x", ASZMIC(hdr), AKF(hdr), AID(hdr)); + + net_buf_simple_pull(buf, 1); + + seq_zero = net_buf_simple_pull_be16(buf); + seg_o = (seq_zero & 0x03) << 3; + seq_zero = (seq_zero >> 2) & 0x1fff; + seg_n = net_buf_simple_pull_u8(buf); + seg_o |= seg_n >> 5; + seg_n &= 0x1f; + + BT_DBG("SeqZero 0x%04x SegO %u SegN %u", seq_zero, seg_o, seg_n); + + if (seg_o > seg_n) { + BT_ERR("%s, SegO greater than SegN (%u > %u)", __func__, seg_o, seg_n); + return -EINVAL; + } + + /* According to Mesh 1.0 specification: + * "The SeqAuth is composed of the IV Index and the sequence number + * (SEQ) of the first segment" + * + * Therefore we need to calculate very first SEQ in order to find + * seqAuth. We can calculate as below: + * + * SEQ(0) = SEQ(n) - (delta between seqZero and SEQ(n) by looking into + * 14 least significant bits of SEQ(n)) + * + * Mentioned delta shall be >= 0, if it is not then seq_auth will + * be broken and it will be verified by the code below. + */ + *seq_auth = SEQ_AUTH(BLE_MESH_NET_IVI_RX(net_rx), + (net_rx->seq - + ((((net_rx->seq & BIT_MASK(14)) - seq_zero)) & + BIT_MASK(13)))); + + /* Look for old RX sessions */ + rx = seg_rx_find(net_rx, seq_auth); + if (rx) { + /* Discard old SeqAuth packet */ + if (rx->seq_auth > *seq_auth) { + BT_WARN("Ignoring old SeqAuth"); + return -EINVAL; + } + + if (!seg_rx_is_valid(rx, net_rx, hdr, seg_n)) { + return -EINVAL; + } + + if (rx->in_use) { + BT_DBG("Existing RX context. Block 0x%08x", rx->block); + goto found_rx; + } + + if (rx->block == BLOCK_COMPLETE(rx->seg_n)) { + BT_WARN("Got segment for already complete SDU"); + send_ack(net_rx->sub, net_rx->ctx.recv_dst, + net_rx->ctx.addr, net_rx->ctx.send_ttl, + seq_auth, rx->block, rx->obo); + return -EALREADY; + } + + /* We ignore instead of sending block ack 0 since the + * ack timer is always smaller than the incomplete + * timer, i.e. the sender is misbehaving. + */ + BT_WARN("Got segment for canceled SDU"); + return -EINVAL; + } + + /* Bail out early if we're not ready to receive such a large SDU */ + if (!sdu_len_is_ok(net_rx->ctl, seg_n)) { + BT_ERR("%s, Too big incoming SDU length", __func__); + send_ack(net_rx->sub, net_rx->ctx.recv_dst, net_rx->ctx.addr, + net_rx->ctx.send_ttl, seq_auth, 0, + net_rx->friend_match); + return -EMSGSIZE; + } + + /* Look for free slot for a new RX session */ + rx = seg_rx_alloc(net_rx, hdr, seq_auth, seg_n); + if (!rx) { + /* Warn but don't cancel since the existing slots willl + * eventually be freed up and we'll be able to process + * this one. + */ + BT_WARN("No free slots for new incoming segmented messages"); + return -ENOMEM; + } + + rx->obo = net_rx->friend_match; + +found_rx: + if (BIT(seg_o) & rx->block) { + BT_WARN("Received already received fragment"); + return -EALREADY; + } + + /* All segments, except the last one, must either have 8 bytes of + * payload (for 64bit Net MIC) or 12 bytes of payload (for 32bit + * Net MIC). + */ + if (seg_o == seg_n) { + /* Set the expected final buffer length */ + rx->buf.len = seg_n * seg_len(rx->ctl) + buf->len; + BT_DBG("Target len %u * %u + %u = %u", seg_n, seg_len(rx->ctl), + buf->len, rx->buf.len); + + if (rx->buf.len > CONFIG_BLE_MESH_RX_SDU_MAX) { + BT_ERR("Too large SDU len"); + send_ack(net_rx->sub, net_rx->ctx.recv_dst, + net_rx->ctx.addr, net_rx->ctx.send_ttl, + seq_auth, 0, rx->obo); + seg_rx_reset(rx, true); + return -EMSGSIZE; + } + } else { + if (buf->len != seg_len(rx->ctl)) { + BT_ERR("%s, Incorrect segment size for message type", __func__); + return -EINVAL; + } + } + + /* Reset the Incomplete Timer */ + rx->last = k_uptime_get_32(); + + if (!k_delayed_work_remaining_get(&rx->ack) && + !bt_mesh_lpn_established()) { + k_delayed_work_submit(&rx->ack, ack_timeout(rx)); + } + + /* Location in buffer can be calculated based on seg_o & rx->ctl */ + memcpy(rx->buf.data + (seg_o * seg_len(rx->ctl)), buf->data, buf->len); + + BT_DBG("Received %u/%u", seg_o, seg_n); + + /* Mark segment as received */ + rx->block |= BIT(seg_o); + + if (rx->block != BLOCK_COMPLETE(seg_n)) { + *pdu_type = BLE_MESH_FRIEND_PDU_PARTIAL; + return 0; + } + + BT_DBG("Complete SDU"); + + if (net_rx->local_match && is_replay(net_rx)) { + BT_WARN("Replay: src 0x%04x dst 0x%04x seq 0x%06x", + net_rx->ctx.addr, net_rx->ctx.recv_dst, net_rx->seq); + /* Clear the segment's bit */ + rx->block &= ~BIT(seg_o); + return -EINVAL; + } + + *pdu_type = BLE_MESH_FRIEND_PDU_COMPLETE; + + k_delayed_work_cancel(&rx->ack); + send_ack(net_rx->sub, net_rx->ctx.recv_dst, net_rx->ctx.addr, + net_rx->ctx.send_ttl, seq_auth, rx->block, rx->obo); + + if (net_rx->ctl) { + err = ctl_recv(net_rx, *hdr, &rx->buf, seq_auth); + } else { + err = sdu_recv(net_rx, (rx->seq_auth & 0xffffff), *hdr, + ASZMIC(hdr), &rx->buf); + } + + seg_rx_reset(rx, false); + + return err; +} + +int bt_mesh_trans_recv(struct net_buf_simple *buf, struct bt_mesh_net_rx *rx) +{ + u64_t seq_auth = TRANS_SEQ_AUTH_NVAL; + enum bt_mesh_friend_pdu_type pdu_type = BLE_MESH_FRIEND_PDU_SINGLE; + struct net_buf_simple_state state; + int err; + + if (IS_ENABLED(CONFIG_BLE_MESH_FRIEND)) { + rx->friend_match = bt_mesh_friend_match(rx->sub->net_idx, + rx->ctx.recv_dst); + } else { + rx->friend_match = false; + } + + BT_DBG("src 0x%04x dst 0x%04x seq 0x%08x friend_match %u", + rx->ctx.addr, rx->ctx.recv_dst, rx->seq, rx->friend_match); + + /* Remove network headers */ + net_buf_simple_pull(buf, BLE_MESH_NET_HDR_LEN); + + BT_DBG("Payload %s", bt_hex(buf->data, buf->len)); + + /* If LPN mode is enabled messages are only accepted when we've + * requested the Friend to send them. The messages must also + * be encrypted using the Friend Credentials. + */ + if (IS_ENABLED(CONFIG_BLE_MESH_NODE) && bt_mesh_is_provisioned()) { + if (IS_ENABLED(CONFIG_BLE_MESH_LOW_POWER) && + bt_mesh_lpn_established() && rx->net_if == BLE_MESH_NET_IF_ADV && + (!bt_mesh_lpn_waiting_update() || !rx->friend_cred)) { + BT_WARN("Ignoring unexpected message in Low Power mode"); + return -EAGAIN; + } + } + + /* Save the app-level state so the buffer can later be placed in + * the Friend Queue. + */ + net_buf_simple_save(buf, &state); + + if (SEG(buf->data)) { + /* Segmented messages must match a local element or an + * LPN of this Friend. + */ + if (!rx->local_match && !rx->friend_match) { + return 0; + } + + err = trans_seg(buf, rx, &pdu_type, &seq_auth); + } else { + err = trans_unseg(buf, rx, &seq_auth); + } + + /* Notify LPN state machine so a Friend Poll will be sent. If the + * message was a Friend Update it's possible that a Poll was already + * queued for sending, however that's fine since then the + * bt_mesh_lpn_waiting_update() function will return false: + * we still need to go through the actual sending to the bearer and + * wait for ReceiveDelay before transitioning to WAIT_UPDATE state. + * Another situation where we want to notify the LPN state machine + * is if it's configured to use an automatic Friendship establishment + * timer, in which case we want to reset the timer at this point. + * + */ + if (IS_ENABLED(CONFIG_BLE_MESH_NODE) && bt_mesh_is_provisioned()) { + if (IS_ENABLED(CONFIG_BLE_MESH_LOW_POWER) && + (bt_mesh_lpn_timer() || + (bt_mesh_lpn_established() && bt_mesh_lpn_waiting_update()))) { + bt_mesh_lpn_msg_received(rx); + } + } + + net_buf_simple_restore(buf, &state); + + if (IS_ENABLED(CONFIG_BLE_MESH_NODE) && bt_mesh_is_provisioned()) { + if (IS_ENABLED(CONFIG_BLE_MESH_FRIEND) && rx->friend_match && !err) { + if (seq_auth == TRANS_SEQ_AUTH_NVAL) { + bt_mesh_friend_enqueue_rx(rx, pdu_type, NULL, buf); + } else { + bt_mesh_friend_enqueue_rx(rx, pdu_type, &seq_auth, buf); + } + } + } + + return err; +} + +void bt_mesh_rx_reset(void) +{ + int i; + + BT_DBG("%s", __func__); + + for (i = 0; i < ARRAY_SIZE(seg_rx); i++) { + seg_rx_reset(&seg_rx[i], true); + } + + if (IS_ENABLED(CONFIG_BLE_MESH_SETTINGS)) { + bt_mesh_clear_rpl(); + } else { + (void)memset(bt_mesh.rpl, 0, sizeof(bt_mesh.rpl)); + } +} + +void bt_mesh_tx_reset(void) +{ + int i; + + BT_DBG("%s", __func__); + + for (i = 0; i < ARRAY_SIZE(seg_tx); i++) { + seg_tx_reset(&seg_tx[i]); + } +} + +void bt_mesh_trans_init(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(seg_tx); i++) { + k_delayed_work_init(&seg_tx[i].retransmit, seg_retransmit); + } + + for (i = 0; i < ARRAY_SIZE(seg_rx); i++) { + k_delayed_work_init(&seg_rx[i].ack, seg_ack); + seg_rx[i].buf.__buf = (seg_rx_buf_data + + (i * CONFIG_BLE_MESH_RX_SDU_MAX)); + seg_rx[i].buf.data = seg_rx[i].buf.__buf; + } +} + +void bt_mesh_rpl_clear(void) +{ + BT_DBG("%s", __func__); + (void)memset(bt_mesh.rpl, 0, sizeof(bt_mesh.rpl)); +} diff --git a/components/bt/ble_mesh/mesh_core/transport.h b/components/bt/ble_mesh/mesh_core/transport.h new file mode 100644 index 0000000000..13845f345c --- /dev/null +++ b/components/bt/ble_mesh/mesh_core/transport.h @@ -0,0 +1,102 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2017 Intel Corporation + * Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _TRANSPORT_H_ +#define _TRANSPORT_H_ + +#define TRANS_SEQ_AUTH_NVAL 0xffffffffffffffff + +#define BLE_MESH_TX_SDU_MAX (CONFIG_BLE_MESH_TX_SEG_MAX * 12) + +#define TRANS_CTL_OP_MASK ((u8_t)BIT_MASK(7)) +#define TRANS_CTL_OP(data) ((data)[0] & TRANS_CTL_OP_MASK) +#define TRANS_CTL_HDR(op, seg) ((op & TRANS_CTL_OP_MASK) | (seg << 7)) + +#define TRANS_CTL_OP_ACK 0x00 +#define TRANS_CTL_OP_FRIEND_POLL 0x01 +#define TRANS_CTL_OP_FRIEND_UPDATE 0x02 +#define TRANS_CTL_OP_FRIEND_REQ 0x03 +#define TRANS_CTL_OP_FRIEND_OFFER 0x04 +#define TRANS_CTL_OP_FRIEND_CLEAR 0x05 +#define TRANS_CTL_OP_FRIEND_CLEAR_CFM 0x06 +#define TRANS_CTL_OP_FRIEND_SUB_ADD 0x07 +#define TRANS_CTL_OP_FRIEND_SUB_REM 0x08 +#define TRANS_CTL_OP_FRIEND_SUB_CFM 0x09 +#define TRANS_CTL_OP_HEARTBEAT 0x0a + +struct bt_mesh_ctl_friend_poll { + u8_t fsn; +} __packed; + +struct bt_mesh_ctl_friend_update { + u8_t flags; + u32_t iv_index; + u8_t md; +} __packed; + +struct bt_mesh_ctl_friend_req { + u8_t criteria; + u8_t recv_delay; + u8_t poll_to[3]; + u16_t prev_addr; + u8_t num_elem; + u16_t lpn_counter; +} __packed; + +struct bt_mesh_ctl_friend_offer { + u8_t recv_win; + u8_t queue_size; + u8_t sub_list_size; + s8_t rssi; + u16_t frnd_counter; +} __packed; + +struct bt_mesh_ctl_friend_clear { + u16_t lpn_addr; + u16_t lpn_counter; +} __packed; + +struct bt_mesh_ctl_friend_clear_confirm { + u16_t lpn_addr; + u16_t lpn_counter; +} __packed; + +#define BLE_MESH_FRIEND_SUB_MIN_LEN (1 + 2) +struct bt_mesh_ctl_friend_sub { + u8_t xact; + u16_t addr_list[5]; +} __packed; + +struct bt_mesh_ctl_friend_sub_confirm { + u8_t xact; +} __packed; + +void bt_mesh_set_hb_sub_dst(u16_t addr); + +struct bt_mesh_app_key *bt_mesh_app_key_find(u16_t app_idx); + +bool bt_mesh_tx_in_progress(void); + +void bt_mesh_rx_reset(void); +void bt_mesh_tx_reset(void); + +int bt_mesh_ctl_send(struct bt_mesh_net_tx *tx, u8_t ctl_op, void *data, + size_t data_len, u64_t *seq_auth, + const struct bt_mesh_send_cb *cb, void *cb_data); + +int bt_mesh_trans_send(struct bt_mesh_net_tx *tx, struct net_buf_simple *msg, + const struct bt_mesh_send_cb *cb, void *cb_data); + +int bt_mesh_trans_recv(struct net_buf_simple *buf, struct bt_mesh_net_rx *rx); + +void bt_mesh_trans_init(void); + +void bt_mesh_rpl_clear(void); + +#endif /* _TRANSPORT_H_ */ diff --git a/components/bt/ble_mesh/mesh_docs/BLE-Mesh_FAQs_EN.md b/components/bt/ble_mesh/mesh_docs/BLE-Mesh_FAQs_EN.md new file mode 100644 index 0000000000..3e3ba69684 --- /dev/null +++ b/components/bt/ble_mesh/mesh_docs/BLE-Mesh_FAQs_EN.md @@ -0,0 +1,9 @@ +# Frequently Asked Questions + +## General Questions + +### Why I do not get a reply from the remote device when I perform get operation immediately after set operation has been performed? +* Any Client Model operation needs to wait for the completion event of an ongoing operation. Once the completion event is received the next command can be executed. If a command is executed before the completion event is received, a timeout error will occur. + +### When I use the API `esp_ble_mesh_client_model_send_msg`, why does it crash with the log messages *Invalid client value when sent client msg* or *Invalid client value when sent client msg*? +* You should initialize a structure of the type `esp_ble_mesh_client_t` and set its value as the user data of client model. diff --git a/components/bt/ble_mesh/mesh_docs/BLE-Mesh_Feature_List_EN.md b/components/bt/ble_mesh/mesh_docs/BLE-Mesh_Feature_List_EN.md new file mode 100644 index 0000000000..cf54f7985e --- /dev/null +++ b/components/bt/ble_mesh/mesh_docs/BLE-Mesh_Feature_List_EN.md @@ -0,0 +1,89 @@ +# Espressif BLE Mesh Feature List + +## Currently Supported Features + +### Mesh Core + +* Provisioning: Node Role + * Advertising and GATT bearer + * Authentication OOB + +* Provisioning: Provisioner Role + * Advertising and GATT bearer + * Authentication OOB + +* Networking + * Relay + * Segmentation and Reassembly + * Key Refresh + * IV Update + +* Proxy Support + +* Multiple Client Models Run Simultaneously + * Support multiple client models send packets to different nodes simultaneously + * No blocking between client model and server + +* NVS Storing + * Store Provisioning Data of The Node Device + +### Mesh Applications + +* Fast Provisioning + * Fast Provisioning Server Model + * Fast Provisioning Client Model + * Example & Demo Video + +* Wi-Fi & BLE Mesh Coexistence + * Example & Demo Video(coming soon) + +* Mesh Console Commands + * Example + + +### Mesh Models + +* Foundation Models + * Configuration Server Model + * Configuration Client Model + * Health Server Model + * Health Client Model + +* Generic Client Models + * Generic OnOff Client + * Generic Level Client + * Generic Location Client + * Generic Default Transition Timer Client + * Generic Power OnOff Client + * Generic Power Level Client + * Generic Battery Client + * Generic Property Client + +* Generic Server Models + * Generic OnOff Server (Simple) + +* Lighting Client Models + * Light Lightness Client + * Light CTL Client + * Light HSL Client + +* Sensor Client Models + * Sensor Client + +* Time and Scenes Client Models + * Scene Client + + +## Future Release Features + +### Mesh Core + +* BLE Mesh BQB Certification +* Friend Feature +* Low Power Node Feature + +### Mesh Applications + +* Fast OTA + +### Mesh Models diff --git a/components/bt/ble_mesh/mesh_docs/BLE-Mesh_Getting_Started_EN.md b/components/bt/ble_mesh/mesh_docs/BLE-Mesh_Getting_Started_EN.md new file mode 100644 index 0000000000..5ac15427a7 --- /dev/null +++ b/components/bt/ble_mesh/mesh_docs/BLE-Mesh_Getting_Started_EN.md @@ -0,0 +1,155 @@ +# Introduction + +Bluetooth mesh networking enables many-to-many (m:m) device communications and is optimized for creating large-scale device networks. + +Devices may relay data to other devices not in direct radio range of the originating device. In this way, mesh networks can span very large physical areas and contain large numbers of devices. It is ideally suited for building automation, sensor networks, and other IoT solutions where tens, hundreds, or thousands of devices need to reliably and securely communicate with one another. + +Bluetooth mesh is not a wireless communications technology, but a networking technology. This technology is dependent upon Bluetooth Low Energy (BLE) - a wireless communications protocol stack. + + +# Specifications + +The official specifications for Bluetooth mesh can be found [here](https://www.bluetooth.com/specifications/mesh-specifications) + + +# Getting Started with BLE Mesh on ESP32 + +If you are new to ESP32, you may first need to go through the [Getting Started guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html). + +Built on top of Zephyr BLE Mesh stack, the ESP BLE Mesh implementation supports device provisioning and node control. It also supports such node features as Proxy, Relay, Low power and Friend. + + +## Access to ESP BLE Mesh +Since you are on this page, you should already have access to Espressif BLE Mesh SDK. If you do not have access, please get in touch with your point of contact. + +## Documentation + +The ESP BLE Mesh code in the SDK is organized as below. Each folder contains source files related to it as well as a subfolder with header files for the exposed functionality. + +``` +$tree components/bt/ble_mesh/ + +├── api /* BLE Mesh functionality exposed through esp_ble_mesh_* APIs for the applications */ +│   ├── core /* BLE Mesh Core APIs */ +│   │   └── include +│   └── models /* Foundation Models and other Client Models APIs */ +│   └── include +├── btc +│   └── include +├── mesh_core /* BLE mesh core based on Zephyr BLE stack with miscellaneous modifications and +│ │ an adaptation layer to make it work with ESP32 */ +│   └── include +├── mesh_docs /* BLE Mesh docs */ +└── mesh_models /* Foundation Models and other Client Models implementations */ + └── include +``` + +To demonstrate the features supported by BLE Mesh SDK, a few sample examples have been added. Each example has a README.md file for quick start as well as a walkthrough file that explains the functionality in detail. + +Below is a snapshot of the BLE Mesh examples directory + +``` +$ tree examples/bluetooth/ble_mesh/ +├── ble_mesh_client_model +│   ├── main +│   │   ├── ble_mesh_client_model_main.c +│   │   ├── board.c +│   │   ├── board.h +│   │   ├── component.mk +│   │   └── Kconfig.projbuild +│   ├── Makefile +│   ├── README.md +│   └── sdkconfig.defaults +├── ble_mesh_node +│   ├── main +│   │   ├── ble_mesh_demo_main.c +│   │   ├── board.c +│   │   ├── board.h +│   │   ├── component.mk +│   │   └── Kconfig.projbuild +│   ├── Makefile +│   ├── README.md +│   ├── sdkconfig.defaults +│   └── tutorial +│   └── Ble_Mesh_Node_Example_Walkthrough.md +├── ble_mesh_provisioner +│ ├── main +│ │   ├── ble_mesh_demo_main.c +│ │   ├── board.c +│ │   ├── board.h +│ │   ├── component.mk +│ │   └── Kconfig.projbuild +│ ├── Makefile +│ ├── README.md +│ ├── sdkconfig.defaults +│ └── tutorial +│ └── Ble_Mesh_Provisioner_Example_Walkthrough.md +├──ble_mesh_console +│ ├── ble_mesh_node +│ └── ble_mesh_provisioner +├──ble_mesh_fast_provision +│ ├── ble_mesh_fast_prov_client +│ └── ble_mesh_fast_prov_server +├──ble_mesh_vendor_models +│ ├── fast_prov_vendor_model +└──ble_mesh_wifi_coexist + ├── main + ├── components + └── tutorial + └── ble_mesh_wifi_coexist.md +8 directories, 26 files +``` + + +## Hardware and Setup + +At present ESP32-DevKitC and ESP-WROVER-KIT are supported for BLE Mesh implementation. You can find the details about the modules [here](https://docs.espressif.com/projects/esp-idf/en/latest/hw-reference/modules-and-boards.html) + +You can choose the board through menuconfig: `make menuconfig -> Example Configuration -> Board selection for BLE Mesh` + +Note that if you plan to use ESP32-DevKitC, you need to connect an RGB LED to GPIO pins 25, 26 and 27. + + +## Sample Examples + +* BLE Mesh Node + +This example shows the use of BLE Mesh as a node device having a Configuration Server model and a Generic OnOff Server model. A BLE Mesh provisioner can then provision the node and control a RGB LED representing on/off state. + +* BLE Mesh Client Model + +This example shows how a Generic OnOff Client model within a node works. The node has a Configuration Server model, a Generic OnOff Server model and a Generic OnOff Client model. + +* BLE Mesh Provisioner + +This example shows how a device can act as a BLE Mesh provisioner to provision devices. The provisioner has a Configuration Server model, a Configuration Client model and a Generic OnOff Client model. + + +## Mobile Apps + +ESP BLE Mesh implementation is compatible with a few phone apps, including Silicon Labs BLE Mesh and nRF Mesh. These apps are available on Google Play and App Store. In addition, Espressif offers its own Android app which is currently being actively developed. You can find the latest APK file [here](http://download.espressif.com/BLE_MESH/BLE_Mesh_Tools/BLE_Mesh_App/EspBleMesh-0.9.4.apk). + +Note: The most recent tested version 1.1.0 of Silicon Labs App has a bug, which has been fixed by a workaround on the SDK side. The fix is implemented through a configuration option enabled by default. For other Android/iOS apps, this option needs to be disabled from menuconfig: +`make menuconfig -> Example Configuration -> This option fixes the bug of Silicon Lab Android App 1.1.0 when reconnection will cause the sequence number to recount from 0` + +## Building and Flashing + +If you build the application for the first time, the menuconfig screen will pop up. You can choose the board from the Example Configuration option. Additionally, you can modify the serial settings in the Serial flasher config option in accordance with your port configuration. + +BLE Mesh specific configuration options can also be modified through: `make menuconfig -> Component config -> Bluetooth Mesh support` + +You can still change options at any other time using `make menuconfig`. + +``` +$ export IDF_PATH=/path/to/esp-ble-mesh-sdk-v0.x + +$ cd examples/bluetooth/ble_mesh/ + +$ make -j8 flash monitor +``` + + +# Reporting Issues + +* If you find a bug or have a feature request, go to [the Issues section on GitHub](https://github.com/espressif/esp-idf/issues). Before reporting a new issue, please check the existing issues at the provided link and the FAQs document in the `mesh_docs` folder. +* When you submit an issue or a feature request on GitHub, please add the tag "BLE Mesh" in the issue title for our faster reaction. diff --git a/components/bt/ble_mesh/mesh_docs/BLE-Mesh_Known_Issues_EN.md b/components/bt/ble_mesh/mesh_docs/BLE-Mesh_Known_Issues_EN.md new file mode 100644 index 0000000000..034d2388d3 --- /dev/null +++ b/components/bt/ble_mesh/mesh_docs/BLE-Mesh_Known_Issues_EN.md @@ -0,0 +1 @@ +To be added. diff --git a/components/bt/ble_mesh/mesh_docs/README.md b/components/bt/ble_mesh/mesh_docs/README.md new file mode 100644 index 0000000000..65775ebf98 --- /dev/null +++ b/components/bt/ble_mesh/mesh_docs/README.md @@ -0,0 +1,50 @@ +# ESP BLE Mesh Framework + +This folder contains all the documents of ESP BLE Mesh. +* Note: breaking changes might be introduced into ESP BLE Mesh on [minor IDF versions](https://docs.espressif.com/projects/esp-idf/en/latest/versions.html) + + +## Demos + +* [Provisioning of BLE Mesh nodes using Smartphone App](http://download.espressif.com/BLE_MESH/Docs4Customers/esp-ble-mesh-demo.mp4) +* [Espressif Fast Provisioning using ESP BLE Mesh App](http://download.espressif.com/BLE_MESH/BLE_Mesh_Demo/V0.4_Demo_Fast_Provision/ESP32_BLE_Mesh_Fast_Provision.mp4) +* [Espressif BLE Mesh and Wi-Fi Coexistence](http://download.espressif.com/BLE_MESH/BLE_Mesh_Demo/V0.5_Demo_Coexistence/ESP_BLE_MESH_%26_WIFI_Coexistence.mp4) + +## Examples + +* [BLE Mesh Node Example Code](../../../../examples/bluetooth/ble_mesh/ble_mesh_node) +* [BLE_Mesh_Node_Example_Walkthrough](../../../../examples/bluetooth/ble_mesh/ble_mesh_node/tutorial/Ble_Mesh_Node_Example_Walkthrough.md) +* [BLE Mesh Provisioner Example Code](../../../../examples/bluetooth/ble_mesh/ble_mesh_provisioner) +* [BLE_Mesh_Provisioner_Example_Walkthrough](../../../../examples/bluetooth/ble_mesh/ble_mesh_provisioner/tutorial/Ble_Mesh_Provisioner_Example_Walkthrough.md) +* [BLE Mesh Client Model Example Code](../../../../examples/bluetooth/ble_mesh/ble_mesh_client_model) +* [BLE_Mesh_Client_Model_Example_Walkthrough](../../../../examples/bluetooth/ble_mesh/ble_mesh_client_model/tutorial/ble_mesh_client_model.md) +* [BLE Mesh Console Example Code](../../../../examples/bluetooth/ble_mesh/ble_mesh_console) +* [BLE Mesh Fast Prov Client Example Code](../../../../examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_client) +* [BLE_Mesh_Fast_Prov_Client_Example_Walkthrough](../../../../examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_client/tutorial/ble_mesh_fast_provision_client.md) +* [BLE Mesh Fast Prov Server Example Code](../../../../examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server) +* [BLE_Mesh_Fast_Prov_Server_Example_Walkthrough](../../../../examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/tutorial/ble_mesh_fast_provision_server.md) +* [BLE Mesh Wifi Coexist Example Code](../../../../examples/bluetooth/ble_mesh/ble_mesh_wifi_coexist) +* [BLE_Mesh_Wifi_Coexist_Example_Walkthrough](../../../../examples/bluetooth/ble_mesh/ble_mesh_wifi_coexist/tutorial%20%20%20%20%20%20/ble_mesh_wifi_coexist.md) +## Documentation + +### ESP BLE Mesh Development Documentation + +* [Getting started with ESP BLE Mesh](BLE-Mesh_Getting_Started_EN.md) +* [ESP BLE Mesh Feature List](BLE-Mesh_Feature_List_EN.md) +* [FAQs](BLE-Mesh_FAQs_EN.md) +* [Known Issues](BLE-Mesh_Known_Issues_EN.md) + +### BLE Mesh Protocol Documentation + +* [BLE Mesh Core Specification](https://www.bluetooth.org/docman/handlers/downloaddoc.ashx?doc_id=429633) +* [BLE Mesh Model Specification](https://www.bluetooth.org/docman/handlers/downloaddoc.ashx?doc_id=429634) +* [An Intro to Bluetooth Mesh Part 1](http://blog.bluetooth.com/an-intro-to-bluetooth-mesh-part1) +* [An Intro to Bluetooth Mesh Part 2](http://blog.bluetooth.com/an-intro-to-bluetooth-mesh-part2) +* [The Fundamental Concepts of Bluetooth Mesh Networking Part 1](http://blog.bluetooth.com/the-fundamental-concepts-of-bluetooth-mesh-networking-part-1) +* [The Fundamental Concepts of Bluetooth Mesh Networking, Part 2](http://blog.bluetooth.com/the-fundamental-concepts-of-bluetooth-mesh-networking-part-2) +* [Bluetooth Mesh Networking: Friendship](http://blog.bluetooth.com/bluetooth-mesh-networking-series-friendship) +* [Management of Devices in a Bluetooth Mesh Network](http://blog.bluetooth.com/management-of-devices-bluetooth-mesh-network) +* [Bluetooth Mesh Security Overview](http://blog.bluetooth.com/bluetooth-mesh-security-overview) +* [Provisioning a Bluetooth Mesh Network Part 1](http://blog.bluetooth.com/provisioning-a-bluetooth-mesh-network-part-1) +* [Provisioning a Bluetooth Mesh Network Part 2](http://blog.bluetooth.com/provisioning-a-bluetooth-mesh-network-part-2) + diff --git a/components/bt/ble_mesh/mesh_models/generic_client.c b/components/bt/ble_mesh/mesh_models/generic_client.c new file mode 100644 index 0000000000..7aeb5d00d3 --- /dev/null +++ b/components/bt/ble_mesh/mesh_models/generic_client.c @@ -0,0 +1,1225 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 +#include + +#include "osi/allocator.h" +#include "sdkconfig.h" + +#include "mesh_types.h" +#include "mesh_kernel.h" +#include "mesh_trace.h" + +#include "mesh.h" +#include "model_opcode.h" +#include "mesh_common.h" +#include "generic_client.h" + +#include "btc_ble_mesh_generic_model.h" + +/** The following are the macro definitions of generic client + * model messages length, and a message is composed of three + * parts: Opcode + msg_value + MIC + */ +/* Generic onoff client messages length */ +#define BLE_MESH_GEN_ONOFF_GET_MSG_LEN (2 + 0 + 4) +#define BLE_MESH_GEN_ONOFF_SET_MSG_LEN (2 + 4 + 4) + +/* Generic level client messages length */ +#define BLE_MESH_GEN_LEVEL_GET_MSG_LEN (2 + 0 + 4) +#define BLE_MESH_GEN_LEVEL_SET_MSG_LEN (2 + 5 + 4) +#define BLE_MESH_GEN_DELTA_SET_MSG_LEN (2 + 7 + 4) +#define BLE_MESH_GEN_MOVE_SET_MSG_LEN (2 + 5 + 4) + +/* Generic default transition time client messages length */ +#define BLE_MESH_GEN_DEF_TRANS_TIME_GET_MSG_LEN (2 + 0 + 4) +#define BLE_MESH_GEN_DEF_TRANS_TIME_SET_MSG_LEN (2 + 1 + 4) + +/* Generic power onoff client messages length */ +#define BLE_MESH_GEN_ONPOWERUP_GET_MSG_LEN (2 + 0 + 4) +#define BLE_MESH_GEN_ONPOWERUP_SET_MSG_LEN (2 + 1 + 4) + +/* Generic power level client messages length */ +#define BLE_MESH_GEN_POWER_LEVEL_GET_MSG_LEN (2 + 0 + 4) +#define BLE_MESH_GEN_POWER_LEVEL_SET_MSG_LEN (2 + 5 + 4) +#define BLE_MESH_GEN_POWER_LAST_GET_MSG_LEN (2 + 0 + 4) +#define BLE_MESH_GEN_POWER_DEFAULT_GET_MSG_LEN (2 + 0 + 4) +#define BLE_MESH_GEN_POWER_DEFAULT_SET_MSG_LEN (2 + 2 + 4) +#define BLE_MESH_GEN_POWER_RANGE_GET_MSG_LEN (2 + 0 + 4) +#define BLE_MESH_GEN_POWER_RANGE_SET_MSG_LEN (2 + 4 + 4) + +/* Generic battery client messages length */ +#define BLE_MESH_GEN_BATTERY_GET_MSG_LEN (2 + 0 + 4) + +/* Generic location client messages length */ +#define BLE_MESH_GEN_LOC_GLOBAL_GET_MSG_LEN (2 + 0 + 4) +#define BLE_MESH_GEN_LOC_GLOBAL_SET_MSG_LEN (1 + 10 + 4) +#define BLE_MESH_GEN_LOC_LOCAL_GET_MSG_LEN (2 + 0 + 4) +#define BLE_MESH_GEN_LOC_LOCAL_SET_MSG_LEN (2 + 9 + 4) + +/* Generic property client messages length */ +#define BLE_MESH_GEN_USER_PROPERTIES_GET_MSG_LEN (2 + 0 + 4) +#define BLE_MESH_GEN_USER_PROPERTY_GET_MSG_LEN (2 + 2 + 4) +#define BLE_MESH_GEN_USER_PROPERTY_SET_MSG_LEN /* variable */ +#define BLE_MESH_GEN_ADMIN_PROPERTIES_GET_MSG_LEN (2 + 0 + 4) +#define BLE_MESH_GEN_ADMIN_PROPERTY_GET_MSG_LEN (2 + 2 + 4) +#define BLE_MESH_GEN_ADMIN_PROPERTY_SET_MSG_LEN /* variable */ +#define BLE_MESH_GEN_MANU_PROPERTIES_GET_MSG_LEN (2 + 0 + 4) +#define BLE_MESH_GEN_MANU_PROPERTY_GET_MSG_LEN (2 + 2 + 4) +#define BLE_MESH_GEN_MANU_PROPERTY_SET_MSG_LEN (1 + 3 + 4) +#define BLE_MESH_GEN_CLINET_PROPERTIES_GET_MSG_LEN (1 + 2 + 4) + +#define BLE_MESH_GEN_GET_STATE_MSG_LEN (2 + 2 + 4) + +static const bt_mesh_client_op_pair_t gen_op_pair[] = { + { BLE_MESH_MODEL_OP_GEN_ONOFF_GET, BLE_MESH_MODEL_OP_GEN_ONOFF_STATUS }, + { BLE_MESH_MODEL_OP_GEN_ONOFF_SET, BLE_MESH_MODEL_OP_GEN_ONOFF_STATUS }, + { BLE_MESH_MODEL_OP_GEN_LEVEL_GET, BLE_MESH_MODEL_OP_GEN_LEVEL_STATUS }, + { BLE_MESH_MODEL_OP_GEN_LEVEL_SET, BLE_MESH_MODEL_OP_GEN_LEVEL_STATUS }, + { BLE_MESH_MODEL_OP_GEN_DELTA_SET, BLE_MESH_MODEL_OP_GEN_LEVEL_STATUS }, + { BLE_MESH_MODEL_OP_GEN_MOVE_SET, BLE_MESH_MODEL_OP_GEN_LEVEL_STATUS }, + { BLE_MESH_MODEL_OP_GEN_DEF_TRANS_TIME_GET, BLE_MESH_MODEL_OP_GEN_DEF_TRANS_TIME_STATUS }, + { BLE_MESH_MODEL_OP_GEN_DEF_TRANS_TIME_SET, BLE_MESH_MODEL_OP_GEN_DEF_TRANS_TIME_STATUS }, + { BLE_MESH_MODEL_OP_GEN_ONPOWERUP_GET, BLE_MESH_MODEL_OP_GEN_ONPOWERUP_STATUS }, + { BLE_MESH_MODEL_OP_GEN_ONPOWERUP_SET, BLE_MESH_MODEL_OP_GEN_ONPOWERUP_STATUS }, + { BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_GET, BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_STATUS }, + { BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_SET, BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_STATUS }, + { BLE_MESH_MODEL_OP_GEN_POWER_LAST_GET, BLE_MESH_MODEL_OP_GEN_POWER_LAST_STATUS }, + { BLE_MESH_MODEL_OP_GEN_POWER_DEFAULT_GET, BLE_MESH_MODEL_OP_GEN_POWER_DEFAULT_STATUS }, + { BLE_MESH_MODEL_OP_GEN_POWER_DEFAULT_SET, BLE_MESH_MODEL_OP_GEN_POWER_DEFAULT_STATUS }, + { BLE_MESH_MODEL_OP_GEN_POWER_RANGE_GET, BLE_MESH_MODEL_OP_GEN_POWER_RANGE_STATUS }, + { BLE_MESH_MODEL_OP_GEN_POWER_RANGE_SET, BLE_MESH_MODEL_OP_GEN_POWER_RANGE_STATUS }, + { BLE_MESH_MODEL_OP_GEN_BATTERY_GET, BLE_MESH_MODEL_OP_GEN_BATTERY_STATUS }, + { BLE_MESH_MODEL_OP_GEN_LOC_GLOBAL_GET, BLE_MESH_MODEL_OP_GEN_LOC_GLOBAL_STATUS }, + { BLE_MESH_MODEL_OP_GEN_LOC_GLOBAL_SET, BLE_MESH_MODEL_OP_GEN_LOC_GLOBAL_STATUS }, + { BLE_MESH_MODEL_OP_GEN_LOC_LOCAL_GET, BLE_MESH_MODEL_OP_GEN_LOC_LOCAL_STATUS }, + { BLE_MESH_MODEL_OP_GEN_LOC_LOCAL_SET, BLE_MESH_MODEL_OP_GEN_LOC_LOCAL_STATUS }, + { BLE_MESH_MODEL_OP_GEN_USER_PROPERTIES_GET, BLE_MESH_MODEL_OP_GEN_USER_PROPERTIES_STATUS }, + { BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_GET, BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_STATUS }, + { BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_SET, BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_STATUS }, + { BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTIES_GET, BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTIES_STATUS }, + { BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_GET, BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_STATUS }, + { BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_SET, BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_STATUS }, + { BLE_MESH_MODEL_OP_GEN_MANU_PROPERTIES_GET, BLE_MESH_MODEL_OP_GEN_MANU_PROPERTIES_STATUS }, + { BLE_MESH_MODEL_OP_GEN_MANU_PROPERTY_GET, BLE_MESH_MODEL_OP_GEN_MANU_PROPERTY_STATUS }, + { BLE_MESH_MODEL_OP_GEN_MANU_PROPERTY_SET, BLE_MESH_MODEL_OP_GEN_MANU_PROPERTY_STATUS }, + { BLE_MESH_MODEL_OP_GEN_CLIENT_PROPERTIES_GET, BLE_MESH_MODEL_OP_GEN_CLIENT_PROPERTIES_STATUS }, +}; + +static void timeout_handler(struct k_work *work) +{ + generic_internal_data_t *internal = NULL; + bt_mesh_generic_client_t *client = NULL; + bt_mesh_client_node_t *node = NULL; + + BT_WARN("Receive generic status message timeout"); + + node = CONTAINER_OF(work, bt_mesh_client_node_t, timer.work); + if (!node || !node->ctx.model) { + BT_ERR("%s, Invalid parameter", __func__); + return; + } + + client = (bt_mesh_generic_client_t *)node->ctx.model->user_data; + if (!client) { + BT_ERR("%s, Generic Client user_data is NULL", __func__); + return; + } + + internal = (generic_internal_data_t *)client->internal_data; + if (!internal) { + BT_ERR("%s, Generic Client internal_data is NULL", __func__); + return; + } + + bt_mesh_callback_generic_status_to_btc(node->opcode, 0x03, node->ctx.model, + &node->ctx, NULL, 0); + + bt_mesh_client_free_node(&internal->queue, node); + + return; +} + +static void generic_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + generic_internal_data_t *internal = NULL; + bt_mesh_generic_client_t *client = NULL; + bt_mesh_client_node_t *node = NULL; + u8_t *val = NULL; + u8_t evt = 0xFF; + u32_t rsp = 0; + size_t len = 0; + + BT_DBG("%s, len %d, bytes %s", __func__, buf->len, bt_hex(buf->data, buf->len)); + + client = (bt_mesh_generic_client_t *)model->user_data; + if (!client) { + BT_ERR("%s, Generic Client user_data is NULL", __func__); + return; + } + + internal = (generic_internal_data_t *)client->internal_data; + if (!internal) { + BT_ERR("%s, Generic Client internal_data is NULL", __func__); + return; + } + + rsp = ctx->recv_op; + + switch (rsp) { + case BLE_MESH_MODEL_OP_GEN_ONOFF_STATUS: { + struct bt_mesh_gen_onoff_status *status = NULL; + if (buf->len != 1 && buf->len != 3) { + BT_ERR("Invalid Generic OnOff Status length %d", buf->len); + return; + } + status = osi_calloc(sizeof(struct bt_mesh_gen_onoff_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->present_onoff = net_buf_simple_pull_u8(buf); + if (buf->len) { + status->op_en = true; + status->target_onoff = net_buf_simple_pull_u8(buf); + status->remain_time = net_buf_simple_pull_u8(buf); + } + val = (u8_t *)status; + len = sizeof(struct bt_mesh_gen_onoff_status); + break; + } + case BLE_MESH_MODEL_OP_GEN_LEVEL_STATUS: { + struct bt_mesh_gen_level_status *status = NULL; + if (buf->len != 2 && buf->len != 5) { + BT_ERR("Invalid Generic Level Status length %d", buf->len); + return; + } + status = osi_calloc(sizeof(struct bt_mesh_gen_level_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->present_level = net_buf_simple_pull_le16(buf); + if (buf->len) { + status->op_en = true; + status->target_level = net_buf_simple_pull_le16(buf); + status->remain_time = net_buf_simple_pull_u8(buf); + } + val = (u8_t *)status; + len = sizeof(struct bt_mesh_gen_level_status); + break; + } + case BLE_MESH_MODEL_OP_GEN_DEF_TRANS_TIME_STATUS: { + struct bt_mesh_gen_def_trans_time_status *status = NULL; + if (buf->len != 1) { + BT_ERR("Invalid Generic Default Trans Time Status length %d", buf->len); + return; + } + status = osi_calloc(sizeof(struct bt_mesh_gen_def_trans_time_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->trans_time = net_buf_simple_pull_u8(buf); + val = (u8_t *)status; + len = sizeof(struct bt_mesh_gen_def_trans_time_status); + break; + } + case BLE_MESH_MODEL_OP_GEN_ONPOWERUP_STATUS: { + struct bt_mesh_gen_onpowerup_status *status = NULL; + if (buf->len != 1) { + BT_ERR("Invalid Generic OnPowerUp Status length %d", buf->len); + return; + } + status = osi_calloc(sizeof(struct bt_mesh_gen_onpowerup_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->onpowerup = net_buf_simple_pull_u8(buf); + val = (u8_t *)status; + len = sizeof(struct bt_mesh_gen_onpowerup_status); + break; + } + case BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_STATUS: { + struct bt_mesh_gen_power_level_status *status = NULL; + if (buf->len != 2 && buf->len != 5) { + BT_ERR("Invalid Generic Power Level Status length %d", buf->len); + return; + } + status = osi_calloc(sizeof(struct bt_mesh_gen_power_level_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->present_power = net_buf_simple_pull_le16(buf); + if (buf->len) { + status->op_en = true; + status->target_power = net_buf_simple_pull_le16(buf); + status->remain_time = net_buf_simple_pull_u8(buf); + } + val = (u8_t *)status; + len = sizeof(struct bt_mesh_gen_power_level_status); + break; + } + case BLE_MESH_MODEL_OP_GEN_POWER_LAST_STATUS: { + struct bt_mesh_gen_power_last_status *status = NULL; + if (buf->len != 2) { + BT_ERR("Invalid Generic Power Last Status length %d", buf->len); + return; + } + status = osi_calloc(sizeof(struct bt_mesh_gen_power_last_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->power = net_buf_simple_pull_le16(buf); + val = (u8_t *)status; + len = sizeof(struct bt_mesh_gen_power_last_status); + break; + } + case BLE_MESH_MODEL_OP_GEN_POWER_DEFAULT_STATUS: { + struct bt_mesh_gen_power_default_status *status = NULL; + if (buf->len != 2) { + BT_ERR("Invalid Generic Power Default Status length %d", buf->len); + return; + } + status = osi_calloc(sizeof(struct bt_mesh_gen_power_default_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->power = net_buf_simple_pull_le16(buf); + val = (u8_t *)status; + len = sizeof(struct bt_mesh_gen_power_default_status); + break; + } + case BLE_MESH_MODEL_OP_GEN_POWER_RANGE_STATUS: { + struct bt_mesh_gen_power_range_status *status = NULL; + if (buf->len != 5) { + BT_ERR("Invalid Generic Power Range Status length %d", buf->len); + return; + } + status = osi_calloc(sizeof(struct bt_mesh_gen_power_range_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->status_code = net_buf_simple_pull_u8(buf); + status->range_min = net_buf_simple_pull_le16(buf); + status->range_max = net_buf_simple_pull_le16(buf); + val = (u8_t *)status; + len = sizeof(struct bt_mesh_gen_power_range_status); + break; + } + case BLE_MESH_MODEL_OP_GEN_BATTERY_STATUS: { + struct bt_mesh_gen_battery_status *status = NULL; + if (buf->len != 8) { + BT_ERR("Invalid Generic Battery Status length %d", buf->len); + return; + } + status = osi_calloc(sizeof(struct bt_mesh_gen_battery_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + u32_t value = 0; + value = net_buf_simple_pull_le32(buf); + status->battery_level = (u8_t)value; + status->time_to_discharge = (value >> 8); + value = net_buf_simple_pull_le32(buf); + status->time_to_charge = (value & 0xffffff); + status->flags = (u8_t)(value >> 24); + val = (u8_t *)status; + len = sizeof(struct bt_mesh_gen_battery_status); + break; + } + case BLE_MESH_MODEL_OP_GEN_LOC_GLOBAL_STATUS: { + struct bt_mesh_gen_loc_global_status *status = NULL; + if (buf->len != 10) { + BT_ERR("Invalid Generic Location Global Status length %d", buf->len); + return; + } + status = osi_calloc(sizeof(struct bt_mesh_gen_loc_global_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->global_latitude = net_buf_simple_pull_le32(buf); + status->global_longitude = net_buf_simple_pull_le32(buf); + status->global_altitude = net_buf_simple_pull_le16(buf); + val = (u8_t *)status; + len = sizeof(struct bt_mesh_gen_loc_global_status); + break; + } + case BLE_MESH_MODEL_OP_GEN_LOC_LOCAL_STATUS: { + struct bt_mesh_gen_loc_local_status *status = NULL; + if (buf->len != 9) { + BT_ERR("Invalid Generic Location Local Status length %d", buf->len); + return; + } + status = osi_calloc(sizeof(struct bt_mesh_gen_loc_local_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->local_north = net_buf_simple_pull_le16(buf); + status->local_east = net_buf_simple_pull_le16(buf); + status->local_altitude = net_buf_simple_pull_le16(buf); + status->floor_number = net_buf_simple_pull_u8(buf); + status->uncertainty = net_buf_simple_pull_le16(buf); + val = (u8_t *)status; + len = sizeof(struct bt_mesh_gen_loc_local_status); + break; + } + case BLE_MESH_MODEL_OP_GEN_USER_PROPERTIES_STATUS: { + struct bt_mesh_gen_user_properties_status *status = NULL; + status = osi_calloc(sizeof(struct bt_mesh_gen_user_properties_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->user_property_ids = bt_mesh_alloc_buf(buf->len); + if (!status->user_property_ids) { + BT_ERR("%s, Failed to allocate memory", __func__); + osi_free(status); + return; + } + net_buf_simple_add_mem(status->user_property_ids, buf->data, buf->len); + val = (u8_t *)status; + len = sizeof(struct bt_mesh_gen_user_properties_status); + break; + } + case BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_STATUS: { + struct bt_mesh_gen_user_property_status *status = NULL; + status = osi_calloc(sizeof(struct bt_mesh_gen_user_property_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->user_property_id = net_buf_simple_pull_le16(buf); + if (buf->len) { + status->op_en = true; + status->user_access = net_buf_simple_pull_u8(buf); + status->user_property_value = bt_mesh_alloc_buf(buf->len); + if (!status->user_property_value) { + BT_ERR("%s, Failed to allocate memory", __func__); + osi_free(status); + return; + } + net_buf_simple_add_mem(status->user_property_value, buf->data, buf->len); + } + val = (u8_t *)status; + len = sizeof(struct bt_mesh_gen_user_property_status); + break; + } + case BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTIES_STATUS: { + struct bt_mesh_gen_admin_properties_status *status = NULL; + status = osi_calloc(sizeof(struct bt_mesh_gen_admin_properties_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->admin_property_ids = bt_mesh_alloc_buf(buf->len); + if (!status->admin_property_ids) { + BT_ERR("%s, Failed to allocate memory", __func__); + osi_free(status); + return; + } + net_buf_simple_add_mem(status->admin_property_ids, buf->data, buf->len); + val = (u8_t *)status; + len = sizeof(struct bt_mesh_gen_admin_properties_status); + break; + } + case BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_STATUS: { + struct bt_mesh_gen_admin_property_status *status = NULL; + status = osi_calloc(sizeof(struct bt_mesh_gen_admin_property_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->admin_property_id = net_buf_simple_pull_le16(buf); + if (buf->len) { + status->op_en = true; + status->admin_user_access = net_buf_simple_pull_u8(buf); + status->admin_property_value = bt_mesh_alloc_buf(buf->len); + if (!status->admin_property_value) { + BT_ERR("%s, Failed to allocate memory", __func__); + osi_free(status); + return; + } + net_buf_simple_add_mem(status->admin_property_value, buf->data, buf->len); + } + val = (u8_t *)status; + len = sizeof(struct bt_mesh_gen_admin_property_status); + break; + } + case BLE_MESH_MODEL_OP_GEN_MANU_PROPERTIES_STATUS: { + struct bt_mesh_gen_manu_properties_status *status = NULL; + status = osi_calloc(sizeof(struct bt_mesh_gen_manu_properties_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->manu_property_ids = bt_mesh_alloc_buf(buf->len); + if (!status->manu_property_ids) { + BT_ERR("%s, Failed to allocate memory", __func__); + osi_free(status); + return; + } + net_buf_simple_add_mem(status->manu_property_ids, buf->data, buf->len); + val = (u8_t *)status; + len = sizeof(struct bt_mesh_gen_manu_properties_status); + break; + } + case BLE_MESH_MODEL_OP_GEN_MANU_PROPERTY_STATUS: { + struct bt_mesh_gen_manu_property_status *status = NULL; + status = osi_calloc(sizeof(struct bt_mesh_gen_manu_property_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->manu_property_id = net_buf_simple_pull_le16(buf); + if (buf->len) { + status->op_en = true; + status->manu_user_access = net_buf_simple_pull_u8(buf); + status->manu_property_value = bt_mesh_alloc_buf(buf->len); + if (!status->manu_property_value) { + BT_ERR("%s, Failed to allocate memory", __func__); + osi_free(status); + return; + } + net_buf_simple_add_mem(status->manu_property_value, buf->data, buf->len); + } + val = (u8_t *)status; + len = sizeof(struct bt_mesh_gen_manu_property_status); + break; + } + case BLE_MESH_MODEL_OP_GEN_CLIENT_PROPERTIES_STATUS: { + struct bt_mesh_gen_client_properties_status *status = NULL; + status = osi_calloc(sizeof(struct bt_mesh_gen_client_properties_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->client_property_ids = bt_mesh_alloc_buf(buf->len); + if (!status->client_property_ids) { + BT_ERR("%s, Failed to allocate memory", __func__); + osi_free(status); + return; + } + net_buf_simple_add_mem(status->client_property_ids, buf->data, buf->len); + val = (u8_t *)status; + len = sizeof(struct bt_mesh_gen_client_properties_status); + break; + } + default: + BT_ERR("%s, Not a Generic Status message opcode", __func__); + return; + } + + buf->data = val; + buf->len = len; + node = bt_mesh_is_model_message_publish(model, ctx, buf, true); + if (!node) { + BT_DBG("Unexpected generic status message 0x%x", rsp); + } else { + switch (node->opcode) { + case BLE_MESH_MODEL_OP_GEN_ONOFF_GET: + case BLE_MESH_MODEL_OP_GEN_LEVEL_GET: + case BLE_MESH_MODEL_OP_GEN_DEF_TRANS_TIME_GET: + case BLE_MESH_MODEL_OP_GEN_ONPOWERUP_GET: + case BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_GET: + case BLE_MESH_MODEL_OP_GEN_POWER_LAST_GET: + case BLE_MESH_MODEL_OP_GEN_POWER_DEFAULT_GET: + case BLE_MESH_MODEL_OP_GEN_POWER_RANGE_GET: + case BLE_MESH_MODEL_OP_GEN_BATTERY_GET: + case BLE_MESH_MODEL_OP_GEN_LOC_GLOBAL_GET: + case BLE_MESH_MODEL_OP_GEN_LOC_LOCAL_GET: + case BLE_MESH_MODEL_OP_GEN_USER_PROPERTIES_GET: + case BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_GET: + case BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTIES_GET: + case BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_GET: + case BLE_MESH_MODEL_OP_GEN_MANU_PROPERTIES_GET: + case BLE_MESH_MODEL_OP_GEN_MANU_PROPERTY_GET: + case BLE_MESH_MODEL_OP_GEN_CLIENT_PROPERTIES_GET: + evt = 0x00; + break; + case BLE_MESH_MODEL_OP_GEN_ONOFF_SET: + case BLE_MESH_MODEL_OP_GEN_LEVEL_SET: + case BLE_MESH_MODEL_OP_GEN_DELTA_SET: + case BLE_MESH_MODEL_OP_GEN_MOVE_SET: + case BLE_MESH_MODEL_OP_GEN_DEF_TRANS_TIME_SET: + case BLE_MESH_MODEL_OP_GEN_ONPOWERUP_SET: + case BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_SET: + case BLE_MESH_MODEL_OP_GEN_POWER_DEFAULT_SET: + case BLE_MESH_MODEL_OP_GEN_POWER_RANGE_SET: + case BLE_MESH_MODEL_OP_GEN_LOC_GLOBAL_SET: + case BLE_MESH_MODEL_OP_GEN_LOC_LOCAL_SET: + case BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_SET: + case BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_SET: + case BLE_MESH_MODEL_OP_GEN_MANU_PROPERTY_SET: + evt = 0x01; + break; + default: + break; + } + + bt_mesh_callback_generic_status_to_btc(node->opcode, evt, model, ctx, val, len); + // Don't forget to release the node at the end. + bt_mesh_client_free_node(&internal->queue, node); + } + + switch (rsp) { + case BLE_MESH_MODEL_OP_GEN_USER_PROPERTIES_STATUS: { + struct bt_mesh_gen_user_properties_status *status; + status = (struct bt_mesh_gen_user_properties_status *)val; + bt_mesh_free_buf(status->user_property_ids); + break; + } + case BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_STATUS: { + struct bt_mesh_gen_user_property_status *status; + status = (struct bt_mesh_gen_user_property_status *)val; + bt_mesh_free_buf(status->user_property_value); + break; + } + case BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTIES_STATUS: { + struct bt_mesh_gen_admin_properties_status *status; + status = (struct bt_mesh_gen_admin_properties_status *)val; + bt_mesh_free_buf(status->admin_property_ids); + break; + } + case BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_STATUS: { + struct bt_mesh_gen_admin_property_status *status; + status = (struct bt_mesh_gen_admin_property_status *)val; + bt_mesh_free_buf(status->admin_property_value); + break; + } + case BLE_MESH_MODEL_OP_GEN_MANU_PROPERTIES_STATUS: { + struct bt_mesh_gen_manu_properties_status *status; + status = (struct bt_mesh_gen_manu_properties_status *)val; + bt_mesh_free_buf(status->manu_property_ids); + break; + } + case BLE_MESH_MODEL_OP_GEN_MANU_PROPERTY_STATUS: { + struct bt_mesh_gen_manu_property_status *status; + status = (struct bt_mesh_gen_manu_property_status *)val; + bt_mesh_free_buf(status->manu_property_value); + break; + } + case BLE_MESH_MODEL_OP_GEN_CLIENT_PROPERTIES_STATUS: { + struct bt_mesh_gen_client_properties_status *status; + status = (struct bt_mesh_gen_client_properties_status *)val; + bt_mesh_free_buf(status->client_property_ids); + break; + } + default: + break; + } + + osi_free(val); + + return; +} + +const struct bt_mesh_model_op gen_onoff_cli_op[] = { + { BLE_MESH_MODEL_OP_GEN_ONOFF_STATUS, 1, generic_status }, + BLE_MESH_MODEL_OP_END, +}; + +const struct bt_mesh_model_op gen_level_cli_op[] = { + { BLE_MESH_MODEL_OP_GEN_LEVEL_STATUS, 2, generic_status }, + BLE_MESH_MODEL_OP_END, +}; + +const struct bt_mesh_model_op gen_def_trans_time_cli_op[] = { + { BLE_MESH_MODEL_OP_GEN_DEF_TRANS_TIME_STATUS, 1, generic_status }, + BLE_MESH_MODEL_OP_END, +}; + +const struct bt_mesh_model_op gen_power_onoff_cli_op[] = { + { BLE_MESH_MODEL_OP_GEN_ONPOWERUP_STATUS, 1, generic_status }, + BLE_MESH_MODEL_OP_END, +}; + +const struct bt_mesh_model_op gen_power_level_cli_op[] = { + { BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_STATUS, 2, generic_status }, + { BLE_MESH_MODEL_OP_GEN_POWER_LAST_STATUS, 2, generic_status }, + { BLE_MESH_MODEL_OP_GEN_POWER_DEFAULT_STATUS, 2, generic_status }, + { BLE_MESH_MODEL_OP_GEN_POWER_RANGE_STATUS, 5, generic_status }, + BLE_MESH_MODEL_OP_END, +}; + +const struct bt_mesh_model_op gen_battery_cli_op[] = { + { BLE_MESH_MODEL_OP_GEN_BATTERY_STATUS, 8, generic_status }, + BLE_MESH_MODEL_OP_END, +}; + +const struct bt_mesh_model_op gen_location_cli_op[] = { + { BLE_MESH_MODEL_OP_GEN_LOC_GLOBAL_STATUS, 10, generic_status }, + { BLE_MESH_MODEL_OP_GEN_LOC_LOCAL_STATUS, 9, generic_status }, + BLE_MESH_MODEL_OP_END, +}; + +const struct bt_mesh_model_op gen_property_cli_op[] = { + { BLE_MESH_MODEL_OP_GEN_USER_PROPERTIES_STATUS, 2, generic_status }, + { BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_STATUS, 2, generic_status }, + { BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTIES_STATUS, 2, generic_status }, + { BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_STATUS, 2, generic_status }, + { BLE_MESH_MODEL_OP_GEN_MANU_PROPERTIES_STATUS, 2, generic_status }, + { BLE_MESH_MODEL_OP_GEN_MANU_PROPERTY_STATUS, 2, generic_status }, + { BLE_MESH_MODEL_OP_GEN_CLIENT_PROPERTIES_STATUS, 2, generic_status }, + BLE_MESH_MODEL_OP_END, +}; + +static int gen_get_state(struct bt_mesh_common_param *common, void *value) +{ + NET_BUF_SIMPLE_DEFINE(msg, BLE_MESH_GEN_GET_STATE_MSG_LEN); + int err; + + bt_mesh_model_msg_init(&msg, common->opcode); + + if (value) { + switch (common->opcode) { + case BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_GET: { + struct bt_mesh_gen_user_property_get *get; + get = (struct bt_mesh_gen_user_property_get *)value; + net_buf_simple_add_le16(&msg, get->user_property_id); + break; + } + case BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_GET: { + struct bt_mesh_gen_admin_property_get *get; + get = (struct bt_mesh_gen_admin_property_get *)value; + net_buf_simple_add_le16(&msg, get->admin_property_id); + break; + } + case BLE_MESH_MODEL_OP_GEN_MANU_PROPERTY_GET: { + struct bt_mesh_gen_manu_property_get *get; + get = (struct bt_mesh_gen_manu_property_get *)value; + net_buf_simple_add_le16(&msg, get->manu_property_id); + break; + } + case BLE_MESH_MODEL_OP_GEN_CLIENT_PROPERTIES_GET: { + struct bt_mesh_gen_client_properties_get *get; + get = (struct bt_mesh_gen_client_properties_get *)value; + net_buf_simple_add_le16(&msg, get->client_property_id); + break; + } + default: + BT_DBG("This generic message should be sent with NULL get pointer"); + break; + } + } + + err = bt_mesh_client_send_msg(common->model, common->opcode, &common->ctx, &msg, + timeout_handler, common->msg_timeout, true, + common->cb, common->cb_data); + if (err) { + BT_ERR("%s, Failed to send Generic Get message (err %d)", __func__, err); + } + + return err; +} + +static int gen_set_state(struct bt_mesh_common_param *common, + void *value, u16_t value_len, bool need_ack) +{ + struct net_buf_simple *msg = NULL; + int err; + + msg = bt_mesh_alloc_buf(value_len); + if (!msg) { + BT_ERR("%s, Failed to allocate memory", __func__); + return -ENOMEM; + } + + bt_mesh_model_msg_init(msg, common->opcode); + + switch (common->opcode) { + case BLE_MESH_MODEL_OP_GEN_ONOFF_SET: + case BLE_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK: { + struct bt_mesh_gen_onoff_set *set; + set = (struct bt_mesh_gen_onoff_set *)value; + net_buf_simple_add_u8(msg, set->onoff); + net_buf_simple_add_u8(msg, set->tid); + if (set->op_en) { + net_buf_simple_add_u8(msg, set->trans_time); + net_buf_simple_add_u8(msg, set->delay); + } + break; + } + + case BLE_MESH_MODEL_OP_GEN_LEVEL_SET: + case BLE_MESH_MODEL_OP_GEN_LEVEL_SET_UNACK: { + struct bt_mesh_gen_level_set *set; + set = (struct bt_mesh_gen_level_set *)value; + net_buf_simple_add_le16(msg, set->level); + net_buf_simple_add_u8(msg, set->tid); + if (set->op_en) { + net_buf_simple_add_u8(msg, set->trans_time); + net_buf_simple_add_u8(msg, set->delay); + } + break; + } + + case BLE_MESH_MODEL_OP_GEN_DELTA_SET: + case BLE_MESH_MODEL_OP_GEN_DELTA_SET_UNACK: { + struct bt_mesh_gen_delta_set *set; + set = (struct bt_mesh_gen_delta_set *)value; + net_buf_simple_add_le32(msg, set->level); + net_buf_simple_add_u8(msg, set->tid); + if (set->op_en) { + net_buf_simple_add_u8(msg, set->trans_time); + net_buf_simple_add_u8(msg, set->delay); + } + break; + } + + case BLE_MESH_MODEL_OP_GEN_MOVE_SET: + case BLE_MESH_MODEL_OP_GEN_MOVE_SET_UNACK: { + struct bt_mesh_gen_move_set *set; + set = (struct bt_mesh_gen_move_set *)value; + net_buf_simple_add_le16(msg, set->delta_level); + net_buf_simple_add_u8(msg, set->tid); + if (set->op_en) { + net_buf_simple_add_u8(msg, set->trans_time); + net_buf_simple_add_u8(msg, set->delay); + } + break; + } + + case BLE_MESH_MODEL_OP_GEN_DEF_TRANS_TIME_SET: + case BLE_MESH_MODEL_OP_GEN_DEF_TRANS_TIME_SET_UNACK: { + struct bt_mesh_gen_def_trans_time_set *set; + set = (struct bt_mesh_gen_def_trans_time_set *)value; + net_buf_simple_add_u8(msg, set->trans_time); + break; + } + + case BLE_MESH_MODEL_OP_GEN_ONPOWERUP_SET: + case BLE_MESH_MODEL_OP_GEN_ONPOWERUP_SET_UNACK: { + struct bt_mesh_gen_onpowerup_set *set; + set = (struct bt_mesh_gen_onpowerup_set *)value; + net_buf_simple_add_u8(msg, set->onpowerup); + break; + } + + case BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_SET: + case BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_SET_UNACK: { + struct bt_mesh_gen_power_level_set *set; + set = (struct bt_mesh_gen_power_level_set *)value; + net_buf_simple_add_le16(msg, set->power); + net_buf_simple_add_u8(msg, set->tid); + if (set->op_en) { + net_buf_simple_add_u8(msg, set->trans_time); + net_buf_simple_add_u8(msg, set->delay); + } + break; + } + + case BLE_MESH_MODEL_OP_GEN_POWER_DEFAULT_SET: + case BLE_MESH_MODEL_OP_GEN_POWER_DEFAULT_SET_UNACK: { + struct bt_mesh_gen_power_default_set *set; + set = (struct bt_mesh_gen_power_default_set *)value; + net_buf_simple_add_le16(msg, set->power); + break; + } + + case BLE_MESH_MODEL_OP_GEN_POWER_RANGE_SET: + case BLE_MESH_MODEL_OP_GEN_POWER_RANGE_SET_UNACK: { + struct bt_mesh_gen_power_range_set *set; + set = (struct bt_mesh_gen_power_range_set *)value; + net_buf_simple_add_le16(msg, set->range_min); + net_buf_simple_add_le16(msg, set->range_max); + break; + } + + case BLE_MESH_MODEL_OP_GEN_LOC_GLOBAL_SET: + case BLE_MESH_MODEL_OP_GEN_LOC_GLOBAL_SET_UNACK: { + struct bt_mesh_gen_loc_global_set *set; + set = (struct bt_mesh_gen_loc_global_set *)value; + net_buf_simple_add_le32(msg, set->global_latitude); + net_buf_simple_add_le32(msg, set->global_longitude); + net_buf_simple_add_le16(msg, set->global_altitude); + break; + } + + case BLE_MESH_MODEL_OP_GEN_LOC_LOCAL_SET: + case BLE_MESH_MODEL_OP_GEN_LOC_LOCAL_SET_UNACK: { + struct bt_mesh_gen_loc_local_set *set; + set = (struct bt_mesh_gen_loc_local_set *)value; + net_buf_simple_add_le16(msg, set->local_north); + net_buf_simple_add_le16(msg, set->local_east); + net_buf_simple_add_le16(msg, set->local_altitude); + net_buf_simple_add_u8(msg, set->floor_number); + net_buf_simple_add_le16(msg, set->uncertainty); + break; + } + + case BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_SET: + case BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_SET_UNACK: { + struct bt_mesh_gen_user_property_set *set; + set = (struct bt_mesh_gen_user_property_set *)value; + net_buf_simple_add_le16(msg, set->user_property_id); + net_buf_simple_add_mem(msg, set->user_property_value->data, set->user_property_value->len); + break; + } + + case BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_SET: + case BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_SET_UNACK: { + struct bt_mesh_gen_admin_property_set *set; + set = (struct bt_mesh_gen_admin_property_set *)value; + net_buf_simple_add_le16(msg, set->admin_property_id); + net_buf_simple_add_u8(msg, set->admin_user_access); + net_buf_simple_add_mem(msg, set->admin_property_value->data, set->admin_property_value->len); + break; + } + + case BLE_MESH_MODEL_OP_GEN_MANU_PROPERTY_SET: + case BLE_MESH_MODEL_OP_GEN_MANU_PROPERTY_SET_UNACK: { + struct bt_mesh_gen_manu_property_set *set; + set = (struct bt_mesh_gen_manu_property_set *)value; + net_buf_simple_add_le16(msg, set->manu_property_id); + net_buf_simple_add_u8(msg, set->manu_user_access); + break; + } + + default: + BT_ERR("%s, Not a Generic Client Set message opcode", __func__); + err = -EINVAL; + goto end; + } + + err = bt_mesh_client_send_msg(common->model, common->opcode, &common->ctx, msg, + timeout_handler, common->msg_timeout, need_ack, + common->cb, common->cb_data); + if (err) { + BT_ERR("%s, Failed to send Generic Set message (err %d)", __func__, err); + } + +end: + bt_mesh_free_buf(msg); + + return err; +} + +int bt_mesh_generic_client_get_state(struct bt_mesh_common_param *common, void *get, void *status) +{ + bt_mesh_generic_client_t *client = NULL; + + if (!common || !common->model) { + BT_ERR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + client = (bt_mesh_generic_client_t *)common->model->user_data; + if (!client || !client->internal_data) { + BT_ERR("%s, Generic Client user data is NULL", __func__); + return -EINVAL; + } + + switch (common->opcode) { + case BLE_MESH_MODEL_OP_GEN_ONOFF_GET: + case BLE_MESH_MODEL_OP_GEN_LEVEL_GET: + case BLE_MESH_MODEL_OP_GEN_DEF_TRANS_TIME_GET: + case BLE_MESH_MODEL_OP_GEN_ONPOWERUP_GET: + case BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_GET: + case BLE_MESH_MODEL_OP_GEN_POWER_LAST_GET: + case BLE_MESH_MODEL_OP_GEN_POWER_DEFAULT_GET: + case BLE_MESH_MODEL_OP_GEN_POWER_RANGE_GET: + case BLE_MESH_MODEL_OP_GEN_BATTERY_GET: + case BLE_MESH_MODEL_OP_GEN_LOC_GLOBAL_GET: + case BLE_MESH_MODEL_OP_GEN_LOC_LOCAL_GET: + case BLE_MESH_MODEL_OP_GEN_USER_PROPERTIES_GET: + case BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTIES_GET: + case BLE_MESH_MODEL_OP_GEN_MANU_PROPERTIES_GET: + break; + case BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_GET: + if (!get) { + BT_ERR("%s, Generic user_property_get is NULL", __func__); + return -EINVAL; + } + break; + case BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_GET: + if (!get) { + BT_ERR("%s, Generic admin_property_get is NULL", __func__); + return -EINVAL; + } + break; + case BLE_MESH_MODEL_OP_GEN_MANU_PROPERTY_GET: + if (!get) { + BT_ERR("%s, Generic manu_property_get is NULL", __func__); + return -EINVAL; + } + break; + case BLE_MESH_MODEL_OP_GEN_CLIENT_PROPERTIES_GET: + if (!get) { + BT_ERR("%s, Generic client_properties_get is NULL", __func__); + return -EINVAL; + } + break; + default: + BT_ERR("%s, Not a Generic Client Get message opcode", __func__); + return -EINVAL; + } + + return gen_get_state(common, get); +} + +int bt_mesh_generic_client_set_state(struct bt_mesh_common_param *common, void *set, void *status) +{ + bt_mesh_generic_client_t *client = NULL; + u16_t length = 0; + bool need_ack = false; + + if (!common || !common->model || !set) { + BT_ERR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + client = (bt_mesh_generic_client_t *)common->model->user_data; + if (!client || !client->internal_data) { + BT_ERR("%s, Generic Client user data is NULL", __func__); + return -EINVAL; + } + + switch (common->opcode) { + case BLE_MESH_MODEL_OP_GEN_ONOFF_SET: + need_ack = true; + case BLE_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK: { + struct bt_mesh_gen_onoff_set *value; + value = (struct bt_mesh_gen_onoff_set *)set; + if (value->op_en) { + if ((value->trans_time & 0x3F) > 0x3E) { + BT_ERR("%s, Invalid Generic OnOff Set transition time", __func__); + return -EINVAL; + } + } + length = BLE_MESH_GEN_ONOFF_SET_MSG_LEN; + break; + } + case BLE_MESH_MODEL_OP_GEN_LEVEL_SET: + need_ack = true; + case BLE_MESH_MODEL_OP_GEN_LEVEL_SET_UNACK: { + struct bt_mesh_gen_level_set *value; + value = (struct bt_mesh_gen_level_set *)set; + if (value->op_en) { + if ((value->trans_time & 0x3F) > 0x3E) { + BT_ERR("%s, Invalid Generic Level Set transition time", __func__); + return -EINVAL; + } + } + length = BLE_MESH_GEN_LEVEL_SET_MSG_LEN; + break; + } + case BLE_MESH_MODEL_OP_GEN_DELTA_SET: + need_ack = true; + case BLE_MESH_MODEL_OP_GEN_DELTA_SET_UNACK: { + struct bt_mesh_gen_delta_set *value; + value = (struct bt_mesh_gen_delta_set *)set; + if (value->op_en) { + if ((value->trans_time & 0x3F) > 0x3E) { + BT_ERR("%s, Invalid Generic Delta Set transition time", __func__); + return -EINVAL; + } + } + length = BLE_MESH_GEN_DELTA_SET_MSG_LEN; + break; + } + case BLE_MESH_MODEL_OP_GEN_MOVE_SET: + need_ack = true; + case BLE_MESH_MODEL_OP_GEN_MOVE_SET_UNACK: { + struct bt_mesh_gen_move_set *value; + value = (struct bt_mesh_gen_move_set *)set; + if (value->op_en) { + if ((value->trans_time & 0x3F) > 0x3E) { + BT_ERR("%s, Invalid Generic Move Set transition time", __func__); + return -EINVAL; + } + } + length = BLE_MESH_GEN_MOVE_SET_MSG_LEN; + break; + } + case BLE_MESH_MODEL_OP_GEN_DEF_TRANS_TIME_SET: + need_ack = true; + case BLE_MESH_MODEL_OP_GEN_DEF_TRANS_TIME_SET_UNACK: { + u8_t value = *(u8_t *)set; + if ((value & 0x3F) > 0x3E) { + BT_ERR("%s, Invalid Generic Default Trans Time Set transition time", __func__); + return -EINVAL; + } + length = BLE_MESH_GEN_DEF_TRANS_TIME_SET_MSG_LEN; + break; + } + case BLE_MESH_MODEL_OP_GEN_ONPOWERUP_SET: + need_ack = true; + case BLE_MESH_MODEL_OP_GEN_ONPOWERUP_SET_UNACK: + length = BLE_MESH_GEN_ONPOWERUP_SET_MSG_LEN; + break; + case BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_SET: + need_ack = true; + case BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_SET_UNACK: { + struct bt_mesh_gen_power_level_set *value; + value = (struct bt_mesh_gen_power_level_set *)set; + if (value->op_en) { + if ((value->trans_time & 0x3F) > 0x3E) { + BT_ERR("%s, Invalid Generic Power Level Set transition time", __func__); + return -EINVAL; + } + } + length = BLE_MESH_GEN_POWER_LEVEL_SET_MSG_LEN; + break; + } + case BLE_MESH_MODEL_OP_GEN_POWER_DEFAULT_SET: + need_ack = true; + case BLE_MESH_MODEL_OP_GEN_POWER_DEFAULT_SET_UNACK: + length = BLE_MESH_GEN_POWER_DEFAULT_SET_MSG_LEN; + break; + case BLE_MESH_MODEL_OP_GEN_POWER_RANGE_SET: + need_ack = true; + case BLE_MESH_MODEL_OP_GEN_POWER_RANGE_SET_UNACK: { + struct bt_mesh_gen_power_range_set *value; + value = (struct bt_mesh_gen_power_range_set *)set; + if (value->range_min > value->range_max) { + BT_ERR("%s, Generic Power Level Set range min is greater than range max", __func__); + return -EINVAL; + } + length = BLE_MESH_GEN_POWER_RANGE_SET_MSG_LEN; + break; + } + case BLE_MESH_MODEL_OP_GEN_LOC_GLOBAL_SET: + need_ack = true; + case BLE_MESH_MODEL_OP_GEN_LOC_GLOBAL_SET_UNACK: + length = BLE_MESH_GEN_LOC_GLOBAL_SET_MSG_LEN; + break; + case BLE_MESH_MODEL_OP_GEN_LOC_LOCAL_SET: + need_ack = true; + case BLE_MESH_MODEL_OP_GEN_LOC_LOCAL_SET_UNACK: + length = BLE_MESH_GEN_LOC_LOCAL_SET_MSG_LEN; + break; + case BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_SET: + need_ack = true; + case BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_SET_UNACK: { + struct bt_mesh_gen_user_property_set *value; + value = (struct bt_mesh_gen_user_property_set *)set; + if (!value->user_property_value) { + BT_ERR("%s, Generic user_property_value is NULL", __func__); + return -EINVAL; + } + length = (1 + 2 + value->user_property_value->len + 4); + break; + } + case BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_SET: + need_ack = true; + case BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_SET_UNACK: { + struct bt_mesh_gen_admin_property_set *value; + value = (struct bt_mesh_gen_admin_property_set *)set; + if (!value->admin_property_value) { + BT_ERR("%s, Generic admin_property_value is NULL", __func__); + return -EINVAL; + } + length = (1 + 2 + 1 + value->admin_property_value->len + 4); + break; + } + case BLE_MESH_MODEL_OP_GEN_MANU_PROPERTY_SET: + need_ack = true; + case BLE_MESH_MODEL_OP_GEN_MANU_PROPERTY_SET_UNACK: + length = BLE_MESH_GEN_MANU_PROPERTY_SET_MSG_LEN; + break; + default: + BT_ERR("%s, Not a Generic Client Set message opcode", __func__); + return -EINVAL; + } + + return gen_set_state(common, set, length, need_ack); +} + +static int generic_client_init(struct bt_mesh_model *model, bool primary) +{ + generic_internal_data_t *internal = NULL; + bt_mesh_generic_client_t *client = NULL; + + BT_DBG("primary %u", primary); + + if (!model) { + BT_ERR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + client = (bt_mesh_generic_client_t *)model->user_data; + if (!client) { + BT_ERR("%s, Generic Client user_data is NULL", __func__); + return -EINVAL; + } + + /* TODO: call osi_free() when deinit function is invoked*/ + internal = osi_calloc(sizeof(generic_internal_data_t)); + if (!internal) { + BT_ERR("%s, Failed to allocate memory", __func__); + return -ENOMEM; + } + + sys_slist_init(&internal->queue); + + client->model = model; + client->op_pair_size = ARRAY_SIZE(gen_op_pair); + client->op_pair = gen_op_pair; + client->internal_data = internal; + + return 0; +} + +int bt_mesh_gen_onoff_cli_init(struct bt_mesh_model *model, bool primary) +{ + return generic_client_init(model, primary); +} + +int bt_mesh_gen_level_cli_init(struct bt_mesh_model *model, bool primary) +{ + return generic_client_init(model, primary); +} + +int bt_mesh_gen_def_trans_time_cli_init(struct bt_mesh_model *model, bool primary) +{ + return generic_client_init(model, primary); +} + +int bt_mesh_gen_pwr_onoff_cli_init(struct bt_mesh_model *model, bool primary) +{ + return generic_client_init(model, primary); +} + +int bt_mesh_gen_pwr_level_cli_init(struct bt_mesh_model *model, bool primary) +{ + return generic_client_init(model, primary); +} + +int bt_mesh_gen_battery_cli_init(struct bt_mesh_model *model, bool primary) +{ + return generic_client_init(model, primary); +} + +int bt_mesh_gen_location_cli_init(struct bt_mesh_model *model, bool primary) +{ + return generic_client_init(model, primary); +} + +int bt_mesh_gen_property_cli_init(struct bt_mesh_model *model, bool primary) +{ + return generic_client_init(model, primary); +} diff --git a/components/bt/ble_mesh/mesh_models/include/generic_client.h b/components/bt/ble_mesh/mesh_models/include/generic_client.h new file mode 100644 index 0000000000..3f272cb952 --- /dev/null +++ b/components/bt/ble_mesh/mesh_models/include/generic_client.h @@ -0,0 +1,491 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +/** @file + * @brief Bluetooth Mesh Generic Client Model APIs. + */ + +#ifndef _GENERIC_CLIENT_H_ +#define _GENERIC_CLIENT_H_ + +#include "mesh_access.h" +#include "mesh_kernel.h" + +#include "model_common.h" + +/* Generic client model common structure */ +typedef bt_mesh_client_common_t bt_mesh_generic_client_t; +typedef bt_mesh_internal_data_t generic_internal_data_t; + +/* Generic OnOff Client Model Context */ +extern const struct bt_mesh_model_op gen_onoff_cli_op[]; + +/** @def BLE_MESH_MODEL_GEN_ONOFF_CLI + * + * Define a new generic onoff client model. Note that this API + * needs to be repeated for each element which the application + * wants to have a generic onoff client model on. + * @param cli_pub Pointer to a unique struct bt_mesh_model_pub. + * @param cli_data Pointer to a unique struct bt_mesh_gen_onoff_cli. + * + * @return New generic onoff client model instance. + */ +#define BLE_MESH_MODEL_GEN_ONOFF_CLI(cli_pub, cli_data) \ + BLE_MESH_MODEL(BLE_MESH_MODEL_ID_GEN_ONOFF_CLI, \ + gen_onoff_cli_op, cli_pub, cli_data) + +typedef bt_mesh_client_common_t bt_mesh_gen_onoff_cli_t; + +struct bt_mesh_gen_onoff_status { + bool op_en; /* Indicate whether optional parameters included */ + u8_t present_onoff; /* Present value of Generic OnOff state */ + u8_t target_onoff; /* Target value of Generic OnOff state (optional) */ + u8_t remain_time; /* Time to complete state transition (C.1) */ +}; + +struct bt_mesh_gen_onoff_set { + bool op_en; /* Indicate whether optional parameters included */ + u8_t onoff; /* Target value of Generic OnOff state */ + u8_t tid; /* Transaction Identifier */ + u8_t trans_time; /* Time to complete state transition (optional) */ + u8_t delay; /* Indicate message execution delay (C.1) */ +}; + +/* Generic Level Client Model Context */ +extern const struct bt_mesh_model_op gen_level_cli_op[]; + +/** @def BLE_MESH_MODEL_GEN_LEVEL_CLI + * + * Define a new generic level client model. Note that this API + * needs to be repeated for each element which the application + * wants to have a generic level client model on. + * @param cli_pub Pointer to a unique struct bt_mesh_model_pub. + * @param cli_data Pointer to a unique struct bt_mesh_gen_level_cli. + * + * @return New generic level client model instance. + */ +#define BLE_MESH_MODEL_GEN_LEVEL_CLI(cli_pub, cli_data) \ + BLE_MESH_MODEL(BLE_MESH_MODEL_ID_GEN_LEVEL_CLI, \ + gen_level_cli_op, cli_pub, cli_data) + +typedef bt_mesh_client_common_t bt_mesh_gen_level_cli_t; + +struct bt_mesh_gen_level_status { + bool op_en; /* Indicate whether optional parameters included */ + s16_t present_level; /* Present value of Generic Level state */ + s16_t target_level; /* Target value of the Generic Level state (optional) */ + u8_t remain_time; /* Time to complete state transition (C.1) */ +}; + +struct bt_mesh_gen_level_set { + bool op_en; /* Indicate whether optional parameters included */ + s16_t level; /* Target value of Generic Level state */ + u8_t tid; /* Transaction Identifier */ + u8_t trans_time; /* Time to complete state transition (optional) */ + u8_t delay; /* Indicate message execution delay (C.1) */ +}; + +struct bt_mesh_gen_delta_set { + bool op_en; /* Indicate whether optional parameters included */ + s32_t level; /* Delta change of Generic Level state */ + u8_t tid; /* Transaction Identifier */ + u8_t trans_time; /* Time to complete state transition (optional) */ + u8_t delay; /* Indicate message execution delay (C.1) */ +}; + +struct bt_mesh_gen_move_set { + bool op_en; /* Indicate whether optional parameters included */ + s16_t delta_level; /* Delta Level step to calculate Move speed for Generic Level state */ + u8_t tid; /* Transaction Identifier */ + u8_t trans_time; /* Time to complete state transition (optional) */ + u8_t delay; /* Indicate message execution delay (C.1) */ +}; + +/* Generic Default Transition Time Client Model Context */ +extern const struct bt_mesh_model_op gen_def_trans_time_cli_op[]; + +/** @def BLE_MESH_MODEL_GEN_DEF_TRANS_TIME_CLI + * + * Define a new generic default transition time client model. Note + * that this API needs to be repeated for each element that the + * application wants to have a generic default transition client + * model on. + * @param cli_pub Pointer to a unique struct bt_mesh_model_pub. + * @param cli_data Pointer to a unique struct bt_mesh_gen_def_trans_time_cli. + * + * @return New generic default transition time client model instance. + */ +#define BLE_MESH_MODEL_GEN_DEF_TRANS_TIME_CLI(cli_pub, cli_data) \ + BLE_MESH_MODEL(BLE_MESH_MODEL_ID_GEN_DEF_TRANS_TIME_CLI, \ + gen_def_trans_time_cli_op, cli_pub, cli_data) + +typedef bt_mesh_client_common_t bt_mesh_gen_def_trans_time_cli_t; + +struct bt_mesh_gen_def_trans_time_set { + u8_t trans_time; /* The value of the Generic Default Transition Time state */ +}; + +struct bt_mesh_gen_def_trans_time_status { + u8_t trans_time; /* The value of the Generic Default Transition Time state */ +}; + +/* Generic Power OnOff Client Model Context */ +extern const struct bt_mesh_model_op gen_power_onoff_cli_op[]; + +/** @def BLE_MESH_MODEL_GEN_POWER_ONOFF_CLI + * + * Define a new generic power onoff client model. Note that this API + * needs to be repeated for each element which the application wants + * to have a generic power onoff client model on. + * @param cli_pub Pointer to a unique struct bt_mesh_model_pub. + * @param cli_data Pointer to a unique struct bt_mesh_gen_power_onoff_cli. + * + * @return New generic power onoff client model instance. + */ +#define BLE_MESH_MODEL_GEN_POWER_ONOFF_CLI(cli_pub, cli_data) \ + BLE_MESH_MODEL(BLE_MESH_MODEL_ID_GEN_POWER_ONOFF_CLI, \ + gen_power_onoff_cli_op, cli_pub, cli_data) + +typedef bt_mesh_client_common_t bt_mesh_gen_power_onoff_cli_t; + +struct bt_mesh_gen_onpowerup_set { + u8_t onpowerup; /* The value of the Generic OnPowerUp state */ +}; + +struct bt_mesh_gen_onpowerup_status { + u8_t onpowerup; /* The value of the Generic OnPowerUp state */ +}; + +/* Generic Power Level Client Model Context */ +extern const struct bt_mesh_model_op gen_power_level_cli_op[]; + +/** @def BLE_MESH_MODEL_GEN_POWER_LEVEL_CLI + * + * Define a new generic power level client model. Note that this API + * needs to be repeated for each element which the application wants + * to have a generic power level client model on. + * @param cli_pub Pointer to a unique struct bt_mesh_model_pub. + * @param cli_data Pointer to a unique struct bt_mesh_gen_power_level_cli. + * + * @return New generic power level client model instance. + */ +#define BLE_MESH_MODEL_GEN_POWER_LEVEL_CLI(cli_pub, cli_data) \ + BLE_MESH_MODEL(BLE_MESH_MODEL_ID_GEN_POWER_LEVEL_CLI, \ + gen_power_level_cli_op, cli_pub, cli_data) + +typedef bt_mesh_client_common_t bt_mesh_gen_power_level_cli_t; + +struct bt_mesh_gen_power_level_status { + bool op_en; /* Indicate whether optional parameters included */ + u16_t present_power; /* Present value of Generic Power Actual state */ + u16_t target_power; /* Target value of Generic Power Actual state (optional) */ + u8_t remain_time; /* Time to complete state transition (C.1) */ +}; + +struct bt_mesh_gen_power_last_status { + u16_t power; /* The value of the Generic Power Last state */ +}; + +struct bt_mesh_gen_power_default_status { + u16_t power; /* The value of the Generic Default Last state */ +}; + +struct bt_mesh_gen_power_range_status { + u8_t status_code; /* Status Code for the requesting message */ + u16_t range_min; /* Value of Range Min field of Generic Power Range state */ + u16_t range_max; /* Value of Range Max field of Generic Power Range state */ +}; + +struct bt_mesh_gen_power_level_set { + bool op_en; /* Indicate whether optional parameters included */ + u16_t power; /* Target value of Generic Power Actual state */ + u8_t tid; /* Transaction Identifier */ + u8_t trans_time; /* Time to complete state transition (optional) */ + u8_t delay; /* Indicate message execution delay (C.1) */ +}; + +struct bt_mesh_gen_power_default_set { + u16_t power; /* The value of the Generic Power Default state */ +}; + +struct bt_mesh_gen_power_range_set { + u16_t range_min; /* Value of Range Min field of Generic Power Range state */ + u16_t range_max; /* Value of Range Max field of Generic Power Range state */ +}; + +/* Generic Battery Client Model Context */ +extern const struct bt_mesh_model_op gen_battery_cli_op[]; + +/** @def BLE_MESH_MODEL_GEN_BATTERY_CLI + * + * Define a new generic battery client model. Note that this API + * needs to be repeated for each element which the application + * wants to have a generic battery client model on. + * @param cli_pub Pointer to a unique struct bt_mesh_model_pub. + * @param cli_data Pointer to a unique struct bt_mesh_gen_battery_cli. + * + * @return New generic battery client model instance. + */ +#define BLE_MESH_MODEL_GEN_BATTERY_CLI(cli_pub, cli_data) \ + BLE_MESH_MODEL(BLE_MESH_MODEL_ID_GEN_BATTERY_CLI, \ + gen_battery_cli_op, cli_pub, cli_data) + +typedef bt_mesh_client_common_t bt_mesh_gen_battery_cli_t; + +struct bt_mesh_gen_battery_status { + u32_t battery_level : 8; /* Value of Generic Battery Level state */ + u32_t time_to_discharge : 24; /* Value of Generic Battery Time to Discharge state */ + u32_t time_to_charge : 24; /* Value of Generic Battery Time to Charge state */ + u32_t flags : 8; /* Value of Generic Battery Flags state */ +}; + +/* Generic Location Client Model Context */ +extern const struct bt_mesh_model_op gen_location_cli_op[]; + +/** @def BLE_MESH_MODEL_GEN_LOCATION_CLI + * + * Define a new generic location client model. Note that this API + * needs to be repeated for each element which the application + * wants to have a generic location client model on. + * @param cli_pub Pointer to a unique struct bt_mesh_model_pub. + * @param cli_data Pointer to a unique struct bt_mesh_gen_location_cli. + * + * @return New generic location client model instance. + */ +#define BLE_MESH_MODEL_GEN_LOCATION_CLI(cli_pub, cli_data) \ + BLE_MESH_MODEL(BLE_MESH_MODEL_ID_GEN_LOCATION_CLI, \ + gen_location_cli_op, cli_pub, cli_data) + +typedef bt_mesh_client_common_t bt_mesh_gen_location_cli_t; + +struct bt_mesh_gen_loc_global_status { + s32_t global_latitude; /* Global Coordinates (Latitude) */ + s32_t global_longitude; /* Global Coordinates (Longitude) */ + s16_t global_altitude; /* Global Altitude */ +}; + +struct bt_mesh_gen_loc_local_status { + s16_t local_north; /* Local Coordinates (North) */ + s16_t local_east; /* Local Coordinates (East) */ + s16_t local_altitude; /* Local Altitude */ + u8_t floor_number; /* Floor Number */ + u16_t uncertainty; /* Uncertainty */ +}; + +struct bt_mesh_gen_loc_global_set { + s32_t global_latitude; /* Global Coordinates (Latitude) */ + s32_t global_longitude; /* Global Coordinates (Longitude) */ + s16_t global_altitude; /* Global Altitude */ +}; + +struct bt_mesh_gen_loc_local_set { + s16_t local_north; /* Local Coordinates (North) */ + s16_t local_east; /* Local Coordinates (East) */ + s16_t local_altitude; /* Local Altitude */ + u8_t floor_number; /* Floor Number */ + u16_t uncertainty; /* Uncertainty */ +}; + +/* Generic Property Client Model Context */ +extern const struct bt_mesh_model_op gen_property_cli_op[]; + +/** @def BLE_MESH_MODEL_GEN_LOCATION_CLI + * + * Define a new generic location client model. Note that this API + * needs to be repeated for each element which the application + * wants to have a generic location client model on. + * @param cli_pub Pointer to a unique struct bt_mesh_model_pub. + * @param cli_data Pointer to a unique struct bt_mesh_gen_location_cli. + * + * @return New generic location client model instance. + */ +#define BLE_MESH_MODEL_GEN_PROPERTY_CLI(cli_pub, cli_data) \ + BLE_MESH_MODEL(BLE_MESH_MODEL_ID_GEN_PROP_CLI, \ + gen_property_cli_op, cli_pub, cli_data) + +typedef bt_mesh_client_common_t bt_mesh_gen_property_cli_t; + +struct bt_mesh_gen_user_properties_status { + struct net_buf_simple *user_property_ids; /* Buffer contains a sequence of N User Property IDs */ +}; + +struct bt_mesh_gen_user_property_status { + bool op_en; /* Indicate whether optional parameters included */ + u16_t user_property_id; /* Property ID identifying a Generic User Property */ + u8_t user_access; /* Enumeration indicating user access (optional) */ + struct net_buf_simple *user_property_value; /* Raw value for the User Property (C.1) */ +}; + +struct bt_mesh_gen_admin_properties_status { + struct net_buf_simple *admin_property_ids; /* Buffer contains a sequence of N Admin Property IDs */ +}; + +struct bt_mesh_gen_admin_property_status { + bool op_en; /* Indicate whether optional parameters included */ + u16_t admin_property_id; /* Property ID identifying a Generic Admin Property */ + u8_t admin_user_access; /* Enumeration indicating user access (optional) */ + struct net_buf_simple *admin_property_value; /* Raw value for the Admin Property (C.1) */ +}; + +struct bt_mesh_gen_manu_properties_status { + struct net_buf_simple *manu_property_ids; /* Buffer contains a sequence of N Manufacturer Property IDs */ +}; + +struct bt_mesh_gen_manu_property_status { + bool op_en; /* Indicate whether optional parameters included */ + u16_t manu_property_id; /* Property ID identifying a Generic Manufacturer Property */ + u8_t manu_user_access; /* Enumeration indicating user access (optional) */ + struct net_buf_simple *manu_property_value; /* Raw value for the Manufacturer Property (C.1) */ +}; + +struct bt_mesh_gen_client_properties_status { + struct net_buf_simple *client_property_ids; /* Buffer contains a sequence of N Client Property IDs */ +}; + +struct bt_mesh_gen_user_property_get { + u16_t user_property_id; /* Property ID identifying a Generic User Property */ +}; + +struct bt_mesh_gen_user_property_set { + u16_t user_property_id; /* Property ID identifying a Generic User Property */ + struct net_buf_simple *user_property_value; /* Raw value for the User Property */ +}; + +struct bt_mesh_gen_admin_property_get { + u16_t admin_property_id; /* Property ID identifying a Generic Admin Property */ +}; + +struct bt_mesh_gen_admin_property_set { + u16_t admin_property_id; /* Property ID identifying a Generic Admin Property */ + u8_t admin_user_access; /* Enumeration indicating user access */ + struct net_buf_simple *admin_property_value; /* Raw value for the Admin Property */ +}; + +struct bt_mesh_gen_manu_property_get { + u16_t manu_property_id; /* Property ID identifying a Generic Manufacturer Property */ +}; + +struct bt_mesh_gen_manu_property_set { + u16_t manu_property_id; /* Property ID identifying a Generic Manufacturer Property */ + u8_t manu_user_access; /* Enumeration indicating user access */ +}; + +struct bt_mesh_gen_client_properties_get { + u16_t client_property_id; /* A starting Client Property ID present within an element */ +}; + +/** + * @brief This function is called to initialize generic onoff client model user_data. + * + * @param[in] model: Pointer to generic onoff client model + * @param[in] primary: Whether belongs to primary element + * + * @return Zero-success, other-fail + */ +int bt_mesh_gen_onoff_cli_init(struct bt_mesh_model *model, bool primary); + +/** + * @brief This function is called to initialize generic level client model user_data. + * + * @param[in] model: Pointer to generic level client model + * @param[in] primary: Whether belongs to primary element + * + * @return Zero-success, other-fail + */ +int bt_mesh_gen_level_cli_init(struct bt_mesh_model *model, bool primary); + +/** + * @brief This function is called to initialize generic default transition time + * client model user_data. + * + * @param[in] model: Pointer to generic default transition time client model + * @param[in] primary: Whether belongs to primary element + * + * @return Zero-success, other-fail + */ +int bt_mesh_gen_def_trans_time_cli_init(struct bt_mesh_model *model, bool primary); + +/** + * @brief This function is called to initialize generic power onoff client model user_data. + * + * @param[in] model: Pointer to generic power onoff client model + * @param[in] primary: Whether belongs to primary element + * + * @return Zero-success, other-fail + */ +int bt_mesh_gen_pwr_onoff_cli_init(struct bt_mesh_model *model, bool primary); + +/** + * @brief This function is called to initialize generic power level client model user_data. + * + * @param[in] model: Pointer to generic power level client model + * @param[in] primary: Whether belongs to primary element + * + * @return Zero-success, other-fail + */ +int bt_mesh_gen_pwr_level_cli_init(struct bt_mesh_model *model, bool primary); + +/** + * @brief This function is called to initialize generic battery client model user_data. + * + * @param[in] model: Pointer to generic battery client model + * @param[in] primary: Whether belongs to primary element + * + * @return Zero-success, other-fail + */ +int bt_mesh_gen_battery_cli_init(struct bt_mesh_model *model, bool primary); + +/** + * @brief This function is called to initialize generic location client model user_data. + * + * @param[in] model: Pointer to generic location client model + * @param[in] primary: Whether belongs to primary element + * + * @return Zero-success, other-fail + */ +int bt_mesh_gen_location_cli_init(struct bt_mesh_model *model, bool primary); + +/** + * @brief This function is called to initialize generic property client model user_data. + * + * @param[in] model: Pointer to generic property client model + * @param[in] primary: Whether belongs to primary element + * + * @return Zero-success, other-fail + */ +int bt_mesh_gen_property_cli_init(struct bt_mesh_model *model, bool primary); + +/** + * @brief This function is called to get generic states. + * + * @param[in] common: Message common information structure + * @param[in] get: Pointer of generic get message value + * @param[out] status: Pointer of generic status message value + * + * @return Zero-success, other-fail + */ +int bt_mesh_generic_client_get_state(struct bt_mesh_common_param *common, void *get, void *status); + +/** + * @brief This function is called to set generic states. + * + * @param[in] common: Message common information structure + * @param[in] set: Pointer of generic set message value + * @param[out] status: Pointer of generic status message value + * + * @return Zero-success, other-fail + */ +int bt_mesh_generic_client_set_state(struct bt_mesh_common_param *common, void *set, void *status); + +#endif /* _GENERIC_CLIENT_H_ */ diff --git a/components/bt/ble_mesh/mesh_models/include/lighting_client.h b/components/bt/ble_mesh/mesh_models/include/lighting_client.h new file mode 100644 index 0000000000..5b6b92a5ae --- /dev/null +++ b/components/bt/ble_mesh/mesh_models/include/lighting_client.h @@ -0,0 +1,492 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +/** @file + * @brief Bluetooth Mesh Lighting Client Model APIs. + */ + +#ifndef _LIGHTING_CLIENT_H_ +#define _LIGHTING_CLIENT_H_ + +#include "mesh_access.h" +#include "mesh_kernel.h" + +#include "model_common.h" + +/* Light client model common structure */ +typedef bt_mesh_client_common_t bt_mesh_light_client_t; +typedef bt_mesh_internal_data_t light_internal_data_t; + +/* Light Lightness Client Model Context */ +extern const struct bt_mesh_model_op light_lightness_cli_op[]; + +/** @def BLE_MESH_MODEL_LIGHT_LIGHTNESS_CLI + * + * Define a new light lightness client model. Note that this API + * needs to be repeated for each element which the application + * wants to have a light lightness client model on. + * @param cli_pub Pointer to a unique struct bt_mesh_model_pub. + * @param cli_data Pointer to a unique struct bt_mesh_light_lightness_cli. + * + * @return New light lightness client model instance. + */ +#define BLE_MESH_MODEL_LIGHT_LIGHTNESS_CLI(cli_pub, cli_data) \ + BLE_MESH_MODEL(BLE_MESH_MODEL_ID_LIGHT_LIGHTNESS_CLI, \ + light_lightness_cli_op, cli_pub, cli_data) + +typedef bt_mesh_client_common_t bt_mesh_light_lightness_cli_t; + +struct bt_mesh_light_lightness_status { + bool op_en; /* Indicate whether optional parameters included */ + u16_t present_lightness; /* Present value of light lightness actual state */ + u16_t target_lightness; /* Target value of light lightness actual state (optional) */ + u8_t remain_time; /* Time to complete state transition (C.1) */ +}; + +struct bt_mesh_light_lightness_linear_status { + bool op_en; /* Indicate whether optional parameters included */ + u16_t present_lightness; /* Present value of light lightness linear state */ + u16_t target_lightness; /* Target value of light lightness linear state (optional) */ + u8_t remain_time; /* Time to complete state transition (C.1) */ +}; + +struct bt_mesh_light_lightness_last_status { + u16_t lightness; /* The value of the Light Lightness Last state */ +}; + +struct bt_mesh_light_lightness_default_status { + u16_t lightness; /* The value of the Light Lightness default state */ +}; + +struct bt_mesh_light_lightness_range_status { + u8_t status_code; /* Status Code for the requesting message */ + u16_t range_min; /* Value of range min field of light lightness range state */ + u16_t range_max; /* Value of range max field of light lightness range state */ +}; + +struct bt_mesh_light_lightness_set { + bool op_en; /* Indicate whether optional parameters included */ + u16_t lightness; /* Target value of light lightness actual state */ + u8_t tid; /* Transaction Identifier */ + u8_t trans_time; /* Time to complete state transition (optional) */ + u8_t delay; /* Indicate message execution delay (C.1) */ +}; + +struct bt_mesh_light_lightness_linear_set { + bool op_en; /* Indicate whether optional parameters included */ + u16_t lightness; /* Target value of light lightness linear state */ + u8_t tid; /* Transaction Identifier */ + u8_t trans_time; /* Time to complete state transition (optional) */ + u8_t delay; /* Indicate message execution delay (C.1) */ +}; + +struct bt_mesh_light_lightness_default_set { + u16_t lightness; /* The value of the Light Lightness Default state */ +}; + +struct bt_mesh_light_lightness_range_set { + u16_t range_min; /* Value of range min field of light lightness range state */ + u16_t range_max; /* Value of range max field of light lightness range state */ +}; + +/* Light CTL Client Model Context */ +extern const struct bt_mesh_model_op light_ctl_cli_op[]; + +/** @def BLE_MESH_MODEL_LIGHT_CTL_CLI + * + * Define a new light CTL client model. Note that this API needs + * to be repeated for each element which the application wants to + * have a light CTL client model on. + * @param cli_pub Pointer to a unique struct bt_mesh_model_pub. + * @param cli_data Pointer to a unique struct bt_mesh_light_ctl_cli. + * + * @return New light CTL client model instance. + */ +#define BLE_MESH_MODEL_LIGHT_CTL_CLI(cli_pub, cli_data) \ + BLE_MESH_MODEL(BLE_MESH_MODEL_ID_LIGHT_CTL_CLI, \ + light_ctl_cli_op, cli_pub, cli_data) + +typedef bt_mesh_client_common_t bt_mesh_light_ctl_cli_t; + +struct bt_mesh_light_ctl_status { + bool op_en; /* Indicate whether optional parameters included */ + u16_t present_ctl_lightness; /* Present value of light ctl lightness state */ + u16_t present_ctl_temperature; /* Present value of light ctl temperature state */ + u16_t target_ctl_lightness; /* Target value of light ctl lightness state (optional) */ + u16_t target_ctl_temperature; /* Target value of light ctl temperature state (C.1) */ + u8_t remain_time; /* Time to complete state transition (C.1) */ +}; + +struct bt_mesh_light_ctl_temperature_status { + bool op_en; /* Indicate whether optional parameters included */ + u16_t present_ctl_temperature; /* Present value of light ctl temperature state */ + u16_t present_ctl_delta_uv; /* Present value of light ctl delta UV state */ + u16_t target_ctl_temperature; /* Target value of light ctl temperature state (optional) */ + u16_t target_ctl_delta_uv; /* Target value of light ctl delta UV state (C.1) */ + u8_t remain_time; /* Time to complete state transition (C.1) */ +}; + +struct bt_mesh_light_ctl_temperature_range_status { + u8_t status_code; /* Status code for the requesting message */ + u16_t range_min; /* Value of temperature range min field of light ctl temperature range state */ + u16_t range_max; /* Value of temperature range max field of light ctl temperature range state */ +}; + +struct bt_mesh_light_ctl_default_status { + u16_t lightness; /* Value of light lightness default state */ + u16_t temperature; /* Value of light temperature default state */ + s16_t delta_uv; /* Value of light delta UV default state */ +}; + +struct bt_mesh_light_ctl_set { + bool op_en; /* Indicate whether optional parameters included */ + u16_t ctl_lightness; /* Target value of light ctl lightness state */ + u16_t ctl_temperature; /* Target value of light ctl temperature state */ + s16_t ctl_delta_uv; /* Target value of light ctl delta UV state */ + u8_t tid; /* Transaction Identifier */ + u8_t trans_time; /* Time to complete state transition (optional) */ + u8_t delay; /* Indicate message execution delay (C.1) */ +}; + +struct bt_mesh_light_ctl_temperature_set { + bool op_en; /* Indicate whether optional parameters included */ + u16_t ctl_temperature; /* Target value of light ctl temperature state */ + s16_t ctl_delta_uv; /* Target value of light ctl delta UV state */ + u8_t tid; /* Transaction Identifier */ + u8_t trans_time; /* Time to complete state transition (optional) */ + u8_t delay; /* Indicate message execution delay (C.1) */ +}; + +struct bt_mesh_light_ctl_temperature_range_set { + u16_t range_min; /* Value of temperature range min field of light ctl temperature range state */ + u16_t range_max; /* Value of temperature range max field of light ctl temperature range state */ +}; + +struct bt_mesh_light_ctl_default_set { + u16_t lightness; /* Value of light lightness default state */ + u16_t temperature; /* Value of light temperature default state */ + s16_t delta_uv; /* Value of light delta UV default state */ +}; + +/* Light HSL Client Model Context */ +extern const struct bt_mesh_model_op light_hsl_cli_op[]; + +/** @def BLE_MESH_MODEL_LIGHT_HSL_CLI + * + * Define a new light HSL client model. Note that this API needs + * to be repeated for each element which the application wants to + * have a light HSL client model on. + * @param cli_pub Pointer to a unique struct bt_mesh_model_pub. + * @param cli_data Pointer to a unique struct bt_mesh_light_hsl_cli. + * + * @return New light HSL client model instance. + */ +#define BLE_MESH_MODEL_LIGHT_HSL_CLI(cli_pub, cli_data) \ + BLE_MESH_MODEL(BLE_MESH_MODEL_ID_LIGHT_HSL_CLI, \ + light_hsl_cli_op, cli_pub, cli_data) + +typedef bt_mesh_client_common_t bt_mesh_light_hsl_cli_t; + +struct bt_mesh_light_hsl_status { + bool op_en; /* Indicate whether optional parameters included */ + u16_t hsl_lightness; /* Present value of light hsl lightness state */ + u16_t hsl_hue; /* Present value of light hsl hue state */ + u16_t hsl_saturation; /* Present value of light hsl saturation state */ + u8_t remain_time; /* Time to complete state transition (optional) */ +}; + +struct bt_mesh_light_hsl_target_status { + bool op_en; /* Indicate whether optional parameters included */ + u16_t hsl_lightness_target; /* Target value of light hsl lightness state */ + u16_t hsl_hue_target; /* Target value of light hsl hue state */ + u16_t hsl_saturation_target; /* Target value of light hsl saturation state */ + u8_t remain_time; /* Time to complete state transition (optional) */ +}; + +struct bt_mesh_light_hsl_hue_status { + bool op_en; /* Indicate whether optional parameters included */ + u16_t present_hue; /* Present value of light hsl hue state */ + u16_t target_hue; /* Target value of light hsl hue state (optional) */ + u8_t remain_time; /* Time to complete state transition (C.1) */ +}; + +struct bt_mesh_light_hsl_saturation_status { + bool op_en; /* Indicate whether optional parameters included */ + u16_t present_saturation; /* Present value of light hsl saturation state */ + u16_t target_saturation; /* Target value of light hsl saturation state (optional) */ + u8_t remain_time; /* Time to complete state transition (C.1) */ +}; + +struct bt_mesh_light_hsl_default_status { + u16_t lightness; /* Value of light lightness default state */ + u16_t hue; /* Value of light hue default state */ + u16_t saturation; /* Value of light saturation default state */ +}; + +struct bt_mesh_light_hsl_range_status { + u8_t status_code; /* Status code for the requesting message */ + u16_t hue_range_min; /* Value of hue range min field of light hsl hue range state */ + u16_t hue_range_max; /* Value of hue range max field of light hsl hue range state */ + u16_t saturation_range_min; /* Value of saturation range min field of light hsl saturation range state */ + u16_t saturation_range_max; /* Value of saturation range max field of light hsl saturation range state */ +}; + +struct bt_mesh_light_hsl_set { + bool op_en; /* Indicate whether optional parameters included */ + u16_t hsl_lightness; /* Target value of light hsl lightness state */ + u16_t hsl_hue; /* Target value of light hsl hue state */ + u16_t hsl_saturation; /* Target value of light hsl saturation state */ + u8_t tid; /* Transaction Identifier */ + u8_t trans_time; /* Time to complete state transition (optional) */ + u8_t delay; /* Indicate message execution delay (C.1) */ +}; + +struct bt_mesh_light_hsl_hue_set { + bool op_en; /* Indicate whether optional parameters included */ + u16_t hue; /* Target value of light hsl hue state */ + u8_t tid; /* Transaction Identifier */ + u8_t trans_time; /* Time to complete state transition (optional) */ + u8_t delay; /* Indicate message execution delay (C.1) */ +}; + +struct bt_mesh_light_hsl_saturation_set { + bool op_en; /* Indicate whether optional parameters included */ + u16_t saturation; /* Target value of light hsl hue state */ + u8_t tid; /* Transaction Identifier */ + u8_t trans_time; /* Time to complete state transition (optional) */ + u8_t delay; /* Indicate message execution delay (C.1) */ +}; + +struct bt_mesh_light_hsl_default_set { + u16_t lightness; /* Value of light lightness default state */ + u16_t hue; /* Value of light hue default state */ + u16_t saturation; /* Value of light saturation default state */ +}; + +struct bt_mesh_light_hsl_range_set { + u16_t hue_range_min; /* Value of hue range min field of light hsl hue range state */ + u16_t hue_range_max; /* Value of hue range max field of light hsl hue range state */ + u16_t saturation_range_min; /* Value of saturation range min field of light hsl saturation range state */ + u16_t saturation_range_max; /* Value of saturation range max field of light hsl saturation range state */ +}; + +/* Light xyL Client Model Context */ +extern const struct bt_mesh_model_op light_xyl_cli_op[]; + +/** @def BLE_MESH_MODEL_LIGHT_XYL_CLI + * + * Define a new light xyL client model. Note that this API needs + * to be repeated for each element which the application wants + * to have a light xyL client model on. + * @param cli_pub Pointer to a unique struct bt_mesh_model_pub. + * @param cli_data Pointer to a unique struct bt_mesh_light_xyl_cli. + * + * @return New light xyL client model instance. + */ +#define BLE_MESH_MODEL_LIGHT_XYL_CLI(cli_pub, cli_data) \ + BLE_MESH_MODEL(BLE_MESH_MODEL_ID_LIGHT_XYL_CLI, \ + light_xyl_cli_op, cli_pub, cli_data) + +typedef bt_mesh_client_common_t bt_mesh_light_xyl_cli_t; + +struct bt_mesh_light_xyl_status { + bool op_en; /* Indicate whether optional parameters included */ + u16_t xyl_lightness; /* The present value of the Light xyL Lightness state */ + u16_t xyl_x; /* The present value of the Light xyL x state */ + u16_t xyl_y; /* The present value of the Light xyL y state */ + u8_t remain_time; /* Time to complete state transition (optional) */ +}; + +struct bt_mesh_light_xyl_target_status { + bool op_en; /* Indicate whether optional parameters included */ + u16_t target_xyl_lightness; /* The target value of the Light xyL Lightness state */ + u16_t target_xyl_x; /* The target value of the Light xyL x state */ + u16_t target_xyl_y; /* The target value of the Light xyL y state */ + u8_t remain_time; /* Time to complete state transition (optional) */ +}; + +struct bt_mesh_light_xyl_default_status { + u16_t lightness; /* The value of the Light Lightness Default state */ + u16_t xyl_x; /* The value of the Light xyL x Default state */ + u16_t xyl_y; /* The value of the Light xyL y Default state */ +}; + +struct bt_mesh_light_xyl_range_status { + u8_t status_code; /* Status Code for the requesting message */ + u16_t xyl_x_range_min; /* The value of the xyL x Range Min field of the Light xyL x Range state */ + u16_t xyl_x_range_max; /* The value of the xyL x Range Max field of the Light xyL x Range state */ + u16_t xyl_y_range_min; /* The value of the xyL y Range Min field of the Light xyL y Range state */ + u16_t xyl_y_range_max; /* The value of the xyL y Range Max field of the Light xyL y Range state */ +}; + +struct bt_mesh_light_xyl_set { + bool op_en; /* Indicate whether optional parameters included */ + u16_t xyl_lightness; /* The target value of the Light xyL Lightness state */ + u16_t xyl_x; /* The target value of the Light xyL x state */ + u16_t xyl_y; /* The target value of the Light xyL y state */ + u8_t tid; /* Transaction Identifier */ + u8_t trans_time; /* Time to complete state transition (optional) */ + u8_t delay; /* Indicate message execution delay (C.1) */ +}; + +struct bt_mesh_light_xyl_default_set { + u16_t lightness; /* The value of the Light Lightness Default state */ + u16_t xyl_x; /* The value of the Light xyL x Default state */ + u16_t xyl_y; /* The value of the Light xyL y Default state */ +}; + +struct bt_mesh_light_xyl_range_set { + u16_t xyl_x_range_min; /* The value of the xyL x Range Min field of the Light xyL x Range state */ + u16_t xyl_x_range_max; /* The value of the xyL x Range Max field of the Light xyL x Range state */ + u16_t xyl_y_range_min; /* The value of the xyL y Range Min field of the Light xyL y Range state */ + u16_t xyl_y_range_max; /* The value of the xyL y Range Max field of the Light xyL y Range state */ +}; + +/* Light LC Client Model Context */ +extern const struct bt_mesh_model_op light_lc_cli_op[]; + +/** @def BLE_MESH_MODEL_LIGHT_LC_CLI + * + * Define a new light lc client model. Note that this API needs + * to be repeated for each element which the application wants + * to have a light lc client model on. + * @param cli_pub Pointer to a unique struct bt_mesh_model_pub. + * @param cli_data Pointer to a unique struct bt_mesh_light_lc_cli. + * + * @return New light lc client model instance. + */ +#define BLE_MESH_MODEL_LIGHT_LC_CLI(cli_pub, cli_data) \ + BLE_MESH_MODEL(BLE_MESH_MODEL_ID_LIGHT_LC_CLI, \ + light_lc_cli_op, cli_pub, cli_data) + +typedef bt_mesh_client_common_t bt_mesh_light_lc_cli_t; + +struct bt_mesh_light_lc_mode_status { + u8_t mode; /* The present value of the Light LC Mode state */ +}; + +struct bt_mesh_light_lc_om_status { + u8_t mode; /* The present value of the Light LC Occupancy Mode state */ +}; + +struct bt_mesh_light_lc_light_onoff_status { + bool op_en; /* Indicate whether optional parameters included */ + u8_t present_light_onoff; /* The present value of the Light LC Light OnOff state */ + u8_t target_light_onoff; /* The target value of the Light LC Light OnOff state (Optional) */ + u8_t remain_time; /* Time to complete state transition (C.1) */ +}; + +struct bt_mesh_light_lc_property_status { + u16_t light_lc_property_id; /* Property ID identifying a Light LC Property */ + struct net_buf_simple *light_lc_property_value; /* Raw value for the Light LC Property */ +}; + +struct bt_mesh_light_lc_mode_set { + u8_t mode; /* The target value of the Light LC Mode state */ +}; + +struct bt_mesh_light_lc_om_set { + u8_t mode; /* The target value of the Light LC Occupancy Mode state */ +}; + +struct bt_mesh_light_lc_light_onoff_set { + bool op_en; /* Indicate whether optional parameters included */ + u8_t light_onoff; /* The target value of the Light LC Light OnOff state */ + u8_t tid; /* Transaction Identifier */ + u8_t trans_time; /* Time to complete state transition (optional) */ + u8_t delay; /* Indicate message execution delay (C.1) */ +}; + +struct bt_mesh_light_lc_property_get { + u16_t light_lc_property_id; /* Property ID identifying a Light LC Property */ +}; + +struct bt_mesh_light_lc_property_set { + u16_t light_lc_property_id; /* Property ID identifying a Light LC Property */ + struct net_buf_simple *light_lc_property_value; /* Raw value for the Light LC Property */ +}; + +/** + * @brief This function is called to initialize light lightness client model user_data. + * + * @param[in] model: Pointer to light lightness client model + * @param[in] primary: Whether belongs to primary element + * + * @return Zero-success, other-fail + */ +int bt_mesh_light_lightness_cli_init(struct bt_mesh_model *model, bool primary); + +/** + * @brief This function is called to initialize light ctl client model user_data. + * + * @param[in] model: Pointer to light ctl client model + * @param[in] primary: Whether belongs to primary element + * + * @return Zero-success, other-fail + */ +int bt_mesh_light_ctl_cli_init(struct bt_mesh_model *model, bool primary); + +/** + * @brief This function is called to initialize light hsl client model user_data. + * + * @param[in] model: Pointer to light hsl client model + * @param[in] primary: Whether belongs to primary element + * + * @return Zero-success, other-fail + */ +int bt_mesh_light_hsl_cli_init(struct bt_mesh_model *model, bool primary); + +/** + * @brief This function is called to initialize light xyl client model user_data. + * + * @param[in] model: Pointer to light xyl client model + * @param[in] primary: Whether belongs to primary element + * + * @return Zero-success, other-fail + */ +int bt_mesh_light_xyl_cli_init(struct bt_mesh_model *model, bool primary); + +/** + * @brief This function is called to initialize light lc client model user_data. + * + * @param[in] model: Pointer to light lc client model + * @param[in] primary: Whether belongs to primary element + * + * @return Zero-success, other-fail + */ +int bt_mesh_light_lc_cli_init(struct bt_mesh_model *model, bool primary); + +/** + * @brief This function is called to get light states. + * + * @param[in] common: Message common information structure + * @param[in] get: Pointer of light get message value + * @param[out] status: Pointer of light status message value + * + * @return Zero-success, other-fail + */ +int bt_mesh_light_client_get_state(struct bt_mesh_common_param *common, void *get, void *status); + +/** + * @brief This function is called to set light states. + * + * @param[in] common: Message common information structure + * @param[in] set: Pointer of light set message value + * @param[out] status: Pointer of light status message value + * + * @return Zero-success, other-fail + */ +int bt_mesh_light_client_set_state(struct bt_mesh_common_param *common, void *set, void *status); + +#endif /* _LIGHTING_CLIENT_H_ */ diff --git a/components/bt/ble_mesh/mesh_models/include/mesh_common.h b/components/bt/ble_mesh/mesh_models/include/mesh_common.h new file mode 100644 index 0000000000..2a116be01e --- /dev/null +++ b/components/bt/ble_mesh/mesh_models/include/mesh_common.h @@ -0,0 +1,46 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +/** @file + * @brief Bluetooth Mesh Model Common APIs. + */ + +#ifndef _MESH_COMMON_H_ +#define _MESH_COMMON_H_ + +#include "osi/allocator.h" + +#include "mesh_types.h" +#include "mesh_buf.h" +#include "mesh_trace.h" + +/** + * @brief This function allocates memory to store outgoing message. + * + * @param[in] size: Length of memory allocated to store message value + * + * @return NULL-fail, pointer of a net_buf_simple structure-success + */ +struct net_buf_simple *bt_mesh_alloc_buf(u16_t size); + +/** + * @brief This function releases the memory allocated for the outgoing message. + * + * @param[in] buf: Pointer to the net_buf_simple structure to be freed + * + * @return none + */ +void bt_mesh_free_buf(struct net_buf_simple *buf); + +#endif /* _MESH_COMMON_H_ */ \ No newline at end of file diff --git a/components/bt/ble_mesh/mesh_models/include/model_common.h b/components/bt/ble_mesh/mesh_models/include/model_common.h new file mode 100644 index 0000000000..fd9cc6dfda --- /dev/null +++ b/components/bt/ble_mesh/mesh_models/include/model_common.h @@ -0,0 +1,133 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#ifndef _MODEL_COMMON_H_ +#define _MODEL_COMMON_H_ + +#include "mesh_access.h" + +/** Mesh Client Model Context */ +typedef struct { + u32_t cli_op; /* The client opcode */ + u32_t status_op; /* The server status opcode corresponding to the client opcode */ +} bt_mesh_client_op_pair_t; + +/** Mesh Client Model Context */ +typedef struct { + struct bt_mesh_model *model; + int op_pair_size; /* the size of op_pair */ + const bt_mesh_client_op_pair_t *op_pair; + /** + * @brief This function is a callback function used to push the received unsolicited + * messages to the application layer. + * + * @param[in] opcode: Opcode of received status message + * @param[in] model: Model associated with the status message + * @param[in] ctx: Context information of the status message + * @param[in] buf: Buffer contains the status message value + * + * @return None + */ + void (*publish_status)(u32_t opcode, struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf); + void *internal_data; /* Pointer of the structure of internal data */ + u8_t msg_role; /* device role of the tx message */ +} bt_mesh_client_common_t; + +typedef struct { + sys_slist_t queue; +} bt_mesh_internal_data_t; + +typedef struct { + sys_snode_t client_node; + struct bt_mesh_msg_ctx ctx; + u32_t opcode; /* Indicate the opcode of the message sending */ + u32_t op_pending; /* Indicate the status message waiting for */ + struct k_delayed_work timer; /* Message send Timer. Only for stack-internal use. */ +} bt_mesh_client_node_t; + +int bt_mesh_client_init(struct bt_mesh_model *model); + +/** + * @brief Check the msg is a publish msg or not + * + * @param model Mesh (client) Model that the message belongs to. + * @param ctx Message context, includes keys, TTL, etc. + * @param buf The message buffer + * @param need_pub Indicate if the msg sent to app layer as a publish msg + * @return 0 on success, or (negative) error code on failure. + */ +bt_mesh_client_node_t *bt_mesh_is_model_message_publish(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf, + bool need_pub); + +bool bt_mesh_client_find_opcode_in_list(sys_slist_t *list, u32_t opcode); + +bool bt_mesh_client_check_node_in_list(sys_slist_t *list, uint16_t tx_dst); + +bt_mesh_client_node_t *bt_mesh_client_pick_node(sys_slist_t *list, u16_t tx_dst); + +int bt_mesh_client_send_msg(struct bt_mesh_model *model, + u32_t opcode, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *msg, + k_work_handler_t timer_handler, + s32_t timeout, bool need_ack, + const struct bt_mesh_send_cb *cb, void *cb_data); + +int bt_mesh_client_free_node(sys_slist_t *queue, bt_mesh_client_node_t *node); + +enum { + NODE = 0, + PROVISIONER, + FAST_PROV, +}; + +#define ROLE_NVAL 0xFF + +struct bt_mesh_common_param { + u32_t opcode; /* Message opcode */ + struct bt_mesh_model *model; /* Pointer to cli structure */ + struct bt_mesh_msg_ctx ctx; /* Message context */ + s32_t msg_timeout; /* Time to get response messages */ + const struct bt_mesh_send_cb *cb; /* User defined callback function */ + void *cb_data; /* Data as parameter of the cb function */ +}; + +typedef struct bt_mesh_role_param { + struct bt_mesh_model *model; /* The client model structure */ + u8_t role; /* Role of the device - Node/Provisioner */ +} bt_mesh_role_param_t; + +/** + * @brief This function copies node_index for stack internal use. + * + * @param[in] common: Pointer to the struct bt_mesh_role_param structure + * + * @return Zero - success, otherwise - fail + */ +int bt_mesh_set_model_role(bt_mesh_role_param_t *common); + +/** + * @brief This function gets msg role for stack internal use. + * + * @param[in] model: Pointer to the model structure + * @param[in] srv_send: Indicate if the message is sent by a server model + * + * @return 0 - Node, 1 - Provisioner + */ +u8_t bt_mesh_get_model_role(struct bt_mesh_model *model, bool srv_send); + +#endif /* _MODEL_COMMON_H_ */ + diff --git a/components/bt/ble_mesh/mesh_models/include/model_opcode.h b/components/bt/ble_mesh/mesh_models/include/model_opcode.h new file mode 100644 index 0000000000..01929d5cfe --- /dev/null +++ b/components/bt/ble_mesh/mesh_models/include/model_opcode.h @@ -0,0 +1,276 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#ifndef _MODEL_OPCODE_H_ +#define _MODEL_OPCODE_H_ + +#include "mesh_main.h" + +/* Generic OnOff Message Opcode */ +#define BLE_MESH_MODEL_OP_GEN_ONOFF_GET BLE_MESH_MODEL_OP_2(0x82, 0x01) +#define BLE_MESH_MODEL_OP_GEN_ONOFF_SET BLE_MESH_MODEL_OP_2(0x82, 0x02) +#define BLE_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK BLE_MESH_MODEL_OP_2(0x82, 0x03) +#define BLE_MESH_MODEL_OP_GEN_ONOFF_STATUS BLE_MESH_MODEL_OP_2(0x82, 0x04) + +/* Generic Level Message Opcode */ +#define BLE_MESH_MODEL_OP_GEN_LEVEL_GET BLE_MESH_MODEL_OP_2(0x82, 0x05) +#define BLE_MESH_MODEL_OP_GEN_LEVEL_SET BLE_MESH_MODEL_OP_2(0x82, 0x06) +#define BLE_MESH_MODEL_OP_GEN_LEVEL_SET_UNACK BLE_MESH_MODEL_OP_2(0x82, 0x07) +#define BLE_MESH_MODEL_OP_GEN_LEVEL_STATUS BLE_MESH_MODEL_OP_2(0x82, 0x08) +#define BLE_MESH_MODEL_OP_GEN_DELTA_SET BLE_MESH_MODEL_OP_2(0x82, 0x09) +#define BLE_MESH_MODEL_OP_GEN_DELTA_SET_UNACK BLE_MESH_MODEL_OP_2(0x82, 0x0A) +#define BLE_MESH_MODEL_OP_GEN_MOVE_SET BLE_MESH_MODEL_OP_2(0x82, 0x0B) +#define BLE_MESH_MODEL_OP_GEN_MOVE_SET_UNACK BLE_MESH_MODEL_OP_2(0x82, 0x0C) + +/* Generic Default Transition Time Message Opcode*/ +#define BLE_MESH_MODEL_OP_GEN_DEF_TRANS_TIME_GET BLE_MESH_MODEL_OP_2(0x82, 0x0D) +#define BLE_MESH_MODEL_OP_GEN_DEF_TRANS_TIME_SET BLE_MESH_MODEL_OP_2(0x82, 0x0E) +#define BLE_MESH_MODEL_OP_GEN_DEF_TRANS_TIME_SET_UNACK BLE_MESH_MODEL_OP_2(0x82, 0x0F) +#define BLE_MESH_MODEL_OP_GEN_DEF_TRANS_TIME_STATUS BLE_MESH_MODEL_OP_2(0x82, 0x10) + +/* Generic Power OnOff Message Opcode*/ +#define BLE_MESH_MODEL_OP_GEN_ONPOWERUP_GET BLE_MESH_MODEL_OP_2(0x82, 0x11) +#define BLE_MESH_MODEL_OP_GEN_ONPOWERUP_STATUS BLE_MESH_MODEL_OP_2(0x82, 0x12) + +/* Generic Power OnOff Setup Message Opcode */ +#define BLE_MESH_MODEL_OP_GEN_ONPOWERUP_SET BLE_MESH_MODEL_OP_2(0x82, 0x13) +#define BLE_MESH_MODEL_OP_GEN_ONPOWERUP_SET_UNACK BLE_MESH_MODEL_OP_2(0x82, 0x14) + +/* Generic Power Level Message Opcode */ +#define BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_GET BLE_MESH_MODEL_OP_2(0x82, 0x15) +#define BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_SET BLE_MESH_MODEL_OP_2(0x82, 0x16) +#define BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_SET_UNACK BLE_MESH_MODEL_OP_2(0x82, 0x17) +#define BLE_MESH_MODEL_OP_GEN_POWER_LEVEL_STATUS BLE_MESH_MODEL_OP_2(0x82, 0x18) +#define BLE_MESH_MODEL_OP_GEN_POWER_LAST_GET BLE_MESH_MODEL_OP_2(0x82, 0x19) +#define BLE_MESH_MODEL_OP_GEN_POWER_LAST_STATUS BLE_MESH_MODEL_OP_2(0x82, 0x1A) +#define BLE_MESH_MODEL_OP_GEN_POWER_DEFAULT_GET BLE_MESH_MODEL_OP_2(0x82, 0x1B) +#define BLE_MESH_MODEL_OP_GEN_POWER_DEFAULT_STATUS BLE_MESH_MODEL_OP_2(0x82, 0x1C) +#define BLE_MESH_MODEL_OP_GEN_POWER_RANGE_GET BLE_MESH_MODEL_OP_2(0x82, 0x1D) +#define BLE_MESH_MODEL_OP_GEN_POWER_RANGE_STATUS BLE_MESH_MODEL_OP_2(0x82, 0x1E) + +/* Generic Power Level Setup Message Opcode */ +#define BLE_MESH_MODEL_OP_GEN_POWER_DEFAULT_SET BLE_MESH_MODEL_OP_2(0x82, 0x1F) +#define BLE_MESH_MODEL_OP_GEN_POWER_DEFAULT_SET_UNACK BLE_MESH_MODEL_OP_2(0x82, 0x20) +#define BLE_MESH_MODEL_OP_GEN_POWER_RANGE_SET BLE_MESH_MODEL_OP_2(0x82, 0x21) +#define BLE_MESH_MODEL_OP_GEN_POWER_RANGE_SET_UNACK BLE_MESH_MODEL_OP_2(0x82, 0x22) + +/* Generic Battery Message Opcode */ +#define BLE_MESH_MODEL_OP_GEN_BATTERY_GET BLE_MESH_MODEL_OP_2(0x82, 0x23) +#define BLE_MESH_MODEL_OP_GEN_BATTERY_STATUS BLE_MESH_MODEL_OP_2(0x82, 0x24) + +/* Generic Location Message Opcode */ +#define BLE_MESH_MODEL_OP_GEN_LOC_GLOBAL_GET BLE_MESH_MODEL_OP_2(0x82, 0x25) +#define BLE_MESH_MODEL_OP_GEN_LOC_GLOBAL_STATUS BLE_MESH_MODEL_OP_1(0x40) +#define BLE_MESH_MODEL_OP_GEN_LOC_LOCAL_GET BLE_MESH_MODEL_OP_2(0x82, 0x26) +#define BLE_MESH_MODEL_OP_GEN_LOC_LOCAL_STATUS BLE_MESH_MODEL_OP_2(0x82, 0x27) + +/* Generic Location Setup Message Opcode */ +#define BLE_MESH_MODEL_OP_GEN_LOC_GLOBAL_SET BLE_MESH_MODEL_OP_1(0x41) +#define BLE_MESH_MODEL_OP_GEN_LOC_GLOBAL_SET_UNACK BLE_MESH_MODEL_OP_1(0x42) +#define BLE_MESH_MODEL_OP_GEN_LOC_LOCAL_SET BLE_MESH_MODEL_OP_2(0x82, 0x28) +#define BLE_MESH_MODEL_OP_GEN_LOC_LOCAL_SET_UNACK BLE_MESH_MODEL_OP_2(0x82, 0x29) + +/* Generic Manufacturer Property Message Opcode */ +#define BLE_MESH_MODEL_OP_GEN_MANU_PROPERTIES_GET BLE_MESH_MODEL_OP_2(0x82, 0x2A) +#define BLE_MESH_MODEL_OP_GEN_MANU_PROPERTIES_STATUS BLE_MESH_MODEL_OP_1(0x43) +#define BLE_MESH_MODEL_OP_GEN_MANU_PROPERTY_GET BLE_MESH_MODEL_OP_2(0x82, 0x2B) +#define BLE_MESH_MODEL_OP_GEN_MANU_PROPERTY_SET BLE_MESH_MODEL_OP_1(0x44) +#define BLE_MESH_MODEL_OP_GEN_MANU_PROPERTY_SET_UNACK BLE_MESH_MODEL_OP_1(0x45) +#define BLE_MESH_MODEL_OP_GEN_MANU_PROPERTY_STATUS BLE_MESH_MODEL_OP_1(0x46) + +/* Generic Admin Property Message Opcode */ +#define BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTIES_GET BLE_MESH_MODEL_OP_2(0x82, 0x2C) +#define BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTIES_STATUS BLE_MESH_MODEL_OP_1(0x47) +#define BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_GET BLE_MESH_MODEL_OP_2(0x82, 0x2D) +#define BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_SET BLE_MESH_MODEL_OP_1(0x48) +#define BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_SET_UNACK BLE_MESH_MODEL_OP_1(0x49) +#define BLE_MESH_MODEL_OP_GEN_ADMIN_PROPERTY_STATUS BLE_MESH_MODEL_OP_1(0x4A) + +/* Generic User Property Message Opcode */ +#define BLE_MESH_MODEL_OP_GEN_USER_PROPERTIES_GET BLE_MESH_MODEL_OP_2(0x82, 0x2E) +#define BLE_MESH_MODEL_OP_GEN_USER_PROPERTIES_STATUS BLE_MESH_MODEL_OP_1(0x4B) +#define BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_GET BLE_MESH_MODEL_OP_2(0x82, 0x2F) +#define BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_SET BLE_MESH_MODEL_OP_1(0x4C) +#define BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_SET_UNACK BLE_MESH_MODEL_OP_1(0x4D) +#define BLE_MESH_MODEL_OP_GEN_USER_PROPERTY_STATUS BLE_MESH_MODEL_OP_1(0x4E) + +/* Generic Client Property Message Opcode */ +#define BLE_MESH_MODEL_OP_GEN_CLIENT_PROPERTIES_GET BLE_MESH_MODEL_OP_1(0x4F) +#define BLE_MESH_MODEL_OP_GEN_CLIENT_PROPERTIES_STATUS BLE_MESH_MODEL_OP_1(0x50) + +/* Sensor Message Opcode */ +#define BLE_MESH_MODEL_OP_SENSOR_DESCRIPTOR_GET BLE_MESH_MODEL_OP_2(0x82, 0x30) +#define BLE_MESH_MODEL_OP_SENSOR_DESCRIPTOR_STATUS BLE_MESH_MODEL_OP_1(0x51) +#define BLE_MESH_MODEL_OP_SENSOR_GET BLE_MESH_MODEL_OP_2(0x82, 0x31) +#define BLE_MESH_MODEL_OP_SENSOR_STATUS BLE_MESH_MODEL_OP_1(0x52) +#define BLE_MESH_MODEL_OP_SENSOR_COLUMN_GET BLE_MESH_MODEL_OP_2(0x82, 0x32) +#define BLE_MESH_MODEL_OP_SENSOR_COLUMN_STATUS BLE_MESH_MODEL_OP_1(0x53) +#define BLE_MESH_MODEL_OP_SENSOR_SERIES_GET BLE_MESH_MODEL_OP_2(0x82, 0x33) +#define BLE_MESH_MODEL_OP_SENSOR_SERIES_STATUS BLE_MESH_MODEL_OP_1(0x54) + +/* Sensor Setup Message Opcode */ +#define BLE_MESH_MODEL_OP_SENSOR_CADENCE_GET BLE_MESH_MODEL_OP_2(0x82, 0x34) +#define BLE_MESH_MODEL_OP_SENSOR_CADENCE_SET BLE_MESH_MODEL_OP_1(0x55) +#define BLE_MESH_MODEL_OP_SENSOR_CADENCE_SET_UNACK BLE_MESH_MODEL_OP_1(0x56) +#define BLE_MESH_MODEL_OP_SENSOR_CADENCE_STATUS BLE_MESH_MODEL_OP_1(0x57) +#define BLE_MESH_MODEL_OP_SENSOR_SETTINGS_GET BLE_MESH_MODEL_OP_2(0x82, 0x35) +#define BLE_MESH_MODEL_OP_SENSOR_SETTINGS_STATUS BLE_MESH_MODEL_OP_1(0x58) +#define BLE_MESH_MODEL_OP_SENSOR_SETTING_GET BLE_MESH_MODEL_OP_2(0x82, 0x36) +#define BLE_MESH_MODEL_OP_SENSOR_SETTING_SET BLE_MESH_MODEL_OP_1(0x59) +#define BLE_MESH_MODEL_OP_SENSOR_SETTING_SET_UNACK BLE_MESH_MODEL_OP_1(0x5A) +#define BLE_MESH_MODEL_OP_SENSOR_SETTING_STATUS BLE_MESH_MODEL_OP_1(0x5B) + +/* Time Message Opcode */ +#define BLE_MESH_MODEL_OP_TIME_GET BLE_MESH_MODEL_OP_2(0x82, 0x37) +#define BLE_MESH_MODEL_OP_TIME_SET BLE_MESH_MODEL_OP_1(0x5C) +#define BLE_MESH_MODEL_OP_TIME_STATUS BLE_MESH_MODEL_OP_1(0x5D) +#define BLE_MESH_MODEL_OP_TIME_ROLE_GET BLE_MESH_MODEL_OP_2(0x82, 0x38) +#define BLE_MESH_MODEL_OP_TIME_ROLE_SET BLE_MESH_MODEL_OP_2(0x82, 0x39) +#define BLE_MESH_MODEL_OP_TIME_ROLE_STATUS BLE_MESH_MODEL_OP_2(0x82, 0x3A) +#define BLE_MESH_MODEL_OP_TIME_ZONE_GET BLE_MESH_MODEL_OP_2(0x82, 0x3B) +#define BLE_MESH_MODEL_OP_TIME_ZONE_SET BLE_MESH_MODEL_OP_2(0x82, 0x3C) +#define BLE_MESH_MODEL_OP_TIME_ZONE_STATUS BLE_MESH_MODEL_OP_2(0x82, 0x3D) +#define BLE_MESH_MODEL_OP_TAI_UTC_DELTA_GET BLE_MESH_MODEL_OP_2(0x82, 0x3E) +#define BLE_MESH_MODEL_OP_TAI_UTC_DELTA_SET BLE_MESH_MODEL_OP_2(0x82, 0x3F) +#define BLE_MESH_MODEL_OP_TAI_UTC_DELTA_STATUS BLE_MESH_MODEL_OP_2(0x82, 0x40) + +/* Scene Message Opcode */ +#define BLE_MESH_MODEL_OP_SCENE_GET BLE_MESH_MODEL_OP_2(0x82, 0x41) +#define BLE_MESH_MODEL_OP_SCENE_RECALL BLE_MESH_MODEL_OP_2(0x82, 0x42) +#define BLE_MESH_MODEL_OP_SCENE_RECALL_UNACK BLE_MESH_MODEL_OP_2(0x82, 0x43) +#define BLE_MESH_MODEL_OP_SCENE_STATUS BLE_MESH_MODEL_OP_1(0x5E) +#define BLE_MESH_MODEL_OP_SCENE_REGISTER_GET BLE_MESH_MODEL_OP_2(0x82, 0x44) +#define BLE_MESH_MODEL_OP_SCENE_REGISTER_STATUS BLE_MESH_MODEL_OP_2(0x82, 0x45) + +/* Scene Setup Message Opcode */ +#define BLE_MESH_MODEL_OP_SCENE_STORE BLE_MESH_MODEL_OP_2(0x82, 0x46) +#define BLE_MESH_MODEL_OP_SCENE_STORE_UNACK BLE_MESH_MODEL_OP_2(0x82, 0x47) +#define BLE_MESH_MODEL_OP_SCENE_DELETE BLE_MESH_MODEL_OP_2(0x82, 0x9E) +#define BLE_MESH_MODEL_OP_SCENE_DELETE_UNACK BLE_MESH_MODEL_OP_2(0x82, 0x9F) + +/* Scheduler Message Opcode */ +#define BLE_MESH_MODEL_OP_SCHEDULER_ACT_GET BLE_MESH_MODEL_OP_2(0x82, 0x48) +#define BLE_MESH_MODEL_OP_SCHEDULER_ACT_STATUS BLE_MESH_MODEL_OP_1(0x5F) +#define BLE_MESH_MODEL_OP_SCHEDULER_GET BLE_MESH_MODEL_OP_2(0x82, 0x49) +#define BLE_MESH_MODEL_OP_SCHEDULER_STATUS BLE_MESH_MODEL_OP_2(0x82, 0x4A) + +/* Scheduler Setup Message Opcode */ +#define BLE_MESH_MODEL_OP_SCHEDULER_ACT_SET BLE_MESH_MODEL_OP_1(0x60) +#define BLE_MESH_MODEL_OP_SCHEDULER_ACT_SET_UNACK BLE_MESH_MODEL_OP_1(0x61) + +/* Light Lightness Message Opcode */ +#define BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_GET BLE_MESH_MODEL_OP_2(0x82, 0x4B) +#define BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_SET BLE_MESH_MODEL_OP_2(0x82, 0x4C) +#define BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_SET_UNACK BLE_MESH_MODEL_OP_2(0x82, 0x4D) +#define BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_STATUS BLE_MESH_MODEL_OP_2(0x82, 0x4E) +#define BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_GET BLE_MESH_MODEL_OP_2(0x82, 0x4F) +#define BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_SET BLE_MESH_MODEL_OP_2(0x82, 0x50) +#define BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_SET_UNACK BLE_MESH_MODEL_OP_2(0x82, 0x51) +#define BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_STATUS BLE_MESH_MODEL_OP_2(0x82, 0x52) +#define BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LAST_GET BLE_MESH_MODEL_OP_2(0x82, 0x53) +#define BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LAST_STATUS BLE_MESH_MODEL_OP_2(0x82, 0x54) +#define BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_DEFAULT_GET BLE_MESH_MODEL_OP_2(0x82, 0x55) +#define BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_DEFAULT_STATUS BLE_MESH_MODEL_OP_2(0x82, 0x56) +#define BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_RANGE_GET BLE_MESH_MODEL_OP_2(0x82, 0x57) +#define BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_RANGE_STATUS BLE_MESH_MODEL_OP_2(0x82, 0x58) + +/* Light Lightness Setup Message Opcode */ +#define BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_DEFAULT_SET BLE_MESH_MODEL_OP_2(0x82, 0x59) +#define BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_DEFAULT_SET_UNACK BLE_MESH_MODEL_OP_2(0x82, 0x5A) +#define BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_RANGE_SET BLE_MESH_MODEL_OP_2(0x82, 0x5B) +#define BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_RANGE_SET_UNACK BLE_MESH_MODEL_OP_2(0x82, 0x5C) + +/* Light CTL Message Opcode */ +#define BLE_MESH_MODEL_OP_LIGHT_CTL_GET BLE_MESH_MODEL_OP_2(0x82, 0x5D) +#define BLE_MESH_MODEL_OP_LIGHT_CTL_SET BLE_MESH_MODEL_OP_2(0x82, 0x5E) +#define BLE_MESH_MODEL_OP_LIGHT_CTL_SET_UNACK BLE_MESH_MODEL_OP_2(0x82, 0x5F) +#define BLE_MESH_MODEL_OP_LIGHT_CTL_STATUS BLE_MESH_MODEL_OP_2(0x82, 0x60) +#define BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_GET BLE_MESH_MODEL_OP_2(0x82, 0x61) +#define BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_RANGE_GET BLE_MESH_MODEL_OP_2(0x82, 0x62) +#define BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_RANGE_STATUS BLE_MESH_MODEL_OP_2(0x82, 0x63) +#define BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_SET BLE_MESH_MODEL_OP_2(0x82, 0x64) +#define BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_SET_UNACK BLE_MESH_MODEL_OP_2(0x82, 0x65) +#define BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_STATUS BLE_MESH_MODEL_OP_2(0x82, 0x66) +#define BLE_MESH_MODEL_OP_LIGHT_CTL_DEFAULT_GET BLE_MESH_MODEL_OP_2(0x82, 0x67) +#define BLE_MESH_MODEL_OP_LIGHT_CTL_DEFAULT_STATUS BLE_MESH_MODEL_OP_2(0x82, 0x68) + +/* Light CTL Setup Message Opcode */ +#define BLE_MESH_MODEL_OP_LIGHT_CTL_DEFAULT_SET BLE_MESH_MODEL_OP_2(0x82, 0x69) +#define BLE_MESH_MODEL_OP_LIGHT_CTL_DEFAULT_SET_UNACK BLE_MESH_MODEL_OP_2(0x82, 0x6A) +#define BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_RANGE_SET BLE_MESH_MODEL_OP_2(0x82, 0x6B) +#define BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_RANGE_SET_UNACK BLE_MESH_MODEL_OP_2(0x82, 0x6C) + +/* Light HSL Message Opcode */ +#define BLE_MESH_MODEL_OP_LIGHT_HSL_GET BLE_MESH_MODEL_OP_2(0x82, 0x6D) +#define BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_GET BLE_MESH_MODEL_OP_2(0x82, 0x6E) +#define BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_SET BLE_MESH_MODEL_OP_2(0x82, 0x6F) +#define BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_SET_UNACK BLE_MESH_MODEL_OP_2(0x82, 0x70) +#define BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_STATUS BLE_MESH_MODEL_OP_2(0x82, 0x71) +#define BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_GET BLE_MESH_MODEL_OP_2(0x82, 0x72) +#define BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_SET BLE_MESH_MODEL_OP_2(0x82, 0x73) +#define BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_SET_UNACK BLE_MESH_MODEL_OP_2(0x82, 0x74) +#define BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_STATUS BLE_MESH_MODEL_OP_2(0x82, 0x75) +#define BLE_MESH_MODEL_OP_LIGHT_HSL_SET BLE_MESH_MODEL_OP_2(0x82, 0x76) +#define BLE_MESH_MODEL_OP_LIGHT_HSL_SET_UNACK BLE_MESH_MODEL_OP_2(0x82, 0x77) +#define BLE_MESH_MODEL_OP_LIGHT_HSL_STATUS BLE_MESH_MODEL_OP_2(0x82, 0x78) +#define BLE_MESH_MODEL_OP_LIGHT_HSL_TARGET_GET BLE_MESH_MODEL_OP_2(0x82, 0x79) +#define BLE_MESH_MODEL_OP_LIGHT_HSL_TARGET_STATUS BLE_MESH_MODEL_OP_2(0x82, 0x7A) +#define BLE_MESH_MODEL_OP_LIGHT_HSL_DEFAULT_GET BLE_MESH_MODEL_OP_2(0x82, 0x7B) +#define BLE_MESH_MODEL_OP_LIGHT_HSL_DEFAULT_STATUS BLE_MESH_MODEL_OP_2(0x82, 0x7C) +#define BLE_MESH_MODEL_OP_LIGHT_HSL_RANGE_GET BLE_MESH_MODEL_OP_2(0x82, 0x7D) +#define BLE_MESH_MODEL_OP_LIGHT_HSL_RANGE_STATUS BLE_MESH_MODEL_OP_2(0x82, 0x7E) + +/* Light HSL Setup Message Opcode */ +#define BLE_MESH_MODEL_OP_LIGHT_HSL_DEFAULT_SET BLE_MESH_MODEL_OP_2(0x82, 0x7F) +#define BLE_MESH_MODEL_OP_LIGHT_HSL_DEFAULT_SET_UNACK BLE_MESH_MODEL_OP_2(0x82, 0x80) +#define BLE_MESH_MODEL_OP_LIGHT_HSL_RANGE_SET BLE_MESH_MODEL_OP_2(0x82, 0x81) +#define BLE_MESH_MODEL_OP_LIGHT_HSL_RANGE_SET_UNACK BLE_MESH_MODEL_OP_2(0x82, 0x82) /* Model spec is wrong */ + +/* Light xyL Message Opcode */ +#define BLE_MESH_MODEL_OP_LIGHT_XYL_GET BLE_MESH_MODEL_OP_2(0x82, 0x83) +#define BLE_MESH_MODEL_OP_LIGHT_XYL_SET BLE_MESH_MODEL_OP_2(0x82, 0x84) +#define BLE_MESH_MODEL_OP_LIGHT_XYL_SET_UNACK BLE_MESH_MODEL_OP_2(0x82, 0x85) +#define BLE_MESH_MODEL_OP_LIGHT_XYL_STATUS BLE_MESH_MODEL_OP_2(0x82, 0x86) +#define BLE_MESH_MODEL_OP_LIGHT_XYL_TARGET_GET BLE_MESH_MODEL_OP_2(0x82, 0x87) +#define BLE_MESH_MODEL_OP_LIGHT_XYL_TARGET_STATUS BLE_MESH_MODEL_OP_2(0x82, 0x88) +#define BLE_MESH_MODEL_OP_LIGHT_XYL_DEFAULT_GET BLE_MESH_MODEL_OP_2(0x82, 0x89) +#define BLE_MESH_MODEL_OP_LIGHT_XYL_DEFAULT_STATUS BLE_MESH_MODEL_OP_2(0x82, 0x8A) +#define BLE_MESH_MODEL_OP_LIGHT_XYL_RANGE_GET BLE_MESH_MODEL_OP_2(0x82, 0x8B) +#define BLE_MESH_MODEL_OP_LIGHT_XYL_RANGE_STATUS BLE_MESH_MODEL_OP_2(0x82, 0x8C) + +/* Light xyL Setup Message Opcode */ +#define BLE_MESH_MODEL_OP_LIGHT_XYL_DEFAULT_SET BLE_MESH_MODEL_OP_2(0x82, 0x8D) +#define BLE_MESH_MODEL_OP_LIGHT_XYL_DEFAULT_SET_UNACK BLE_MESH_MODEL_OP_2(0x82, 0x8E) +#define BLE_MESH_MODEL_OP_LIGHT_XYL_RANGE_SET BLE_MESH_MODEL_OP_2(0x82, 0x8F) +#define BLE_MESH_MODEL_OP_LIGHT_XYL_RANGE_SET_UNACK BLE_MESH_MODEL_OP_2(0x82, 0x90) + +/* Light Control Message Opcode */ +#define BLE_MESH_MODEL_OP_LIGHT_LC_MODE_GET BLE_MESH_MODEL_OP_2(0x82, 0x91) +#define BLE_MESH_MODEL_OP_LIGHT_LC_MODE_SET BLE_MESH_MODEL_OP_2(0x82, 0x92) +#define BLE_MESH_MODEL_OP_LIGHT_LC_MODE_SET_UNACK BLE_MESH_MODEL_OP_2(0x82, 0x93) +#define BLE_MESH_MODEL_OP_LIGHT_LC_MODE_STATUS BLE_MESH_MODEL_OP_2(0x82, 0x94) +#define BLE_MESH_MODEL_OP_LIGHT_LC_OM_GET BLE_MESH_MODEL_OP_2(0x82, 0x95) +#define BLE_MESH_MODEL_OP_LIGHT_LC_OM_SET BLE_MESH_MODEL_OP_2(0x82, 0x96) +#define BLE_MESH_MODEL_OP_LIGHT_LC_OM_SET_UNACK BLE_MESH_MODEL_OP_2(0x82, 0x97) +#define BLE_MESH_MODEL_OP_LIGHT_LC_OM_STATUS BLE_MESH_MODEL_OP_2(0x82, 0x98) +#define BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_GET BLE_MESH_MODEL_OP_2(0x82, 0x99) +#define BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_SET BLE_MESH_MODEL_OP_2(0x82, 0x9A) +#define BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_SET_UNACK BLE_MESH_MODEL_OP_2(0x82, 0x9B) +#define BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_STATUS BLE_MESH_MODEL_OP_2(0x82, 0x9C) +#define BLE_MESH_MODEL_OP_LIGHT_LC_PROPERTY_GET BLE_MESH_MODEL_OP_2(0x82, 0x9D) +#define BLE_MESH_MODEL_OP_LIGHT_LC_PROPERTY_SET BLE_MESH_MODEL_OP_1(0x62) +#define BLE_MESH_MODEL_OP_LIGHT_LC_PROPERTY_SET_UNACK BLE_MESH_MODEL_OP_1(0x63) +#define BLE_MESH_MODEL_OP_LIGHT_LC_PROPERTY_STATUS BLE_MESH_MODEL_OP_1(0x64) + +#endif /* _MODEL_OPCODE_H_ */ diff --git a/components/bt/ble_mesh/mesh_models/include/sensor_client.h b/components/bt/ble_mesh/mesh_models/include/sensor_client.h new file mode 100644 index 0000000000..2b259a8953 --- /dev/null +++ b/components/bt/ble_mesh/mesh_models/include/sensor_client.h @@ -0,0 +1,167 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +/** @file + * @brief Bluetooth Mesh Sensor Client Model APIs. + */ + +#ifndef _SENSOR_CLIENT_H_ +#define _SENSOR_CLIENT_H_ + +#include "mesh_access.h" +#include "mesh_kernel.h" + +#include "model_common.h" + +/* Sensor Client Model Context */ +extern const struct bt_mesh_model_op sensor_cli_op[]; + +/** @def BLE_MESH_MODEL_SENSOR_CLI + * + * Define a new sensor client model. Note that this API needs to + * be repeated for each element which the application wants to + * have a sensor client model on. + * @param cli_pub Pointer to a unique struct bt_mesh_model_pub. + * @param cli_data Pointer to a unique struct bt_mesh_sensor_cli. + * + * @return New sensor client model instance. + */ +#define BLE_MESH_MODEL_SENSOR_CLI(cli_pub, cli_data) \ + BLE_MESH_MODEL(BLE_MESH_MODEL_ID_SENSOR_CLI, \ + sensor_cli_op, cli_pub, cli_data) + +typedef bt_mesh_client_common_t bt_mesh_sensor_client_t; +typedef bt_mesh_internal_data_t sensor_internal_data_t; + +struct bt_mesh_sensor_descriptor_status { + struct net_buf_simple *descriptor; /* Sequence of 8-octet sensor descriptors (optional) */ +}; + +struct bt_mesh_sensor_cadence_status { + u16_t property_id; /* Property for the sensor */ + struct net_buf_simple *sensor_cadence_value; /* Value of sensor cadence state */ +}; + +struct bt_mesh_sensor_settings_status { + u16_t sensor_property_id; /* Property ID identifying a sensor */ + struct net_buf_simple *sensor_setting_property_ids; /* A sequence of N sensor setting property IDs (optional) */ +}; + +struct bt_mesh_sensor_setting_status { + bool op_en; /* Indicate whether optional parameters included */ + u16_t sensor_property_id; /* Property ID identifying a sensor */ + u16_t sensor_setting_property_id; /* Setting ID identifying a setting within a sensor */ + u8_t sensor_setting_access; /* Read/Write access rights for the setting (optional) */ + struct net_buf_simple *sensor_setting_raw; /* Raw value for the setting */ +}; + +struct bt_mesh_sensor_status { + struct net_buf_simple *marshalled_sensor_data; /* Value of sensor data state (optional) */ +}; + +struct bt_mesh_sensor_column_status { + u16_t property_id; /* Property identifying a sensor and the Y axis */ + struct net_buf_simple *sensor_column_value; /* Left values of sensor column status */ +}; + +struct bt_mesh_sensor_series_status { + u16_t property_id; /* Property identifying a sensor and the Y axis */ + struct net_buf_simple *sensor_series_value; /* Left values of sensor series status */ +}; + +struct bt_mesh_sensor_descriptor_get { + bool op_en; /* Indicate whether optional parameters included */ + u16_t property_id; /* Property ID for the sensor (optional) */ +}; + +struct bt_mesh_sensor_cadence_get { + u16_t property_id; /* Property ID for the sensor */ +}; + +struct bt_mesh_sensor_cadence_set { + u16_t property_id; /* Property ID for the sensor */ + u8_t fast_cadence_period_divisor : 7, /* Divisor for the publish period */ + status_trigger_type : 1; /* The unit and format of the Status Trigger Delta fields */ + struct net_buf_simple *status_trigger_delta_down; /* Delta down value that triggers a status message */ + struct net_buf_simple *status_trigger_delta_up; /* Delta up value that triggers a status message */ + u8_t status_min_interval; /* Minimum interval between two consecutive Status messages */ + struct net_buf_simple *fast_cadence_low; /* Low value for the fast cadence range */ + struct net_buf_simple *fast_cadence_high; /* Fast value for the fast cadence range */ +}; + +struct bt_mesh_sensor_settings_get { + u16_t sensor_property_id; /* Property ID for the sensor */ +}; + +struct bt_mesh_sensor_setting_get { + u16_t sensor_property_id; /* Property ID identifying a sensor */ + u16_t sensor_setting_property_id; /* Setting ID identifying a setting within a sensor */ +}; + +struct bt_mesh_sensor_setting_set { + u16_t sensor_property_id; /* Property ID identifying a sensor */ + u16_t sensor_setting_property_id; /* Setting ID identifying a setting within a sensor */ + struct net_buf_simple *sensor_setting_raw; /* Raw value for the setting */ +}; + +struct bt_mesh_sensor_get { + bool op_en; /* Indicate whether optional parameters included */ + u16_t property_id; /* Property ID for the sensor (optional) */ +}; + +struct bt_mesh_sensor_column_get { + u16_t property_id; /* Property identifying a sensor */ + struct net_buf_simple *raw_value_x; /* Raw value identifying a column */ +}; + +struct bt_mesh_sensor_series_get { + bool op_en; /* Indicate whether optional parameters included */ + u16_t property_id; /* Property identifying a sensor */ + struct net_buf_simple *raw_value_x1; /* Raw value identifying a starting column (optional) */ + struct net_buf_simple *raw_value_x2; /* Raw value identifying a ending column (C.1) */ +}; + +/** + * @brief This function is called to initialize sensor client model user_data. + * + * @param[in] model: Pointer to sensor client model + * @param[in] primary: Whether belongs to primary element + * + * @return Zero-success, other-fail + */ +int bt_mesh_sensor_cli_init(struct bt_mesh_model *model, bool primary); + +/** + * @brief This function is called to get sensor states. + * + * @param[in] common: Message common information structure + * @param[in] get: Pointer of sensor get message value + * @param[out] status: Pointer of sensor status message value + * + * @return Zero-success, other-fail + */ +int bt_mesh_sensor_client_get_state(struct bt_mesh_common_param *common, void *get, void *status); + +/** + * @brief This function is called to set sensor states. + * + * @param[in] common: Message common information structure + * @param[in] set: Pointer of sensor set message value + * @param[out] status: Pointer of sensor status message value + * + * @return Zero-success, other-fail + */ +int bt_mesh_sensor_client_set_state(struct bt_mesh_common_param *common, void *set, void *status); + +#endif /* _SENSOR_CLIENT_H_ */ diff --git a/components/bt/ble_mesh/mesh_models/include/time_scene_client.h b/components/bt/ble_mesh/mesh_models/include/time_scene_client.h new file mode 100644 index 0000000000..baa0d4abc0 --- /dev/null +++ b/components/bt/ble_mesh/mesh_models/include/time_scene_client.h @@ -0,0 +1,257 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +/** @file + * @brief Bluetooth Mesh Time and Scene Client Model APIs. + */ + +#ifndef _TIME_SCENE_CLIENT_H_ +#define _TIME_SCENE_CLIENT_H_ + +#include "mesh_access.h" +#include "mesh_kernel.h" + +#include "model_common.h" + +/* Time scene client model common structure */ +typedef bt_mesh_client_common_t bt_mesh_time_scene_client_t; +typedef bt_mesh_internal_data_t time_scene_internal_data_t; + +/* Time Client Model Context */ +extern const struct bt_mesh_model_op time_cli_op[]; + +/** @def BLE_MESH_MODEL_TIME_CLI + * + * Define a new time client model. Note that this API needs to + * be repeated for each element which the application wants to + * have a time model on. + * @param cli_pub Pointer to a unique struct bt_mesh_model_pub. + * @param cli_data Pointer to a unique struct bt_mesh_time_cli. + * + * @return New time client model instance. + */ +#define BLE_MESH_MODEL_TIME_CLI(cli_pub, cli_data) \ + BLE_MESH_MODEL(BLE_MESH_MODEL_ID_TIME_CLI, \ + time_cli_op, cli_pub, cli_data) + +typedef bt_mesh_client_common_t bt_mesh_time_cli_t; + +struct bt_mesh_time_status { + u8_t tai_seconds[5]; /* The current TAI time in seconds */ + u8_t sub_second; /* The sub-second time in units of 1/256 second */ + u8_t uncertainty; /* The estimated uncertainty in 10-millisecond steps */ + u16_t time_authority : 1; /* 0 = No Time Authority, 1 = Time Authority */ + u16_t tai_utc_delta : 15; /* Current difference between TAI and UTC in seconds */ + u8_t time_zone_offset; /* The local time zone offset in 15-minute increments */ +}; + +struct bt_mesh_time_zone_status { + u8_t time_zone_offset_curr; /* Current local time zone offset */ + u8_t time_zone_offset_new; /* Upcoming local time zone offset */ + u8_t tai_zone_change[5]; /* TAI Seconds time of the upcoming Time Zone Offset change */ +}; + +struct bt_mesh_tai_utc_delta_status { + u16_t tai_utc_delta_curr : 15; /* Current difference between TAI and UTC in seconds */ + u16_t padding_1 : 1; /* Always 0b0. Other values are Prohibited. */ + u16_t tai_utc_delta_new : 15; /* Upcoming difference between TAI and UTC in seconds */ + u16_t padding_2 : 1; /* Always 0b0. Other values are Prohibited. */ + u8_t tai_delta_change[5]; /* TAI Seconds time of the upcoming TAI-UTC Delta change */ +}; + +struct bt_mesh_time_role_status { + u8_t time_role; /* The Time Role for the element */ +}; + +struct bt_mesh_time_set { + u8_t tai_seconds[5]; /* The current TAI time in seconds */ + u8_t sub_second; /* The sub-second time in units of 1/256 second */ + u8_t uncertainty; /* The estimated uncertainty in 10-millisecond steps */ + u16_t time_authority : 1; /* 0 = No Time Authority, 1 = Time Authority */ + u16_t tai_utc_delta : 15; /* Current difference between TAI and UTC in seconds */ + u8_t time_zone_offset; /* The local time zone offset in 15-minute increments */ +}; + +struct bt_mesh_time_zone_set { + u8_t time_zone_offset_new; /* Upcoming local time zone offset */ + u8_t tai_zone_change[5]; /* TAI Seconds time of the upcoming Time Zone Offset change */ +}; + +struct bt_mesh_tai_utc_delta_set { + u16_t tai_utc_delta_new : 15; /* Upcoming difference between TAI and UTC in seconds */ + u16_t padding : 1; /* Always 0b0. Other values are Prohibited. */ + u8_t tai_delta_change[5]; /* TAI Seconds time of the upcoming TAI-UTC Delta change */ +}; + +struct bt_mesh_time_role_set { + u8_t time_role; /* The Time Role for the element */ +}; + +/* Scene Client Model Context */ +extern const struct bt_mesh_model_op scene_cli_op[]; + +/** @def BLE_MESH_MODEL_SCENE_CLI + * + * Define a new scene client model. Note that this API needs to + * be repeated for each element which the application wants to + * have a scene model on. + * @param cli_pub Pointer to a unique struct bt_mesh_model_pub. + * @param cli_data Pointer to a unique struct bt_mesh_scene_cli. + * + * @return New scene client model instance. + */ +#define BLE_MESH_MODEL_SCENE_CLI(cli_pub, cli_data) \ + BLE_MESH_MODEL(BLE_MESH_MODEL_ID_SCENE_CLI, \ + scene_cli_op, cli_pub, cli_data) + +typedef bt_mesh_client_common_t bt_mesh_scene_cli_t; + +struct bt_mesh_scene_status { + bool op_en; /* Indicate whether optional parameters included */ + u8_t status_code; /* Status code for the last operation */ + u16_t current_scene; /* Scene Number of a current scene */ + u16_t target_scene; /* Scene Number of a target scene (optional) */ + u8_t remain_time; /* Time to complete state transition (C.1) */ +}; + +struct bt_mesh_scene_register_status { + u8_t status_code; /* Status code for the previous operation */ + u16_t current_scene; /* Scene Number of a current scene */ + struct net_buf_simple *scenes; /* A list of scenes stored within an element */ +}; + +struct bt_mesh_scene_store { + u16_t scene_number; /* The number of the scene to be stored */ +}; + +struct bt_mesh_scene_recall { + bool op_en; /* Indicate whether optional parameters included */ + u16_t scene_number; /* The number of the scene to be recalled */ + u8_t tid; /* Transaction Identifier */ + u8_t trans_time; /* Time to complete state transition (optional) */ + u8_t delay; /* Indicate message execution delay (C.1) */ +}; + +struct bt_mesh_scene_delete { + u16_t scene_number; /* The number of the scene to be deleted */ +}; + +/* Scheduler Client Model Context */ +extern const struct bt_mesh_model_op scheduler_cli_op[]; + +/** @def BLE_MESH_MODEL_SCHEDULER_CLI + * + * Define a new scheduler client model. Note that this API needs to + * be repeated for each element which the application wants to + * have a scheduler model on. + * @param cli_pub Pointer to a unique struct bt_mesh_model_pub. + * @param cli_data Pointer to a unique struct bt_mesh_scheduler_cli. + * + * @return New scheduler client model instance. + */ +#define BLE_MESH_MODEL_SCHEDULER_CLI(cli_pub, cli_data) \ + BLE_MESH_MODEL(BLE_MESH_MODEL_ID_SCHEDULER_CLI, \ + scheduler_cli_op, cli_pub, cli_data) + +typedef bt_mesh_client_common_t bt_mesh_scheduler_cli_t; + +struct bt_mesh_scheduler_status { + u16_t schedules; /* Bit field indicating defined Actions in the Schedule Register */ +}; + +struct bt_mesh_scheduler_act_status { + u64_t index : 4; /* Enumerates (selects) a Schedule Register entry */ + u64_t year : 7; /* Scheduled year for the action */ + u64_t month : 12; /* Scheduled month for the action */ + u64_t day : 5; /* Scheduled day of the month for the action */ + u64_t hour : 5; /* Scheduled hour for the action */ + u64_t minute : 6; /* Scheduled minute for the action */ + u64_t second : 6; /* Scheduled second for the action */ + u64_t day_of_week : 7; /* Schedule days of the week for the action */ + u64_t action : 4; /* Action to be performed at the scheduled time */ + u64_t trans_time : 8; /* Transition time for this action */ + u16_t scene_number; /* Transition time for this action */ +}; + +struct bt_mesh_scheduler_act_get { + u8_t index; /* Index of the Schedule Register entry to get */ +}; + +struct bt_mesh_scheduler_act_set { + u64_t index : 4; /* Index of the Schedule Register entry to set */ + u64_t year : 7; /* Scheduled year for the action */ + u64_t month : 12; /* Scheduled month for the action */ + u64_t day : 5; /* Scheduled day of the month for the action */ + u64_t hour : 5; /* Scheduled hour for the action */ + u64_t minute : 6; /* Scheduled minute for the action */ + u64_t second : 6; /* Scheduled second for the action */ + u64_t day_of_week : 7; /* Schedule days of the week for the action */ + u64_t action : 4; /* Action to be performed at the scheduled time */ + u64_t trans_time : 8; /* Transition time for this action */ + u16_t scene_number; /* Transition time for this action */ +}; + +/** + * @brief This function is called to initialize time client model user_data. + * + * @param[in] model: Pointer to time client model + * @param[in] primary: Whether belongs to primary element + * + * @return Zero-success, other-fail + */ +int bt_mesh_time_cli_init(struct bt_mesh_model *model, bool primary); + +/** + * @brief This function is called to initialize scene client model user_data. + * + * @param[in] model: Pointer to scene client model + * @param[in] primary: Whether belongs to primary element + * + * @return Zero-success, other-fail + */ +int bt_mesh_scene_cli_init(struct bt_mesh_model *model, bool primary); + +/** + * @brief This function is called to initialize scheduler client model user_data. + * + * @param[in] model: Pointer to scheduler client model + * @param[in] primary: Whether belongs to primary element + * + * @return Zero-success, other-fail + */ +int bt_mesh_scheduler_cli_init(struct bt_mesh_model *model, bool primary); + +/** + * @brief This function is called to get scene states. + * + * @param[in] common: Message common information structure + * @param[in] get: Pointer of time scene get message value + * @param[out] status: Pointer of time scene status message value + * + * @return Zero-success, other-fail + */ +int bt_mesh_time_scene_client_get_state(struct bt_mesh_common_param *common, void *get, void *status); + +/** + * @brief This function is called to set scene states. + * + * @param[in] common: Message common information structure + * @param[in] set: Pointer of time scene set message value + * @param[out] status: Pointer of time scene status message value + * + * @return Zero-success, other-fail + */ +int bt_mesh_time_scene_client_set_state(struct bt_mesh_common_param *common, void *set, void *status); + +#endif /* _TIME_SCENE_CLIENT_H_ */ diff --git a/components/bt/ble_mesh/mesh_models/lighting_client.c b/components/bt/ble_mesh/mesh_models/lighting_client.c new file mode 100644 index 0000000000..3aea99a8a4 --- /dev/null +++ b/components/bt/ble_mesh/mesh_models/lighting_client.c @@ -0,0 +1,1400 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 +#include + +#include "osi/allocator.h" +#include "sdkconfig.h" + +#include "mesh_types.h" +#include "mesh_kernel.h" +#include "mesh_trace.h" + +#include "mesh.h" +#include "model_opcode.h" +#include "mesh_common.h" +#include "lighting_client.h" + +#include "btc_ble_mesh_lighting_model.h" + +/** The following are the macro definitions of lighting client + * model messages length, and a message is composed of three + * parts: Opcode + msg_value + MIC + */ +/* Light lightness client messages length */ +#define BLE_MESH_LIGHT_LIGHTNESS_GET_MSG_LEN (2 + 0 + 4) +#define BLE_MESH_LIGHT_LIGHTNESS_SET_MSG_LEN (2 + 5 + 4) +#define BLE_MESH_LIGHT_LIGHTNESS_LINEAR_GET_MSG_LEN (2 + 0 + 4) +#define BLE_MESH_LIGHT_LIGHTNESS_LINEAR_SET_MSG_LEN (2 + 5 + 4) +#define BLE_MESH_LIGHT_LIGHTNESS_LAST_GET_MSG_LEN (2 + 0 + 4) +#define BLE_MESH_LIGHT_LIGHTNESS_DEFAULT_GET_MSG_LEN (2 + 0 + 4) +#define BLE_MESH_LIGHT_LIGHTNESS_DEFAULT_SET_MSG_LEN (2 + 2 + 4) +#define BLE_MESH_LIGHT_LIGHTNESS_RANGE_GET_MSG_LEN (2 + 0 + 4) +#define BLE_MESH_LIGHT_LIGHTNESS_RANGE_SET_MSG_LEN (2 + 4 + 4) + +/* Light CTL client messages length */ +#define BLE_MESH_LIGHT_CTL_GET_MSG_LEN (2 + 0 + 4) +#define BLE_MESH_LIGHT_CTL_SET_MSG_LEN (2 + 9 + 4) +#define BLE_MESH_LIGHT_CTL_TEMPERATURE_GET_MSG_LEN (2 + 0 + 4) +#define BLE_MESH_LIGHT_CTL_TEMPERATURE_SET_MSG_LEN (2 + 7 + 4) +#define BLE_MESH_LIGHT_CTL_TEMPERATURE_RANGE_GET_MSG_LEN (2 + 0 + 4) +#define BLE_MESH_LIGHT_CTL_TEMPERATURE_RANGE_SET_MSG_LEN (2 + 4 + 4) +#define BLE_MESH_LIGHT_CTL_DEFAULT_GET_MSG_LEN (2 + 0 + 4) +#define BLE_MESH_LIGHT_CTL_DEFAULT_SET_MSG_LEN (2 + 6 + 4) + +/* Light HSL client messages length */ +#define BLE_MESH_LIGHT_HSL_GET_MSG_LEN (2 + 0 + 4) +#define BLE_MESH_LIGHT_HSL_SET_MSG_LEN (2 + 9 + 4) +#define BLE_MESH_LIGHT_HSL_TARGET_GET_MSG_LEN (2 + 0 + 4) +#define BLE_MESH_LIGHT_HSL_HUE_GET_MSG_LEN (2 + 0 + 4) +#define BLE_MESH_LIGHT_HSL_HUE_SET_MSG_LEN (2 + 5 + 4) +#define BLE_MESH_LIGHT_HSL_SATURATION_GET_MSG_LEN (2 + 0 + 4) +#define BLE_MESH_LIGHT_HSL_SATURATION_SET_MSG_LEN (2 + 5 + 4) +#define BLE_MESH_LIGHT_HSL_DEFAULT_GET_MSG_LEN (2 + 0 + 4) +#define BLE_MESH_LIGHT_HSL_DEFAULT_SET_MSG_LEN (2 + 6 + 4) +#define BLE_MESH_LIGHT_HSL_RANGE_GET_MSG_LEN (2 + 0 + 4) +#define BLE_MESH_LIGHT_HSL_RANGE_SET_MSG_LEN (2 + 8 + 4) + +/* Light xyL client messages length */ +#define BLE_MESH_LIGHT_XYL_SET_MSG_LEN (2 + 9 + 4) +#define BLE_MESH_LIGHT_XYL_DEFAULT_SET_MSG_LEN (2 + 6 + 4) +#define BLE_MESH_LIGHT_XYL_RANGE_SET_MSG_LEN (2 + 8 + 4) + +/* Light LC client messages length */ +#define BLE_MESH_LIGHT_LC_MODE_SET_MSG_LEN (2 + 1 + 4) +#define BLE_MESH_LIGHT_LC_OM_SET_MSG_LEN (2 + 1 + 4) +#define BLE_MESH_LIGHT_LC_LIGHT_ONOFF_SET_MSG_LEN (2 + 4 + 4) +#define BLE_MESH_LIGHT_LC_PROPERTY_GET_MSG_LEN (2 + 2 + 4) +#define BLE_MESH_LIGHT_LC_PROPERTY_SET_MSG_LEN /* variable */ + +#define BLE_MESH_LIGHT_GET_STATE_MSG_LEN (2 + 2 + 4) + +static const bt_mesh_client_op_pair_t light_op_pair[] = { + { BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_GET, BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_STATUS }, + { BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_SET, BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_STATUS }, + { BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_GET, BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_STATUS }, + { BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_SET, BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_STATUS }, + { BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LAST_GET, BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LAST_STATUS }, + { BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_DEFAULT_GET, BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_DEFAULT_STATUS }, + { BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_DEFAULT_SET, BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_DEFAULT_STATUS }, + { BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_RANGE_GET, BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_RANGE_STATUS }, + { BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_RANGE_SET, BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_RANGE_STATUS }, + { BLE_MESH_MODEL_OP_LIGHT_CTL_GET, BLE_MESH_MODEL_OP_LIGHT_CTL_STATUS }, + { BLE_MESH_MODEL_OP_LIGHT_CTL_SET, BLE_MESH_MODEL_OP_LIGHT_CTL_STATUS }, + { BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_GET, BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_STATUS }, + { BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_SET, BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_STATUS }, + { BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_RANGE_GET, BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_RANGE_STATUS }, + { BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_RANGE_SET, BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_RANGE_STATUS }, + { BLE_MESH_MODEL_OP_LIGHT_CTL_DEFAULT_GET, BLE_MESH_MODEL_OP_LIGHT_CTL_DEFAULT_STATUS }, + { BLE_MESH_MODEL_OP_LIGHT_CTL_DEFAULT_SET, BLE_MESH_MODEL_OP_LIGHT_CTL_DEFAULT_STATUS }, + { BLE_MESH_MODEL_OP_LIGHT_HSL_GET, BLE_MESH_MODEL_OP_LIGHT_HSL_STATUS }, + { BLE_MESH_MODEL_OP_LIGHT_HSL_SET, BLE_MESH_MODEL_OP_LIGHT_HSL_STATUS }, + { BLE_MESH_MODEL_OP_LIGHT_HSL_TARGET_GET, BLE_MESH_MODEL_OP_LIGHT_HSL_TARGET_STATUS }, + { BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_GET, BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_STATUS }, + { BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_SET, BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_STATUS }, + { BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_GET, BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_STATUS }, + { BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_SET, BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_STATUS }, + { BLE_MESH_MODEL_OP_LIGHT_HSL_DEFAULT_GET, BLE_MESH_MODEL_OP_LIGHT_HSL_DEFAULT_STATUS }, + { BLE_MESH_MODEL_OP_LIGHT_HSL_DEFAULT_SET, BLE_MESH_MODEL_OP_LIGHT_HSL_DEFAULT_STATUS }, + { BLE_MESH_MODEL_OP_LIGHT_HSL_RANGE_GET, BLE_MESH_MODEL_OP_LIGHT_HSL_RANGE_STATUS }, + { BLE_MESH_MODEL_OP_LIGHT_HSL_RANGE_SET, BLE_MESH_MODEL_OP_LIGHT_HSL_RANGE_STATUS }, + { BLE_MESH_MODEL_OP_LIGHT_XYL_GET, BLE_MESH_MODEL_OP_LIGHT_XYL_STATUS }, + { BLE_MESH_MODEL_OP_LIGHT_XYL_SET, BLE_MESH_MODEL_OP_LIGHT_XYL_STATUS }, + { BLE_MESH_MODEL_OP_LIGHT_XYL_TARGET_GET, BLE_MESH_MODEL_OP_LIGHT_XYL_TARGET_STATUS }, + { BLE_MESH_MODEL_OP_LIGHT_XYL_DEFAULT_GET, BLE_MESH_MODEL_OP_LIGHT_XYL_DEFAULT_STATUS }, + { BLE_MESH_MODEL_OP_LIGHT_XYL_DEFAULT_SET, BLE_MESH_MODEL_OP_LIGHT_XYL_DEFAULT_STATUS }, + { BLE_MESH_MODEL_OP_LIGHT_XYL_RANGE_GET, BLE_MESH_MODEL_OP_LIGHT_XYL_RANGE_STATUS }, + { BLE_MESH_MODEL_OP_LIGHT_XYL_RANGE_SET, BLE_MESH_MODEL_OP_LIGHT_XYL_RANGE_STATUS }, + { BLE_MESH_MODEL_OP_LIGHT_LC_MODE_GET, BLE_MESH_MODEL_OP_LIGHT_LC_MODE_STATUS }, + { BLE_MESH_MODEL_OP_LIGHT_LC_MODE_SET, BLE_MESH_MODEL_OP_LIGHT_LC_MODE_STATUS }, + { BLE_MESH_MODEL_OP_LIGHT_LC_OM_GET, BLE_MESH_MODEL_OP_LIGHT_LC_OM_STATUS }, + { BLE_MESH_MODEL_OP_LIGHT_LC_OM_SET, BLE_MESH_MODEL_OP_LIGHT_LC_OM_STATUS }, + { BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_GET, BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_STATUS }, + { BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_SET, BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_STATUS }, + { BLE_MESH_MODEL_OP_LIGHT_LC_PROPERTY_GET, BLE_MESH_MODEL_OP_LIGHT_LC_PROPERTY_STATUS }, + { BLE_MESH_MODEL_OP_LIGHT_LC_PROPERTY_SET, BLE_MESH_MODEL_OP_LIGHT_LC_PROPERTY_STATUS }, +}; + +static void timeout_handler(struct k_work *work) +{ + light_internal_data_t *internal = NULL; + bt_mesh_light_client_t *client = NULL; + bt_mesh_client_node_t *node = NULL; + + BT_WARN("Receive light status message timeout"); + + node = CONTAINER_OF(work, bt_mesh_client_node_t, timer.work); + if (!node || !node->ctx.model) { + BT_ERR("%s, Invalid parameter", __func__); + return; + } + + client = (bt_mesh_light_client_t *)node->ctx.model->user_data; + if (!client) { + BT_ERR("%s, Lighting Client user_data is NULL", __func__); + return; + } + + internal = (light_internal_data_t *)client->internal_data; + if (!internal) { + BT_ERR("%s, Lighting Client internal_data is NULL", __func__); + return; + } + + bt_mesh_callback_light_status_to_btc(node->opcode, 0x03, node->ctx.model, + &node->ctx, NULL, 0); + + bt_mesh_client_free_node(&internal->queue, node); + + return; +} + +static void light_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + light_internal_data_t *internal = NULL; + bt_mesh_light_client_t *client = NULL; + bt_mesh_client_node_t *node = NULL; + u8_t *val = NULL; + u8_t evt = 0xFF; + u32_t rsp = 0; + size_t len = 0; + + BT_DBG("%s, len %d, bytes %s", __func__, buf->len, bt_hex(buf->data, buf->len)); + + client = (bt_mesh_light_client_t *)model->user_data; + if (!client) { + BT_ERR("%s, Lighting Client user_data is NULL", __func__); + return; + } + + internal = (light_internal_data_t *)client->internal_data; + if (!internal) { + BT_ERR("%s, Lighting Client internal_data is NULL", __func__); + return; + } + + rsp = ctx->recv_op; + + switch (rsp) { + case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_STATUS: { + struct bt_mesh_light_lightness_status *status = NULL; + if (buf->len != 2 && buf->len != 5) { + BT_ERR("%s, Invalid Light Lightness Status length %d", __func__, buf->len); + return; + } + status = osi_calloc(sizeof(struct bt_mesh_light_lightness_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->present_lightness = net_buf_simple_pull_le16(buf); + if (buf->len) { + status->op_en = true; + status->target_lightness = net_buf_simple_pull_le16(buf); + status->remain_time = net_buf_simple_pull_u8(buf); + } + val = (u8_t *)status; + len = sizeof(struct bt_mesh_light_lightness_status); + break; + } + case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_STATUS: { + struct bt_mesh_light_lightness_linear_status *status = NULL; + if (buf->len != 2 && buf->len != 5) { + BT_ERR("%s, Invalid Light Lightness Linear Status length %d", __func__, buf->len); + return; + } + status = osi_calloc(sizeof(struct bt_mesh_light_lightness_linear_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->present_lightness = net_buf_simple_pull_le16(buf); + if (buf->len) { + status->op_en = true; + status->target_lightness = net_buf_simple_pull_le16(buf); + status->remain_time = net_buf_simple_pull_u8(buf); + } + val = (u8_t *)status; + len = sizeof(struct bt_mesh_light_lightness_linear_status); + break; + } + case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LAST_STATUS: { + struct bt_mesh_light_lightness_last_status *status = NULL; + if (buf->len != 2) { + BT_ERR("%s, Invalid Light Lightness Last Status length %d", __func__, buf->len); + return; + } + status = osi_calloc(sizeof(struct bt_mesh_light_lightness_last_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->lightness = net_buf_simple_pull_le16(buf); + val = (u8_t *)status; + len = sizeof(struct bt_mesh_light_lightness_last_status); + break; + } + case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_DEFAULT_STATUS: { + struct bt_mesh_light_lightness_default_status *status = NULL; + if (buf->len != 2) { + BT_ERR("%s, Invalid Light Lightness Default Status length %d", __func__, buf->len); + return; + } + status = osi_calloc(sizeof(struct bt_mesh_light_lightness_default_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->lightness = net_buf_simple_pull_le16(buf); + val = (u8_t *)status; + len = sizeof(struct bt_mesh_light_lightness_default_status); + break; + } + case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_RANGE_STATUS: { + struct bt_mesh_light_lightness_range_status *status = NULL; + if (buf->len != 5) { + BT_ERR("%s, Invalid Light Lightness Range Status length %d", __func__, buf->len); + return; + } + status = osi_calloc(sizeof(struct bt_mesh_light_lightness_range_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->status_code = net_buf_simple_pull_u8(buf); + status->range_min = net_buf_simple_pull_le16(buf); + status->range_max = net_buf_simple_pull_le16(buf); + val = (u8_t *)status; + len = sizeof(struct bt_mesh_light_lightness_range_status); + break; + } + case BLE_MESH_MODEL_OP_LIGHT_CTL_STATUS: { + struct bt_mesh_light_ctl_status *status = NULL; + if (buf->len != 4 && buf->len != 9) { + BT_ERR("%s, Invalid Light CTL Status length %d", __func__, buf->len); + return; + } + status = osi_calloc(sizeof(struct bt_mesh_light_ctl_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->present_ctl_lightness = net_buf_simple_pull_le16(buf); + status->present_ctl_temperature = net_buf_simple_pull_le16(buf); + if (buf->len) { + status->op_en = true; + status->target_ctl_lightness = net_buf_simple_pull_le16(buf); + status->target_ctl_temperature = net_buf_simple_pull_le16(buf); + status->remain_time = net_buf_simple_pull_u8(buf); + } + val = (u8_t *)status; + len = sizeof(struct bt_mesh_light_ctl_status); + break; + } + case BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_STATUS: { + struct bt_mesh_light_ctl_temperature_status *status = NULL; + if (buf->len != 4 && buf->len != 9) { + BT_ERR("%s, Invalid Light CTL Temperature Status length %d", __func__, buf->len); + return; + } + status = osi_calloc(sizeof(struct bt_mesh_light_ctl_temperature_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->present_ctl_temperature = net_buf_simple_pull_le16(buf); + status->present_ctl_delta_uv = net_buf_simple_pull_le16(buf); + if (buf->len) { + status->op_en = true; + status->target_ctl_temperature = net_buf_simple_pull_le16(buf); + status->target_ctl_delta_uv = net_buf_simple_pull_le16(buf); + status->remain_time = net_buf_simple_pull_u8(buf); + } + val = (u8_t *)status; + len = sizeof(struct bt_mesh_light_ctl_temperature_status); + break; + } + case BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_RANGE_STATUS: { + struct bt_mesh_light_ctl_temperature_range_status *status = NULL; + if (buf->len != 5) { + BT_ERR("%s, Invalid Light CTL Temperature Range Status length %d", __func__, buf->len); + return; + } + status = osi_calloc(sizeof(struct bt_mesh_light_ctl_temperature_range_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->status_code = net_buf_simple_pull_u8(buf); + status->range_min = net_buf_simple_pull_le16(buf); + status->range_max = net_buf_simple_pull_le16(buf); + val = (u8_t *)status; + len = sizeof(struct bt_mesh_light_ctl_temperature_range_status); + break; + } + case BLE_MESH_MODEL_OP_LIGHT_CTL_DEFAULT_STATUS: { + struct bt_mesh_light_ctl_default_status *status = NULL; + if (buf->len != 6) { + BT_ERR("%s, Invalid Light CTL Default Status length %d", __func__, buf->len); + return; + } + status = osi_calloc(sizeof(struct bt_mesh_light_ctl_default_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->lightness = net_buf_simple_pull_le16(buf); + status->temperature = net_buf_simple_pull_le16(buf); + status->delta_uv = net_buf_simple_pull_le16(buf); + val = (u8_t *)status; + len = sizeof(struct bt_mesh_light_ctl_default_status); + break; + } + case BLE_MESH_MODEL_OP_LIGHT_HSL_STATUS: { + struct bt_mesh_light_hsl_status *status = NULL; + if (buf->len != 6 && buf->len != 7) { + BT_ERR("%s, Invalid Light HSL Status length %d", __func__, buf->len); + return; + } + status = osi_calloc(sizeof(struct bt_mesh_light_hsl_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->hsl_lightness = net_buf_simple_pull_le16(buf); + status->hsl_hue = net_buf_simple_pull_le16(buf); + status->hsl_saturation = net_buf_simple_pull_le16(buf); + if (buf->len) { + status->op_en = true; + status->remain_time = net_buf_simple_pull_u8(buf); + } + val = (u8_t *)status; + len = sizeof(struct bt_mesh_light_hsl_status); + break; + } + case BLE_MESH_MODEL_OP_LIGHT_HSL_TARGET_STATUS: { + struct bt_mesh_light_hsl_target_status *status = NULL; + if (buf->len != 6 && buf->len != 7) { + BT_ERR("%s, Invalid Light HSL Target Status length %d", __func__, buf->len); + return; + } + status = osi_calloc(sizeof(struct bt_mesh_light_hsl_target_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->hsl_lightness_target = net_buf_simple_pull_le16(buf); + status->hsl_hue_target = net_buf_simple_pull_le16(buf); + status->hsl_saturation_target = net_buf_simple_pull_le16(buf); + if (buf->len) { + status->op_en = true; + status->remain_time = net_buf_simple_pull_u8(buf); + } + val = (u8_t *)status; + len = sizeof(struct bt_mesh_light_hsl_target_status); + break; + } + case BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_STATUS: { + struct bt_mesh_light_hsl_hue_status *status = NULL; + if (buf->len != 2 && buf->len != 5) { + BT_ERR("%s, Invalid Light HSL Hue Status length %d", __func__, buf->len); + return; + } + status = osi_calloc(sizeof(struct bt_mesh_light_hsl_hue_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->present_hue = net_buf_simple_pull_le16(buf); + if (buf->len) { + status->op_en = true; + status->target_hue = net_buf_simple_pull_le16(buf); + status->remain_time = net_buf_simple_pull_u8(buf); + } + val = (u8_t *)status; + len = sizeof(struct bt_mesh_light_hsl_hue_status); + break; + } + case BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_STATUS: { + struct bt_mesh_light_hsl_saturation_status *status = NULL; + if (buf->len != 2 && buf->len != 5) { + BT_ERR("%s, Invalid Light HSL Saturation Status length %d", __func__, buf->len); + return; + } + status = osi_calloc(sizeof(struct bt_mesh_light_hsl_saturation_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->present_saturation = net_buf_simple_pull_le16(buf); + if (buf->len) { + status->op_en = true; + status->target_saturation = net_buf_simple_pull_le16(buf); + status->remain_time = net_buf_simple_pull_u8(buf); + } + val = (u8_t *)status; + len = sizeof(struct bt_mesh_light_hsl_saturation_status); + break; + } + case BLE_MESH_MODEL_OP_LIGHT_HSL_DEFAULT_STATUS: { + struct bt_mesh_light_hsl_default_status *status = NULL; + if (buf->len != 6) { + BT_ERR("%s, Invalid Light HSL Default Status length %d", __func__, buf->len); + return; + } + status = osi_calloc(sizeof(struct bt_mesh_light_hsl_default_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->lightness = net_buf_simple_pull_le16(buf); + status->hue = net_buf_simple_pull_le16(buf); + status->saturation = net_buf_simple_pull_le16(buf); + val = (u8_t *)status; + len = sizeof(struct bt_mesh_light_hsl_default_status); + break; + } + case BLE_MESH_MODEL_OP_LIGHT_HSL_RANGE_STATUS: { + struct bt_mesh_light_hsl_range_status *status = NULL; + if (buf->len != 9) { + BT_ERR("%s, Invalid Light HSL Range Status length %d", __func__, buf->len); + return; + } + status = osi_calloc(sizeof(struct bt_mesh_light_hsl_range_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->status_code = net_buf_simple_pull_u8(buf); + status->hue_range_min = net_buf_simple_pull_le16(buf); + status->hue_range_max = net_buf_simple_pull_le16(buf); + status->saturation_range_min = net_buf_simple_pull_le16(buf); + status->saturation_range_max = net_buf_simple_pull_le16(buf); + val = (u8_t *)status; + len = sizeof(struct bt_mesh_light_hsl_range_status); + break; + } + case BLE_MESH_MODEL_OP_LIGHT_XYL_STATUS: { + struct bt_mesh_light_xyl_status *status = NULL; + if (buf->len != 6 && buf->len != 7) { + BT_ERR("%s, Invalid Light xyL Status length %d", __func__, buf->len); + return; + } + status = osi_calloc(sizeof(struct bt_mesh_light_xyl_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->xyl_lightness = net_buf_simple_pull_le16(buf); + status->xyl_x = net_buf_simple_pull_le16(buf); + status->xyl_y = net_buf_simple_pull_le16(buf); + if (buf->len) { + status->op_en = true; + status->remain_time = net_buf_simple_pull_u8(buf); + } + val = (u8_t *)status; + len = sizeof(struct bt_mesh_light_xyl_status); + break; + } + case BLE_MESH_MODEL_OP_LIGHT_XYL_TARGET_STATUS: { + struct bt_mesh_light_xyl_target_status *status = NULL; + if (buf->len != 6 && buf->len != 7) { + BT_ERR("%s, Invalid Light xyL Target Status length %d", __func__, buf->len); + return; + } + status = osi_calloc(sizeof(struct bt_mesh_light_xyl_target_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->target_xyl_lightness = net_buf_simple_pull_le16(buf); + status->target_xyl_x = net_buf_simple_pull_le16(buf); + status->target_xyl_y = net_buf_simple_pull_le16(buf); + if (buf->len) { + status->op_en = true; + status->remain_time = net_buf_simple_pull_u8(buf); + } + val = (u8_t *)status; + len = sizeof(struct bt_mesh_light_xyl_target_status); + break; + } + case BLE_MESH_MODEL_OP_LIGHT_XYL_DEFAULT_STATUS: { + struct bt_mesh_light_xyl_default_status *status = NULL; + if (buf->len != 6) { + BT_ERR("%s, Invalid Light xyL Default Status length %d", __func__, buf->len); + return; + } + status = osi_calloc(sizeof(struct bt_mesh_light_xyl_default_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->lightness = net_buf_simple_pull_le16(buf); + status->xyl_x = net_buf_simple_pull_le16(buf); + status->xyl_y = net_buf_simple_pull_le16(buf); + val = (u8_t *)status; + len = sizeof(struct bt_mesh_light_xyl_default_status); + break; + } + case BLE_MESH_MODEL_OP_LIGHT_XYL_RANGE_STATUS: { + struct bt_mesh_light_xyl_range_status *status = NULL; + if (buf->len != 9) { + BT_ERR("%s, Invalid Light xyL Range Status length %d", __func__, buf->len); + return; + } + status = osi_calloc(sizeof(struct bt_mesh_light_xyl_range_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->status_code = net_buf_simple_pull_u8(buf); + status->xyl_x_range_min = net_buf_simple_pull_le16(buf); + status->xyl_x_range_max = net_buf_simple_pull_le16(buf); + status->xyl_y_range_min = net_buf_simple_pull_le16(buf); + status->xyl_y_range_max = net_buf_simple_pull_le16(buf); + val = (u8_t *)status; + len = sizeof(struct bt_mesh_light_xyl_range_status); + break; + } + case BLE_MESH_MODEL_OP_LIGHT_LC_MODE_STATUS: { + struct bt_mesh_light_lc_mode_status *status = NULL; + if (buf->len != 1) { + BT_ERR("%s, Invalid Light LC Mode Status length %d", __func__, buf->len); + return; + } + status = osi_calloc(sizeof(struct bt_mesh_light_lc_mode_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->mode = net_buf_simple_pull_u8(buf); + val = (u8_t *)status; + len = sizeof(struct bt_mesh_light_lc_mode_status); + break; + } + case BLE_MESH_MODEL_OP_LIGHT_LC_OM_STATUS: { + struct bt_mesh_light_lc_om_status *status = NULL; + if (buf->len != 1) { + BT_ERR("%s, Invalid Light LC OM Status length %d", __func__, buf->len); + return; + } + status = osi_calloc(sizeof(struct bt_mesh_light_lc_om_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->mode = net_buf_simple_pull_u8(buf); + val = (u8_t *)status; + len = sizeof(struct bt_mesh_light_lc_om_status); + break; + } + case BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_STATUS: { + struct bt_mesh_light_lc_light_onoff_status *status = NULL; + if (buf->len != 1 && buf->len != 3) { + BT_ERR("%s, Invalid Light LC Light OnOff Status length %d", __func__, buf->len); + return; + } + status = osi_calloc(sizeof(struct bt_mesh_light_lc_light_onoff_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->present_light_onoff = net_buf_simple_pull_u8(buf); + if (buf->len) { + status->op_en = true; + status->target_light_onoff = net_buf_simple_pull_u8(buf); + status->remain_time = net_buf_simple_pull_u8(buf); + } + val = (u8_t *)status; + len = sizeof(struct bt_mesh_light_lc_light_onoff_status); + break; + } + case BLE_MESH_MODEL_OP_LIGHT_LC_PROPERTY_STATUS: { + struct bt_mesh_light_lc_property_status *status = NULL; + status = osi_calloc(sizeof(struct bt_mesh_light_lc_property_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->light_lc_property_id = net_buf_simple_pull_le16(buf); + status->light_lc_property_value = bt_mesh_alloc_buf(buf->len); + if (!status->light_lc_property_value) { + BT_ERR("%s, Failed to allocate memory", __func__); + osi_free(status); + return; + } + net_buf_simple_add_mem(status->light_lc_property_value, buf->data, buf->len); + val = (u8_t *)status; + len = sizeof(struct bt_mesh_light_lc_property_status); + break; + } + default: + BT_ERR("%s, Not a Lighting Status message opcode", __func__); + return; + } + + buf->data = val; + buf->len = len; + node = bt_mesh_is_model_message_publish(model, ctx, buf, true); + if (!node) { + BT_DBG("Unexpected light status message 0x%x", rsp); + } else { + switch (node->opcode) { + case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_GET: + case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_GET: + case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LAST_GET: + case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_DEFAULT_GET: + case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_RANGE_GET: + case BLE_MESH_MODEL_OP_LIGHT_CTL_GET: + case BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_GET: + case BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_RANGE_GET: + case BLE_MESH_MODEL_OP_LIGHT_CTL_DEFAULT_GET: + case BLE_MESH_MODEL_OP_LIGHT_HSL_GET: + case BLE_MESH_MODEL_OP_LIGHT_HSL_TARGET_GET: + case BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_GET: + case BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_GET: + case BLE_MESH_MODEL_OP_LIGHT_HSL_DEFAULT_GET: + case BLE_MESH_MODEL_OP_LIGHT_HSL_RANGE_GET: + case BLE_MESH_MODEL_OP_LIGHT_XYL_GET: + case BLE_MESH_MODEL_OP_LIGHT_XYL_TARGET_GET: + case BLE_MESH_MODEL_OP_LIGHT_XYL_DEFAULT_GET: + case BLE_MESH_MODEL_OP_LIGHT_XYL_RANGE_GET: + case BLE_MESH_MODEL_OP_LIGHT_LC_MODE_GET: + case BLE_MESH_MODEL_OP_LIGHT_LC_OM_GET: + case BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_GET: + case BLE_MESH_MODEL_OP_LIGHT_LC_PROPERTY_GET: + evt = 0x00; + break; + case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_SET: + case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_SET: + case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_DEFAULT_SET: + case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_RANGE_SET: + case BLE_MESH_MODEL_OP_LIGHT_CTL_SET: + case BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_SET: + case BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_RANGE_SET: + case BLE_MESH_MODEL_OP_LIGHT_CTL_DEFAULT_SET: + case BLE_MESH_MODEL_OP_LIGHT_HSL_SET: + case BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_SET: + case BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_SET: + case BLE_MESH_MODEL_OP_LIGHT_HSL_DEFAULT_SET: + case BLE_MESH_MODEL_OP_LIGHT_HSL_RANGE_SET: + case BLE_MESH_MODEL_OP_LIGHT_XYL_SET: + case BLE_MESH_MODEL_OP_LIGHT_XYL_DEFAULT_SET: + case BLE_MESH_MODEL_OP_LIGHT_XYL_RANGE_SET: + case BLE_MESH_MODEL_OP_LIGHT_LC_MODE_SET: + case BLE_MESH_MODEL_OP_LIGHT_LC_OM_SET: + case BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_SET: + case BLE_MESH_MODEL_OP_LIGHT_LC_PROPERTY_SET: + evt = 0x01; + break; + default: + break; + } + + bt_mesh_callback_light_status_to_btc(node->opcode, evt, model, ctx, val, len); + // Don't forget to release the node at the end. + bt_mesh_client_free_node(&internal->queue, node); + } + + switch (rsp) { + case BLE_MESH_MODEL_OP_LIGHT_LC_PROPERTY_STATUS: { + struct bt_mesh_light_lc_property_status *status; + status = (struct bt_mesh_light_lc_property_status *)val; + bt_mesh_free_buf(status->light_lc_property_value); + break; + } + default: + break; + } + + osi_free(val); + + return; +} + +const struct bt_mesh_model_op light_lightness_cli_op[] = { + { BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_STATUS, 2, light_status }, + { BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_STATUS, 2, light_status }, + { BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LAST_STATUS, 2, light_status }, + { BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_DEFAULT_STATUS, 2, light_status }, + { BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_RANGE_STATUS, 5, light_status }, + BLE_MESH_MODEL_OP_END, +}; + +const struct bt_mesh_model_op light_ctl_cli_op[] = { + { BLE_MESH_MODEL_OP_LIGHT_CTL_STATUS, 4, light_status }, + { BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_STATUS, 4, light_status }, + { BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_RANGE_STATUS, 5, light_status }, + { BLE_MESH_MODEL_OP_LIGHT_CTL_DEFAULT_STATUS, 6, light_status }, + BLE_MESH_MODEL_OP_END, +}; + +const struct bt_mesh_model_op light_hsl_cli_op[] = { + { BLE_MESH_MODEL_OP_LIGHT_HSL_STATUS, 6, light_status }, + { BLE_MESH_MODEL_OP_LIGHT_HSL_TARGET_STATUS, 6, light_status }, + { BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_STATUS, 2, light_status }, + { BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_STATUS, 2, light_status }, + { BLE_MESH_MODEL_OP_LIGHT_HSL_DEFAULT_STATUS, 6, light_status }, + { BLE_MESH_MODEL_OP_LIGHT_HSL_RANGE_STATUS, 9, light_status }, + BLE_MESH_MODEL_OP_END, +}; + +const struct bt_mesh_model_op light_xyl_cli_op[] = { + { BLE_MESH_MODEL_OP_LIGHT_XYL_STATUS, 6, light_status }, + { BLE_MESH_MODEL_OP_LIGHT_XYL_TARGET_STATUS, 6, light_status }, + { BLE_MESH_MODEL_OP_LIGHT_XYL_DEFAULT_STATUS, 6, light_status }, + { BLE_MESH_MODEL_OP_LIGHT_XYL_RANGE_STATUS, 9, light_status }, + BLE_MESH_MODEL_OP_END, +}; + +const struct bt_mesh_model_op light_lc_cli_op[] = { + { BLE_MESH_MODEL_OP_LIGHT_LC_MODE_STATUS, 1, light_status }, + { BLE_MESH_MODEL_OP_LIGHT_LC_OM_STATUS, 1, light_status }, + { BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_STATUS, 1, light_status }, + { BLE_MESH_MODEL_OP_LIGHT_LC_PROPERTY_STATUS, 2, light_status }, + BLE_MESH_MODEL_OP_END, +}; + +static int light_get_state(struct bt_mesh_common_param *common, void *value) +{ + NET_BUF_SIMPLE_DEFINE(msg, BLE_MESH_LIGHT_GET_STATE_MSG_LEN); + int err; + + bt_mesh_model_msg_init(&msg, common->opcode); + + if (value) { + switch (common->opcode) { + case BLE_MESH_MODEL_OP_LIGHT_LC_PROPERTY_GET: { + struct bt_mesh_light_lc_property_get *get; + get = (struct bt_mesh_light_lc_property_get *)value; + net_buf_simple_add_le16(&msg, get->light_lc_property_id); + break; + } + default: + BT_DBG("This lighting message should be sent with NULL get pointer"); + break; + } + } + + err = bt_mesh_client_send_msg(common->model, common->opcode, &common->ctx, &msg, + timeout_handler, common->msg_timeout, true, + common->cb, common->cb_data); + if (err) { + BT_ERR("%s, Failed to send Lighting Client Get message (err %d)", __func__, err); + } + + return err; +} + +static int light_set_state(struct bt_mesh_common_param *common, + void *value, u16_t value_len, bool need_ack) +{ + struct net_buf_simple *msg = NULL; + int err; + + msg = bt_mesh_alloc_buf(value_len); + if (!msg) { + BT_ERR("%s, Failed to allocate memory", __func__); + return -ENOMEM; + } + + bt_mesh_model_msg_init(msg, common->opcode); + + switch (common->opcode) { + case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_SET: + case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_SET_UNACK: { + struct bt_mesh_light_lightness_set *set; + set = (struct bt_mesh_light_lightness_set *)value; + net_buf_simple_add_le16(msg, set->lightness); + net_buf_simple_add_u8(msg, set->tid); + if (set->op_en) { + net_buf_simple_add_u8(msg, set->trans_time); + net_buf_simple_add_u8(msg, set->delay); + } + break; + } + case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_SET: + case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_SET_UNACK: { + struct bt_mesh_light_lightness_linear_set *set; + set = (struct bt_mesh_light_lightness_linear_set *)value; + net_buf_simple_add_le16(msg, set->lightness); + net_buf_simple_add_u8(msg, set->tid); + if (set->op_en) { + net_buf_simple_add_u8(msg, set->trans_time); + net_buf_simple_add_u8(msg, set->delay); + } + break; + } + case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_DEFAULT_SET: + case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_DEFAULT_SET_UNACK: { + struct bt_mesh_light_lightness_default_set *set; + set = (struct bt_mesh_light_lightness_default_set *)value; + net_buf_simple_add_le16(msg, set->lightness); + break; + } + case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_RANGE_SET: + case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_RANGE_SET_UNACK: { + struct bt_mesh_light_lightness_range_set *set; + set = (struct bt_mesh_light_lightness_range_set *)value; + net_buf_simple_add_le16(msg, set->range_min); + net_buf_simple_add_le16(msg, set->range_max); + break; + } + case BLE_MESH_MODEL_OP_LIGHT_CTL_SET: + case BLE_MESH_MODEL_OP_LIGHT_CTL_SET_UNACK: { + struct bt_mesh_light_ctl_set *set; + set = (struct bt_mesh_light_ctl_set *)value; + net_buf_simple_add_le16(msg, set->ctl_lightness); + net_buf_simple_add_le16(msg, set->ctl_temperature); + net_buf_simple_add_le16(msg, set->ctl_delta_uv); + net_buf_simple_add_u8(msg, set->tid); + if (set->op_en) { + net_buf_simple_add_u8(msg, set->trans_time); + net_buf_simple_add_u8(msg, set->delay); + } + break; + } + case BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_SET: + case BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_SET_UNACK: { + struct bt_mesh_light_ctl_temperature_set *set; + set = (struct bt_mesh_light_ctl_temperature_set *)value; + net_buf_simple_add_le16(msg, set->ctl_temperature); + net_buf_simple_add_le16(msg, set->ctl_delta_uv); + net_buf_simple_add_u8(msg, set->tid); + if (set->op_en) { + net_buf_simple_add_u8(msg, set->trans_time); + net_buf_simple_add_u8(msg, set->delay); + } + break; + } + case BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_RANGE_SET: + case BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_RANGE_SET_UNACK: { + struct bt_mesh_light_ctl_temperature_range_set *set; + set = (struct bt_mesh_light_ctl_temperature_range_set *)value; + net_buf_simple_add_le16(msg, set->range_min); + net_buf_simple_add_le16(msg, set->range_max); + break; + } + case BLE_MESH_MODEL_OP_LIGHT_CTL_DEFAULT_SET: + case BLE_MESH_MODEL_OP_LIGHT_CTL_DEFAULT_SET_UNACK: { + struct bt_mesh_light_ctl_default_set *set; + set = (struct bt_mesh_light_ctl_default_set *)value; + net_buf_simple_add_le16(msg, set->lightness); + net_buf_simple_add_le16(msg, set->temperature); + net_buf_simple_add_le16(msg, set->delta_uv); + break; + } + case BLE_MESH_MODEL_OP_LIGHT_HSL_SET: + case BLE_MESH_MODEL_OP_LIGHT_HSL_SET_UNACK: { + struct bt_mesh_light_hsl_set *set; + set = (struct bt_mesh_light_hsl_set *)value; + net_buf_simple_add_le16(msg, set->hsl_lightness); + net_buf_simple_add_le16(msg, set->hsl_hue); + net_buf_simple_add_le16(msg, set->hsl_saturation); + net_buf_simple_add_u8(msg, set->tid); + if (set->op_en) { + net_buf_simple_add_u8(msg, set->trans_time); + net_buf_simple_add_u8(msg, set->delay); + } + break; + } + case BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_SET: + case BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_SET_UNACK: { + struct bt_mesh_light_hsl_hue_set *set; + set = (struct bt_mesh_light_hsl_hue_set *)value; + net_buf_simple_add_le16(msg, set->hue); + net_buf_simple_add_u8(msg, set->tid); + if (set->op_en) { + net_buf_simple_add_u8(msg, set->trans_time); + net_buf_simple_add_u8(msg, set->delay); + } + break; + } + case BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_SET: + case BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_SET_UNACK: { + struct bt_mesh_light_hsl_saturation_set *set; + set = (struct bt_mesh_light_hsl_saturation_set *)value; + net_buf_simple_add_le16(msg, set->saturation); + net_buf_simple_add_u8(msg, set->tid); + if (set->op_en) { + net_buf_simple_add_u8(msg, set->trans_time); + net_buf_simple_add_u8(msg, set->delay); + } + break; + } + case BLE_MESH_MODEL_OP_LIGHT_HSL_DEFAULT_SET: + case BLE_MESH_MODEL_OP_LIGHT_HSL_DEFAULT_SET_UNACK: { + struct bt_mesh_light_hsl_default_set *set; + set = (struct bt_mesh_light_hsl_default_set *)value; + net_buf_simple_add_le16(msg, set->lightness); + net_buf_simple_add_le16(msg, set->hue); + net_buf_simple_add_le16(msg, set->saturation); + break; + } + case BLE_MESH_MODEL_OP_LIGHT_HSL_RANGE_SET: + case BLE_MESH_MODEL_OP_LIGHT_HSL_RANGE_SET_UNACK: { + struct bt_mesh_light_hsl_range_set *set; + set = (struct bt_mesh_light_hsl_range_set *)value; + net_buf_simple_add_le16(msg, set->hue_range_min); + net_buf_simple_add_le16(msg, set->hue_range_max); + net_buf_simple_add_le16(msg, set->saturation_range_min); + net_buf_simple_add_le16(msg, set->saturation_range_max); + break; + } + case BLE_MESH_MODEL_OP_LIGHT_XYL_SET: + case BLE_MESH_MODEL_OP_LIGHT_XYL_SET_UNACK: { + struct bt_mesh_light_xyl_set *set; + set = (struct bt_mesh_light_xyl_set *)value; + net_buf_simple_add_le16(msg, set->xyl_lightness); + net_buf_simple_add_le16(msg, set->xyl_x); + net_buf_simple_add_le16(msg, set->xyl_y); + net_buf_simple_add_u8(msg, set->tid); + if (set->op_en) { + net_buf_simple_add_u8(msg, set->trans_time); + net_buf_simple_add_u8(msg, set->delay); + } + break; + } + case BLE_MESH_MODEL_OP_LIGHT_XYL_DEFAULT_SET: + case BLE_MESH_MODEL_OP_LIGHT_XYL_DEFAULT_SET_UNACK: { + struct bt_mesh_light_xyl_default_set *set; + set = (struct bt_mesh_light_xyl_default_set *)value; + net_buf_simple_add_le16(msg, set->lightness); + net_buf_simple_add_le16(msg, set->xyl_x); + net_buf_simple_add_le16(msg, set->xyl_y); + break; + } + case BLE_MESH_MODEL_OP_LIGHT_XYL_RANGE_SET: + case BLE_MESH_MODEL_OP_LIGHT_XYL_RANGE_SET_UNACK: { + struct bt_mesh_light_xyl_range_set *set; + set = (struct bt_mesh_light_xyl_range_set *)value; + net_buf_simple_add_le16(msg, set->xyl_x_range_min); + net_buf_simple_add_le16(msg, set->xyl_x_range_max); + net_buf_simple_add_le16(msg, set->xyl_y_range_min); + net_buf_simple_add_le16(msg, set->xyl_y_range_max); + break; + } + case BLE_MESH_MODEL_OP_LIGHT_LC_MODE_SET: + case BLE_MESH_MODEL_OP_LIGHT_LC_MODE_SET_UNACK: { + struct bt_mesh_light_lc_mode_set *set; + set = (struct bt_mesh_light_lc_mode_set *)value; + net_buf_simple_add_u8(msg, set->mode); + break; + } + case BLE_MESH_MODEL_OP_LIGHT_LC_OM_SET: + case BLE_MESH_MODEL_OP_LIGHT_LC_OM_SET_UNACK: { + struct bt_mesh_light_lc_om_set *set; + set = (struct bt_mesh_light_lc_om_set *)value; + net_buf_simple_add_u8(msg, set->mode); + break; + } + case BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_SET: + case BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_SET_UNACK: { + struct bt_mesh_light_lc_light_onoff_set *set; + set = (struct bt_mesh_light_lc_light_onoff_set *)value; + net_buf_simple_add_u8(msg, set->light_onoff); + net_buf_simple_add_u8(msg, set->tid); + if (set->op_en) { + net_buf_simple_add_u8(msg, set->trans_time); + net_buf_simple_add_u8(msg, set->delay); + } + break; + } + case BLE_MESH_MODEL_OP_LIGHT_LC_PROPERTY_SET: + case BLE_MESH_MODEL_OP_LIGHT_LC_PROPERTY_SET_UNACK: { + struct bt_mesh_light_lc_property_set *set; + set = (struct bt_mesh_light_lc_property_set *)value; + net_buf_simple_add_le16(msg, set->light_lc_property_id); + net_buf_simple_add_mem(msg, set->light_lc_property_value->data, set->light_lc_property_value->len); + break; + } + default: + BT_ERR("%s, Not a Lighting Client Set message opcode", __func__); + err = -EINVAL; + goto end; + } + + err = bt_mesh_client_send_msg(common->model, common->opcode, &common->ctx, msg, + timeout_handler, common->msg_timeout, need_ack, + common->cb, common->cb_data); + if (err) { + BT_ERR("%s, Failed to send Lighting Client Set message (err %d)", __func__, err); + } + +end: + bt_mesh_free_buf(msg); + + return err; +} + +int bt_mesh_light_client_get_state(struct bt_mesh_common_param *common, void *get, void *status) +{ + bt_mesh_light_client_t *client = NULL; + + if (!common || !common->model) { + BT_ERR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + client = (bt_mesh_light_client_t *)common->model->user_data; + if (!client || !client->internal_data) { + BT_ERR("%s, Lighting Client user data is NULL", __func__); + return -EINVAL; + } + + switch (common->opcode) { + case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_GET: + case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_GET: + case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LAST_GET: + case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_DEFAULT_GET: + case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_RANGE_GET: + case BLE_MESH_MODEL_OP_LIGHT_CTL_GET: + case BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_GET: + case BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_RANGE_GET: + case BLE_MESH_MODEL_OP_LIGHT_CTL_DEFAULT_GET: + case BLE_MESH_MODEL_OP_LIGHT_HSL_GET: + case BLE_MESH_MODEL_OP_LIGHT_HSL_TARGET_GET: + case BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_GET: + case BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_GET: + case BLE_MESH_MODEL_OP_LIGHT_HSL_DEFAULT_GET: + case BLE_MESH_MODEL_OP_LIGHT_HSL_RANGE_GET: + case BLE_MESH_MODEL_OP_LIGHT_XYL_GET: + case BLE_MESH_MODEL_OP_LIGHT_XYL_TARGET_GET: + case BLE_MESH_MODEL_OP_LIGHT_XYL_DEFAULT_GET: + case BLE_MESH_MODEL_OP_LIGHT_XYL_RANGE_GET: + case BLE_MESH_MODEL_OP_LIGHT_LC_MODE_GET: + case BLE_MESH_MODEL_OP_LIGHT_LC_OM_GET: + case BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_GET: + break; + case BLE_MESH_MODEL_OP_LIGHT_LC_PROPERTY_GET: + if (!get) { + BT_ERR("%s, Lighting lc_property_get is NULL", __func__); + return -EINVAL; + } + break; + default: + BT_ERR("%s, Not a Lighting Client Get message opcode", __func__); + return -EINVAL; + } + + return light_get_state(common, get); +} + +int bt_mesh_light_client_set_state(struct bt_mesh_common_param *common, void *set, void *status) +{ + bt_mesh_light_client_t *client = NULL; + u16_t length = 0; + bool need_ack = false; + + if (!common || !common->model || !set) { + BT_ERR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + client = (bt_mesh_light_client_t *)common->model->user_data; + if (!client || !client->internal_data) { + BT_ERR("%s, Lighting Client user data is NULL", __func__); + return -EINVAL; + } + + switch (common->opcode) { + case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_SET: + need_ack = true; + case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_SET_UNACK: { + struct bt_mesh_light_lightness_set *value; + value = (struct bt_mesh_light_lightness_set *)set; + if (value->op_en) { + if ((value->trans_time & 0x3F) > 0x3E) { + BT_ERR("%s, Invalid Light Lightness Set transition time", __func__); + return -EINVAL; + } + } + length = BLE_MESH_LIGHT_LIGHTNESS_SET_MSG_LEN; + break; + } + case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_SET: + need_ack = true; + case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_SET_UNACK: { + struct bt_mesh_light_lightness_linear_set *value; + value = (struct bt_mesh_light_lightness_linear_set *)set; + if (value->op_en) { + if ((value->trans_time & 0x3F) > 0x3E) { + BT_ERR("%s, Invalid Light Lightness Linear Set transition time", __func__); + return -EINVAL; + } + } + length = BLE_MESH_LIGHT_LIGHTNESS_LINEAR_SET_MSG_LEN; + break; + } + case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_DEFAULT_SET: + need_ack = true; + case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_DEFAULT_SET_UNACK: + length = BLE_MESH_LIGHT_LIGHTNESS_DEFAULT_SET_MSG_LEN; + break; + case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_RANGE_SET: + need_ack = true; + case BLE_MESH_MODEL_OP_LIGHT_LIGHTNESS_RANGE_SET_UNACK: { + struct bt_mesh_light_lightness_range_set *value; + value = (struct bt_mesh_light_lightness_range_set *)set; + if (value->range_min > value->range_max) { + BT_ERR("%s, Light Lightness Range Set range min is greater than range max", __func__); + return -EINVAL; + } + length = BLE_MESH_LIGHT_LIGHTNESS_RANGE_SET_MSG_LEN; + break; + } + case BLE_MESH_MODEL_OP_LIGHT_CTL_SET: + need_ack = true; + case BLE_MESH_MODEL_OP_LIGHT_CTL_SET_UNACK: { + struct bt_mesh_light_ctl_set *value; + value = (struct bt_mesh_light_ctl_set *)set; + if (value->op_en) { + if ((value->trans_time & 0x3F) > 0x3E) { + BT_ERR("%s, Invalid Light CTL Set transition time", __func__); + return -EINVAL; + } + } + length = BLE_MESH_LIGHT_CTL_SET_MSG_LEN; + break; + } + case BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_SET: + need_ack = true; + case BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_SET_UNACK: { + struct bt_mesh_light_ctl_temperature_set *value; + value = (struct bt_mesh_light_ctl_temperature_set *)set; + if (value->op_en) { + if ((value->trans_time & 0x3F) > 0x3E) { + BT_ERR("%s, Invalid Light CTL Temperature Set transition time", __func__); + return -EINVAL; + } + } + length = BLE_MESH_LIGHT_CTL_TEMPERATURE_SET_MSG_LEN; + break; + } + case BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_RANGE_SET: + need_ack = true; + case BLE_MESH_MODEL_OP_LIGHT_CTL_TEMPERATURE_RANGE_SET_UNACK: { + struct bt_mesh_light_ctl_temperature_range_set *value; + value = (struct bt_mesh_light_ctl_temperature_range_set *)set; + if (value->range_min > value->range_max) { + BT_ERR("%s, Light CTL Temperature Range Set range min is greater than range max", __func__); + return -EINVAL; + } + length = BLE_MESH_LIGHT_CTL_TEMPERATURE_RANGE_SET_MSG_LEN; + break; + } + case BLE_MESH_MODEL_OP_LIGHT_CTL_DEFAULT_SET: + need_ack = true; + case BLE_MESH_MODEL_OP_LIGHT_CTL_DEFAULT_SET_UNACK: + length = BLE_MESH_LIGHT_CTL_DEFAULT_SET_MSG_LEN; + break; + case BLE_MESH_MODEL_OP_LIGHT_HSL_SET: + need_ack = true; + case BLE_MESH_MODEL_OP_LIGHT_HSL_SET_UNACK: { + struct bt_mesh_light_hsl_set *value; + value = (struct bt_mesh_light_hsl_set *)set; + if (value->op_en) { + if ((value->trans_time & 0x3F) > 0x3E) { + BT_ERR("%s, Invalid Light HSL Set transition time", __func__); + return -EINVAL; + } + } + length = BLE_MESH_LIGHT_HSL_SET_MSG_LEN; + break; + } + case BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_SET: + need_ack = true; + case BLE_MESH_MODEL_OP_LIGHT_HSL_HUE_SET_UNACK: { + struct bt_mesh_light_hsl_hue_set *value; + value = (struct bt_mesh_light_hsl_hue_set *)set; + if (value->op_en) { + if ((value->trans_time & 0x3F) > 0x3E) { + BT_ERR("%s, Invalid Light HSL Hue Set transition time", __func__); + return -EINVAL; + } + } + length = BLE_MESH_LIGHT_HSL_HUE_SET_MSG_LEN; + break; + } + case BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_SET: + need_ack = true; + case BLE_MESH_MODEL_OP_LIGHT_HSL_SATURATION_SET_UNACK: { + struct bt_mesh_light_hsl_saturation_set *value; + value = (struct bt_mesh_light_hsl_saturation_set *)set; + if (value->op_en) { + if ((value->trans_time & 0x3F) > 0x3E) { + BT_ERR("%s, Invalid Light HSL Saturation Set transition time", __func__); + return -EINVAL; + } + } + length = BLE_MESH_LIGHT_HSL_SATURATION_SET_MSG_LEN; + break; + } + case BLE_MESH_MODEL_OP_LIGHT_HSL_DEFAULT_SET: + need_ack = true; + case BLE_MESH_MODEL_OP_LIGHT_HSL_DEFAULT_SET_UNACK: + length = BLE_MESH_LIGHT_HSL_DEFAULT_SET_MSG_LEN; + break; + case BLE_MESH_MODEL_OP_LIGHT_HSL_RANGE_SET: + need_ack = true; + case BLE_MESH_MODEL_OP_LIGHT_HSL_RANGE_SET_UNACK: { + struct bt_mesh_light_hsl_range_set *value; + value = (struct bt_mesh_light_hsl_range_set *)set; + if (value->hue_range_min > value->hue_range_max || + value->saturation_range_min > value->saturation_range_max) { + BT_ERR("%s, Light HSL Range Set range min is greater than range max", __func__); + return -EINVAL; + } + length = BLE_MESH_LIGHT_HSL_RANGE_SET_MSG_LEN; + break; + } + case BLE_MESH_MODEL_OP_LIGHT_XYL_SET: + need_ack = true; + case BLE_MESH_MODEL_OP_LIGHT_XYL_SET_UNACK: { + struct bt_mesh_light_xyl_set *value; + value = (struct bt_mesh_light_xyl_set *)set; + if (value->op_en) { + if ((value->trans_time & 0x3F) > 0x3E) { + BT_ERR("%s, Invalid Light xyL Set transition time", __func__); + return -EINVAL; + } + } + length = BLE_MESH_LIGHT_XYL_SET_MSG_LEN; + break; + } + case BLE_MESH_MODEL_OP_LIGHT_XYL_DEFAULT_SET: + need_ack = true; + case BLE_MESH_MODEL_OP_LIGHT_XYL_DEFAULT_SET_UNACK: + length = BLE_MESH_LIGHT_XYL_DEFAULT_SET_MSG_LEN; + break; + case BLE_MESH_MODEL_OP_LIGHT_XYL_RANGE_SET: + need_ack = true; + case BLE_MESH_MODEL_OP_LIGHT_XYL_RANGE_SET_UNACK: { + struct bt_mesh_light_xyl_range_set *value; + value = (struct bt_mesh_light_xyl_range_set *)set; + if (value->xyl_x_range_min > value->xyl_x_range_max || + value->xyl_y_range_min > value->xyl_y_range_max) { + BT_ERR("%s, Light xyL Range Set range min is greater than range max", __func__); + return -EINVAL; + } + length = BLE_MESH_LIGHT_XYL_RANGE_SET_MSG_LEN; + break; + } + case BLE_MESH_MODEL_OP_LIGHT_LC_MODE_SET: + need_ack = true; + case BLE_MESH_MODEL_OP_LIGHT_LC_MODE_SET_UNACK: + length = BLE_MESH_LIGHT_LC_MODE_SET_MSG_LEN; + break; + case BLE_MESH_MODEL_OP_LIGHT_LC_OM_SET: + need_ack = true; + case BLE_MESH_MODEL_OP_LIGHT_LC_OM_SET_UNACK: + length = BLE_MESH_LIGHT_LC_OM_SET_MSG_LEN; + break; + case BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_SET: + need_ack = true; + case BLE_MESH_MODEL_OP_LIGHT_LC_LIGHT_ONOFF_SET_UNACK: { + struct bt_mesh_light_lc_light_onoff_set *value; + value = (struct bt_mesh_light_lc_light_onoff_set *)set; + if (value->op_en) { + if ((value->trans_time & 0x3F) > 0x3E) { + BT_ERR("%s, Invalid Light LC Light OnOff Set transition time", __func__); + return -EINVAL; + } + } + length = BLE_MESH_LIGHT_LC_LIGHT_ONOFF_SET_MSG_LEN; + break; + } + case BLE_MESH_MODEL_OP_LIGHT_LC_PROPERTY_SET: + need_ack = true; + case BLE_MESH_MODEL_OP_LIGHT_LC_PROPERTY_SET_UNACK: { + struct bt_mesh_light_lc_property_set *value; + value = (struct bt_mesh_light_lc_property_set *)set; + if (!value->light_lc_property_value) { + BT_ERR("%s, Lighting light_lc_property_value is NULL", __func__); + return -EINVAL; + } + length = (1 + 2 + value->light_lc_property_value->len + 4); + break; + } + default: + BT_ERR("%s, Not a Lighting Client Set message opcode", __func__); + return -EINVAL; + } + + return light_set_state(common, set, length, need_ack); +} + +static int light_client_init(struct bt_mesh_model *model, bool primary) +{ + light_internal_data_t *internal = NULL; + bt_mesh_light_client_t *client = NULL; + + BT_DBG("primary %u", primary); + + if (!model) { + BT_ERR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + client = (bt_mesh_light_client_t *)model->user_data; + if (!client) { + BT_ERR("%s, Lighting Client user_data is NULL", __func__); + return -EINVAL; + } + + /* TODO: call osi_free() when deinit function is invoked*/ + internal = osi_calloc(sizeof(light_internal_data_t)); + if (!internal) { + BT_ERR("%s, Failed to allocate memory", __func__); + return -ENOMEM; + } + + sys_slist_init(&internal->queue); + + client->model = model; + client->op_pair_size = ARRAY_SIZE(light_op_pair); + client->op_pair = light_op_pair; + client->internal_data = internal; + + return 0; +} + +int bt_mesh_light_lightness_cli_init(struct bt_mesh_model *model, bool primary) +{ + return light_client_init(model, primary); +} + +int bt_mesh_light_ctl_cli_init(struct bt_mesh_model *model, bool primary) +{ + return light_client_init(model, primary); +} + +int bt_mesh_light_hsl_cli_init(struct bt_mesh_model *model, bool primary) +{ + return light_client_init(model, primary); +} + +int bt_mesh_light_xyl_cli_init(struct bt_mesh_model *model, bool primary) +{ + return light_client_init(model, primary); +} + +int bt_mesh_light_lc_cli_init(struct bt_mesh_model *model, bool primary) +{ + return light_client_init(model, primary); +} \ No newline at end of file diff --git a/components/bt/ble_mesh/mesh_models/mesh_common.c b/components/bt/ble_mesh/mesh_models/mesh_common.c new file mode 100644 index 0000000000..801644ace1 --- /dev/null +++ b/components/bt/ble_mesh/mesh_models/mesh_common.c @@ -0,0 +1,46 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 + +#include "mesh_common.h" + +struct net_buf_simple *bt_mesh_alloc_buf(u16_t size) +{ + struct net_buf_simple *buf = NULL; + u8_t *data = NULL; + + buf = (struct net_buf_simple *)osi_calloc(sizeof(struct net_buf_simple) + size); + if (!buf) { + BT_ERR("%s, Failed to allocate memory", __func__); + return NULL; + } + + data = (u8_t *)buf + sizeof(struct net_buf_simple); + + buf->data = data; + buf->len = 0; + buf->size = size; + buf->__buf = data; + + return buf; +} + +void bt_mesh_free_buf(struct net_buf_simple *buf) +{ + if (buf) { + osi_free(buf); + } +} diff --git a/components/bt/ble_mesh/mesh_models/model_common.c b/components/bt/ble_mesh/mesh_models/model_common.c new file mode 100644 index 0000000000..c904543de5 --- /dev/null +++ b/components/bt/ble_mesh/mesh_models/model_common.c @@ -0,0 +1,336 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 + +#include "osi/allocator.h" + +#include "mesh_access.h" +#include "mesh_buf.h" +#include "mesh_slist.h" +#include "mesh_main.h" + +#include "mesh.h" +#include "model_common.h" + +bt_mesh_client_node_t *bt_mesh_is_model_message_publish(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf, + bool need_pub) +{ + bt_mesh_internal_data_t *data = NULL; + bt_mesh_client_common_t *cli = NULL; + bt_mesh_client_node_t *node = NULL; + u32_t rsp; + + if (!model || !ctx || !buf) { + BT_ERR("%s, Invalid parameter", __func__); + return NULL; + } + + cli = (bt_mesh_client_common_t *)model->user_data; + if (!cli) { + BT_ERR("%s, Clinet user_data is NULL", __func__); + return NULL; + } + + rsp = ctx->recv_op; + + /** If the received message address is not a unicast address, + * the address may be a group/virtual address, and we push + * this message to the application layer. + */ + if (!BLE_MESH_ADDR_IS_UNICAST(ctx->recv_dst)) { + BT_DBG("Unexpected status message 0x%x", rsp); + if (cli->publish_status && need_pub) { + cli->publish_status(rsp, model, ctx, buf); + } + return NULL; + } + + /** If the source address of the received status message is + * different with the destination address of the sending + * message, then the message is from another element and + * push it to application layer. + */ + data = (bt_mesh_internal_data_t *)cli->internal_data; + if (!data) { + BT_ERR("%s, Client internal_data is NULL", __func__); + return NULL; + } + + if ((node = bt_mesh_client_pick_node(&data->queue, ctx->addr)) == NULL) { + BT_DBG("Unexpected status message 0x%x", rsp); + if (cli->publish_status && need_pub) { + cli->publish_status(rsp, model, ctx, buf); + } + return NULL; + } + + if (node->op_pending != rsp) { + BT_DBG("Unexpected status message 0x%x", rsp); + if (cli->publish_status && need_pub) { + cli->publish_status(rsp, model, ctx, buf); + } + return NULL; + } + + return node; +} + +bool bt_mesh_client_find_opcode_in_list(sys_slist_t *list, u32_t opcode) +{ + if (sys_slist_is_empty(list)) { + return false; + } + + sys_snode_t *cur = NULL; bt_mesh_client_node_t *node = NULL; + for (cur = sys_slist_peek_head(list); + cur != NULL; cur = sys_slist_peek_next(cur)) { + node = (bt_mesh_client_node_t *)cur; + if (node->op_pending == opcode) { + return true; + } + return NULL; + } + + return node; +} + +bool bt_mesh_client_check_node_in_list(sys_slist_t *list, u16_t tx_dst) +{ + if (sys_slist_is_empty(list)) { + return false; + } + + sys_snode_t *cur = NULL; bt_mesh_client_node_t *node = NULL; + for (cur = sys_slist_peek_head(list); + cur != NULL; cur = sys_slist_peek_next(cur)) { + node = (bt_mesh_client_node_t *)cur; + if (node->ctx.addr == tx_dst) { + return true; + } + } + + return false; +} + +bt_mesh_client_node_t *bt_mesh_client_pick_node(sys_slist_t *list, u16_t tx_dst) +{ + if (sys_slist_is_empty(list)) { + return NULL; + } + + sys_snode_t *cur = NULL; bt_mesh_client_node_t *node = NULL; + for (cur = sys_slist_peek_head(list); + cur != NULL; cur = sys_slist_peek_next(cur)) { + node = (bt_mesh_client_node_t *)cur; + if (node->ctx.addr == tx_dst) { + return node; + } + } + + return NULL; +} + +static u32_t bt_mesh_client_get_status_op(const bt_mesh_client_op_pair_t *op_pair, + int size, u32_t opcode) +{ + if (!op_pair || size == 0) { + return 0; + } + + const bt_mesh_client_op_pair_t *op = op_pair; + for (int i = 0; i < size; i++) { + if (op->cli_op == opcode) { + return op->status_op; + } + op++; + } + + return 0; +} + +int bt_mesh_client_send_msg(struct bt_mesh_model *model, + u32_t opcode, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *msg, + k_work_handler_t timer_handler, + s32_t timeout, bool need_ack, + const struct bt_mesh_send_cb *cb, + void *cb_data) +{ + bt_mesh_internal_data_t *internal = NULL; + bt_mesh_client_common_t *cli = NULL; + bt_mesh_client_node_t *node = NULL; + int err; + + if (!model || !ctx || !msg) { + BT_ERR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + cli = (bt_mesh_client_common_t *)model->user_data; + __ASSERT(cli, "Invalid client value when sent client msg."); + internal = (bt_mesh_internal_data_t *)cli->internal_data; + __ASSERT(internal, "Invalid internal value when sent client msg."); + + if (!need_ack) { + /* If this is an unack message, send it directly. */ + return bt_mesh_model_send(model, ctx, msg, cb, cb_data); + } + + if (bt_mesh_client_check_node_in_list(&internal->queue, ctx->addr)) { + BT_ERR("%s, Busy sending message to DST 0x%04x", __func__, ctx->addr); + err = -EBUSY; + } else { + /* Don't forget to free the node in the timeout (timer_handler) function. */ + node = (bt_mesh_client_node_t *)osi_calloc(sizeof(bt_mesh_client_node_t)); + if (!node) { + BT_ERR("%s, Failed to allocate memory", __func__); + return -ENOMEM; + } + memcpy(&node->ctx, ctx, sizeof(struct bt_mesh_msg_ctx)); + node->ctx.model = model; + node->opcode = opcode; + if ((node->op_pending = bt_mesh_client_get_status_op(cli->op_pair, cli->op_pair_size, opcode)) == 0) { + BT_ERR("%s, Not found the status opcode in the op_pair list", __func__); + osi_free(node); + return -EINVAL; + } + if ((err = bt_mesh_model_send(model, ctx, msg, cb, cb_data)) != 0) { + osi_free(node); + } else { + sys_slist_append(&internal->queue, &node->client_node); + k_delayed_work_init(&node->timer, timer_handler); + k_delayed_work_submit(&node->timer, timeout ? timeout : CONFIG_BLE_MESH_CLIENT_MSG_TIMEOUT); + } + } + + return err; +} + +int bt_mesh_client_init(struct bt_mesh_model *model) +{ + bt_mesh_internal_data_t *data = NULL; + bt_mesh_client_common_t *cli = NULL; + + if (!model) { + BT_ERR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + if (!model->op) { + BT_ERR("%s, Client model op is NULL", __func__); + return -EINVAL; + } + + cli = model->user_data; + if (!cli) { + BT_ERR("%s, Client user_data is NULL", __func__); + return -EINVAL; + } + + /* TODO: call osi_free() when deinit function is invoked */ + data = osi_calloc(sizeof(bt_mesh_internal_data_t)); + if (!data) { + BT_ERR("%s, Failed to allocate memory", __func__); + return -ENOMEM; + } + + /* Init the client data queue */ + sys_slist_init(&data->queue); + + cli->model = model; + cli->internal_data = data; + + return 0; +} + +int bt_mesh_client_free_node(sys_slist_t *queue, bt_mesh_client_node_t *node) +{ + if (!queue || !node) { + return -EINVAL; + } + + // Free the node timer + k_delayed_work_free(&node->timer); + // Release the client node from the queue + sys_slist_find_and_remove(queue, &node->client_node); + // Free the node + osi_free(node); + + return 0; +} + +int bt_mesh_set_model_role(bt_mesh_role_param_t *common) +{ + bt_mesh_client_common_t *client = NULL; + + if (!common || !common->model || !common->model->user_data) { + BT_ERR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + client = (bt_mesh_client_common_t *)common->model->user_data; + + switch (common->role) { +#if CONFIG_BLE_MESH_NODE + case NODE: + /* no matter if provisioner is enabled/disabled , node role can be used to send messages */ + client->msg_role = NODE; + break; +#endif +#if CONFIG_BLE_MESH_PROVISIONER + case PROVISIONER: + /* if provisioner is not enabled, provisioner role can't be used to send messages */ + if (!bt_mesh_is_provisioner_en()) { + BT_ERR("%s, Provisioner is disabled", __func__); + return -EINVAL; + } + client->msg_role = PROVISIONER; + break; +#endif +#if CONFIG_BLE_MESH_FAST_PROV + case FAST_PROV: + client->msg_role = FAST_PROV; + break; +#endif + default: + BT_WARN("%s, Unknown model role %x", __func__, common->role); + return -EINVAL; + } + + return 0; +} + +u8_t bt_mesh_get_model_role(struct bt_mesh_model *model, bool srv_send) +{ + bt_mesh_client_common_t *client = NULL; + + if (srv_send) { + BT_DBG("%s, Message is sent by a server model", __func__); + return NODE; + } + + if (!model || !model->user_data) { + BT_ERR("%s, Invalid parameter", __func__); + return ROLE_NVAL; + } + + client = (bt_mesh_client_common_t *)model->user_data; + + return client->msg_role; +} diff --git a/components/bt/ble_mesh/mesh_models/sensor_client.c b/components/bt/ble_mesh/mesh_models/sensor_client.c new file mode 100644 index 0000000000..1933d1027a --- /dev/null +++ b/components/bt/ble_mesh/mesh_models/sensor_client.c @@ -0,0 +1,616 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 +#include + +#include "osi/allocator.h" +#include "sdkconfig.h" + +#include "mesh_types.h" +#include "mesh_kernel.h" +#include "mesh_trace.h" + +#include "mesh.h" +#include "model_opcode.h" +#include "mesh_common.h" +#include "sensor_client.h" + +#include "btc_ble_mesh_sensor_model.h" + +/** The following are the macro definitions of sensor client + * model messages length, and a message is composed of three + * parts: Opcode + msg_value + MIC + */ +/* Sensor client messages length */ +#define BLE_MESH_SENSOR_DESCRIPTOR_GET_MSG_LEN (2 + 2 + 4) +#define BLE_MESH_SENSOR_CADENCE_GET_MSG_LEN (2 + 2 + 4) +#define BLE_MESH_SENSOR_CADENCE_SET_MSG_LEN /* variable */ +#define BLE_MESH_SENSOR_SETTINGS_GET_MSG_LEN (2 + 2 + 4) +#define BLE_MESH_SENSOR_SETTING_GET_MSG_LEN (2 + 4 + 4) +#define BLE_MESH_SENSOR_SETTING_SET_MSG_LEN /* variable */ +#define BLE_MESH_SENSOR_GET_MSG_LEN (2 + 2 + 4) +#define BLE_MESH_SENSOR_COLUMN_GET_MSG_LEN /* variable */ +#define BLE_MESH_SENSOR_SERIES_GET_MSG_LEN /* variable */ + +static const bt_mesh_client_op_pair_t sensor_op_pair[] = { + { BLE_MESH_MODEL_OP_SENSOR_DESCRIPTOR_GET, BLE_MESH_MODEL_OP_SENSOR_DESCRIPTOR_STATUS }, + { BLE_MESH_MODEL_OP_SENSOR_CADENCE_GET, BLE_MESH_MODEL_OP_SENSOR_CADENCE_STATUS }, + { BLE_MESH_MODEL_OP_SENSOR_CADENCE_SET, BLE_MESH_MODEL_OP_SENSOR_CADENCE_STATUS }, + { BLE_MESH_MODEL_OP_SENSOR_SETTINGS_GET, BLE_MESH_MODEL_OP_SENSOR_SETTINGS_STATUS }, + { BLE_MESH_MODEL_OP_SENSOR_SETTING_GET, BLE_MESH_MODEL_OP_SENSOR_SETTING_STATUS }, + { BLE_MESH_MODEL_OP_SENSOR_SETTING_SET, BLE_MESH_MODEL_OP_SENSOR_SETTING_STATUS }, + { BLE_MESH_MODEL_OP_SENSOR_GET, BLE_MESH_MODEL_OP_SENSOR_STATUS }, + { BLE_MESH_MODEL_OP_SENSOR_COLUMN_GET, BLE_MESH_MODEL_OP_SENSOR_COLUMN_STATUS }, + { BLE_MESH_MODEL_OP_SENSOR_SERIES_GET, BLE_MESH_MODEL_OP_SENSOR_SERIES_STATUS }, +}; + +static void timeout_handler(struct k_work *work) +{ + sensor_internal_data_t *internal = NULL; + bt_mesh_sensor_client_t *client = NULL; + bt_mesh_client_node_t *node = NULL; + + BT_WARN("Receive sensor status message timeout"); + + node = CONTAINER_OF(work, bt_mesh_client_node_t, timer.work); + if (!node || !node->ctx.model) { + BT_ERR("%s, Invalid parameter", __func__); + return; + } + + client = (bt_mesh_sensor_client_t *)node->ctx.model->user_data; + if (!client) { + BT_ERR("%s, Sensor Client user_data is NULL", __func__); + return; + } + + internal = (sensor_internal_data_t *)client->internal_data; + if (!internal) { + BT_ERR("%s, Sensor Client internal_data is NULL", __func__); + return; + } + + bt_mesh_callback_sensor_status_to_btc(node->opcode, 0x03, node->ctx.model, + &node->ctx, NULL, 0); + + bt_mesh_client_free_node(&internal->queue, node); + + return; +} + +static void sensor_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + sensor_internal_data_t *internal = NULL; + bt_mesh_sensor_client_t *client = NULL; + bt_mesh_client_node_t *node = NULL; + u8_t *val = NULL; + u8_t evt = 0xFF; + u32_t rsp = 0; + size_t len = 0; + + BT_DBG("%s, len %d, bytes %s", __func__, buf->len, bt_hex(buf->data, buf->len)); + + client = (bt_mesh_sensor_client_t *)model->user_data; + if (!client) { + BT_ERR("%s, Sensor Client user_data is NULL", __func__); + return; + } + + internal = (sensor_internal_data_t *)client->internal_data; + if (!internal) { + BT_ERR("%s, Sensor Client internal_data is NULL", __func__); + return; + } + + rsp = ctx->recv_op; + switch (rsp) { + case BLE_MESH_MODEL_OP_SENSOR_DESCRIPTOR_STATUS: { + struct bt_mesh_sensor_descriptor_status *status = NULL; + status = osi_calloc(sizeof(struct bt_mesh_sensor_descriptor_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->descriptor = bt_mesh_alloc_buf(buf->len); + if (!status->descriptor) { + BT_ERR("%s, Failed to allocate memory", __func__); + osi_free(status); + return; + } + net_buf_simple_add_mem(status->descriptor, buf->data, buf->len); + val = (u8_t *)status; + len = sizeof(struct bt_mesh_sensor_descriptor_status); + break; + } + case BLE_MESH_MODEL_OP_SENSOR_CADENCE_STATUS: { + struct bt_mesh_sensor_cadence_status *status = NULL; + status = osi_calloc(sizeof(struct bt_mesh_sensor_cadence_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->property_id = net_buf_simple_pull_le16(buf); + status->sensor_cadence_value = bt_mesh_alloc_buf(buf->len); + if (!status->sensor_cadence_value) { + BT_ERR("%s, Failed to allocate memory", __func__); + osi_free(status); + return; + } + net_buf_simple_add_mem(status->sensor_cadence_value, buf->data, buf->len); + val = (u8_t *)status; + len = sizeof(struct bt_mesh_sensor_cadence_status); + break; + } + case BLE_MESH_MODEL_OP_SENSOR_SETTINGS_STATUS: { + struct bt_mesh_sensor_settings_status *status = NULL; + status = osi_calloc(sizeof(struct bt_mesh_sensor_settings_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->sensor_property_id = net_buf_simple_pull_le16(buf); + status->sensor_setting_property_ids = bt_mesh_alloc_buf(buf->len); + if (!status->sensor_setting_property_ids) { + BT_ERR("%s, Failed to allocate memory", __func__); + osi_free(status); + return; + } + net_buf_simple_add_mem(status->sensor_setting_property_ids, buf->data, buf->len); + val = (u8_t *)status; + len = sizeof(struct bt_mesh_sensor_settings_status); + break; + } + case BLE_MESH_MODEL_OP_SENSOR_SETTING_STATUS: { + struct bt_mesh_sensor_setting_status *status = NULL; + status = osi_calloc(sizeof(struct bt_mesh_sensor_setting_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->sensor_property_id = net_buf_simple_pull_le16(buf); + status->sensor_setting_property_id = net_buf_simple_pull_le16(buf); + if (buf->len) { + status->op_en = true; + status->sensor_setting_access = net_buf_simple_pull_u8(buf); + status->sensor_setting_raw = bt_mesh_alloc_buf(buf->len); + if (!status->sensor_setting_raw) { + BT_ERR("%s, Failed to allocate memory", __func__); + osi_free(status); + return; + } + net_buf_simple_add_mem(status->sensor_setting_raw, buf->data, buf->len); + } + val = (u8_t *)status; + len = sizeof(struct bt_mesh_sensor_setting_status); + break; + } + case BLE_MESH_MODEL_OP_SENSOR_STATUS: { + struct bt_mesh_sensor_status *status = NULL; + status = osi_calloc(sizeof(struct bt_mesh_sensor_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->marshalled_sensor_data = bt_mesh_alloc_buf(buf->len); + if (!status->marshalled_sensor_data) { + BT_ERR("%s, Failed to allocate memory", __func__); + osi_free(status); + return; + } + net_buf_simple_add_mem(status->marshalled_sensor_data, buf->data, buf->len); + val = (u8_t *)status; + len = sizeof(struct bt_mesh_sensor_status); + break; + } + case BLE_MESH_MODEL_OP_SENSOR_COLUMN_STATUS: { + struct bt_mesh_sensor_column_status *status = NULL; + status = osi_calloc(sizeof(struct bt_mesh_sensor_column_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->property_id = net_buf_simple_pull_le16(buf); + status->sensor_column_value = bt_mesh_alloc_buf(buf->len); + if (!status->sensor_column_value) { + BT_ERR("%s, Failed to allocate memory", __func__); + osi_free(status); + return; + } + net_buf_simple_add_mem(status->sensor_column_value, buf->data, buf->len); + val = (u8_t *)status; + len = sizeof(struct bt_mesh_sensor_column_status); + break; + } + case BLE_MESH_MODEL_OP_SENSOR_SERIES_STATUS: { + struct bt_mesh_sensor_series_status *status = NULL; + status = osi_calloc(sizeof(struct bt_mesh_sensor_series_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->property_id = net_buf_simple_pull_le16(buf); + status->sensor_series_value = bt_mesh_alloc_buf(buf->len); + if (!status->sensor_series_value) { + BT_ERR("%s, Failed to allocate memory", __func__); + osi_free(status); + return; + } + net_buf_simple_add_mem(status->sensor_series_value, buf->data, buf->len); + val = (u8_t *)status; + len = sizeof(struct bt_mesh_sensor_series_status); + break; + } + default: + BT_ERR("%s, Not a Sensor Status message opcode", __func__); + return; + } + + buf->data = val; + buf->len = len; + node = bt_mesh_is_model_message_publish(model, ctx, buf, true); + if (!node) { + BT_DBG("Unexpected sensor status message 0x%x", rsp); + } else { + switch (node->opcode) { + case BLE_MESH_MODEL_OP_SENSOR_DESCRIPTOR_GET: + case BLE_MESH_MODEL_OP_SENSOR_CADENCE_GET: + case BLE_MESH_MODEL_OP_SENSOR_SETTINGS_GET: + case BLE_MESH_MODEL_OP_SENSOR_SETTING_GET: + case BLE_MESH_MODEL_OP_SENSOR_GET: + case BLE_MESH_MODEL_OP_SENSOR_COLUMN_GET: + case BLE_MESH_MODEL_OP_SENSOR_SERIES_GET: + evt = 0x00; + break; + case BLE_MESH_MODEL_OP_SENSOR_CADENCE_SET: + case BLE_MESH_MODEL_OP_SENSOR_SETTING_SET: + evt = 0x01; + break; + default: + break; + } + + bt_mesh_callback_sensor_status_to_btc(node->opcode, evt, model, ctx, val, len); + // Don't forget to release the node at the end. + bt_mesh_client_free_node(&internal->queue, node); + } + + switch (rsp) { + case BLE_MESH_MODEL_OP_SENSOR_DESCRIPTOR_STATUS: { + struct bt_mesh_sensor_descriptor_status *status; + status = (struct bt_mesh_sensor_descriptor_status *)val; + bt_mesh_free_buf(status->descriptor); + break; + } + case BLE_MESH_MODEL_OP_SENSOR_CADENCE_STATUS: { + struct bt_mesh_sensor_cadence_status *status; + status = (struct bt_mesh_sensor_cadence_status *)val; + bt_mesh_free_buf(status->sensor_cadence_value); + break; + } + case BLE_MESH_MODEL_OP_SENSOR_SETTINGS_STATUS: { + struct bt_mesh_sensor_settings_status *status; + status = (struct bt_mesh_sensor_settings_status *)val; + bt_mesh_free_buf(status->sensor_setting_property_ids); + break; + } + case BLE_MESH_MODEL_OP_SENSOR_SETTING_STATUS: { + struct bt_mesh_sensor_setting_status *status; + status = (struct bt_mesh_sensor_setting_status *)val; + bt_mesh_free_buf(status->sensor_setting_raw); + break; + } + case BLE_MESH_MODEL_OP_SENSOR_STATUS: { + struct bt_mesh_sensor_status *status; + status = (struct bt_mesh_sensor_status *)val; + bt_mesh_free_buf(status->marshalled_sensor_data); + break; + } + case BLE_MESH_MODEL_OP_SENSOR_COLUMN_STATUS: { + struct bt_mesh_sensor_column_status *status; + status = (struct bt_mesh_sensor_column_status *)val; + bt_mesh_free_buf(status->sensor_column_value); + break; + } + case BLE_MESH_MODEL_OP_SENSOR_SERIES_STATUS: { + struct bt_mesh_sensor_series_status *status; + status = (struct bt_mesh_sensor_series_status *)val; + bt_mesh_free_buf(status->sensor_series_value); + break; + } + default: + break; + } + + osi_free(val); + + return; +} + +const struct bt_mesh_model_op sensor_cli_op[] = { + { BLE_MESH_MODEL_OP_SENSOR_DESCRIPTOR_STATUS, 0, sensor_status }, + { BLE_MESH_MODEL_OP_SENSOR_CADENCE_STATUS, 2, sensor_status }, + { BLE_MESH_MODEL_OP_SENSOR_SETTINGS_STATUS, 2, sensor_status }, + { BLE_MESH_MODEL_OP_SENSOR_SETTING_STATUS, 4, sensor_status }, + { BLE_MESH_MODEL_OP_SENSOR_STATUS, 0, sensor_status }, + { BLE_MESH_MODEL_OP_SENSOR_COLUMN_STATUS, 2, sensor_status }, + { BLE_MESH_MODEL_OP_SENSOR_SERIES_STATUS, 2, sensor_status }, + BLE_MESH_MODEL_OP_END, +}; + +static int sensor_act_state(struct bt_mesh_common_param *common, + void *value, u16_t value_len, bool need_ack) +{ + struct net_buf_simple *msg = NULL; + int err; + + msg = bt_mesh_alloc_buf(value_len); + if (!msg) { + BT_ERR("%s, Failed to allocate memory", __func__); + return -ENOMEM; + } + + bt_mesh_model_msg_init(msg, common->opcode); + + switch (common->opcode) { + case BLE_MESH_MODEL_OP_SENSOR_DESCRIPTOR_GET: { + struct bt_mesh_sensor_descriptor_get *act; + act = (struct bt_mesh_sensor_descriptor_get *)value; + if (act->op_en) { + net_buf_simple_add_le16(msg, act->property_id); + } + break; + } + case BLE_MESH_MODEL_OP_SENSOR_CADENCE_GET: { + struct bt_mesh_sensor_cadence_get *act; + act = (struct bt_mesh_sensor_cadence_get *)value; + net_buf_simple_add_le16(msg, act->property_id); + break; + } + case BLE_MESH_MODEL_OP_SENSOR_CADENCE_SET: + case BLE_MESH_MODEL_OP_SENSOR_CADENCE_SET_UNACK: { + struct bt_mesh_sensor_cadence_set *act; + act = (struct bt_mesh_sensor_cadence_set *)value; + net_buf_simple_add_le16(msg, act->property_id); + net_buf_simple_add_u8(msg, act->status_trigger_type << 7 | act->fast_cadence_period_divisor); + net_buf_simple_add_mem(msg, act->status_trigger_delta_down->data, act->status_trigger_delta_down->len); + net_buf_simple_add_mem(msg, act->status_trigger_delta_up->data, act->status_trigger_delta_up->len); + net_buf_simple_add_u8(msg, act->status_min_interval); + net_buf_simple_add_mem(msg, act->fast_cadence_low->data, act->fast_cadence_low->len); + net_buf_simple_add_mem(msg, act->fast_cadence_high->data, act->fast_cadence_high->len); + break; + } + case BLE_MESH_MODEL_OP_SENSOR_SETTINGS_GET: { + struct bt_mesh_sensor_settings_get *act; + act = (struct bt_mesh_sensor_settings_get *)value; + net_buf_simple_add_le16(msg, act->sensor_property_id); + break; + } + case BLE_MESH_MODEL_OP_SENSOR_SETTING_GET: { + struct bt_mesh_sensor_setting_get *act; + act = (struct bt_mesh_sensor_setting_get *)value; + net_buf_simple_add_le16(msg, act->sensor_property_id); + net_buf_simple_add_le16(msg, act->sensor_setting_property_id); + break; + } + case BLE_MESH_MODEL_OP_SENSOR_SETTING_SET: + case BLE_MESH_MODEL_OP_SENSOR_SETTING_SET_UNACK: { + struct bt_mesh_sensor_setting_set *act; + act = (struct bt_mesh_sensor_setting_set *)value; + net_buf_simple_add_le16(msg, act->sensor_property_id); + net_buf_simple_add_le16(msg, act->sensor_setting_property_id); + net_buf_simple_add_mem(msg, act->sensor_setting_raw->data, act->sensor_setting_raw->len); + break; + } + case BLE_MESH_MODEL_OP_SENSOR_GET: { + struct bt_mesh_sensor_get *act; + act = (struct bt_mesh_sensor_get *)value; + if (act->op_en) { + net_buf_simple_add_le16(msg, act->property_id); + } + break; + } + case BLE_MESH_MODEL_OP_SENSOR_COLUMN_GET: { + struct bt_mesh_sensor_column_get *act; + act = (struct bt_mesh_sensor_column_get *)value; + net_buf_simple_add_le16(msg, act->property_id); + net_buf_simple_add_mem(msg, act->raw_value_x->data, act->raw_value_x->len); + break; + } + case BLE_MESH_MODEL_OP_SENSOR_SERIES_GET: { + struct bt_mesh_sensor_series_get *act; + act = (struct bt_mesh_sensor_series_get *)value; + net_buf_simple_add_le16(msg, act->property_id); + if (act->op_en) { + net_buf_simple_add_mem(msg, act->raw_value_x1->data, act->raw_value_x1->len); + net_buf_simple_add_mem(msg, act->raw_value_x2->data, act->raw_value_x2->len); + } + break; + } + default: + BT_ERR("%s, Not a Sensor Client message opcode", __func__); + err = -EINVAL; + goto end; + } + + err = bt_mesh_client_send_msg(common->model, common->opcode, &common->ctx, msg, + timeout_handler, common->msg_timeout, need_ack, + common->cb, common->cb_data); + if (err) { + BT_ERR("%s, Failed to send Sensor Client message (err %d)", __func__, err); + } + +end: + bt_mesh_free_buf(msg); + + return err; +} + +int bt_mesh_sensor_client_get_state(struct bt_mesh_common_param *common, void *get, void *status) +{ + bt_mesh_sensor_client_t *client = NULL; + u16_t length = 0; + + if (!common || !common->model || !get) { + BT_ERR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + client = (bt_mesh_sensor_client_t *)common->model->user_data; + if (!client || !client->internal_data) { + BT_ERR("%s, Sensor Client user data is NULL", __func__); + return -EINVAL; + } + + switch (common->opcode) { + case BLE_MESH_MODEL_OP_SENSOR_DESCRIPTOR_GET: + length = BLE_MESH_SENSOR_DESCRIPTOR_GET_MSG_LEN; + break; + case BLE_MESH_MODEL_OP_SENSOR_CADENCE_GET: + length = BLE_MESH_SENSOR_CADENCE_GET_MSG_LEN; + break; + case BLE_MESH_MODEL_OP_SENSOR_SETTINGS_GET: + length = BLE_MESH_SENSOR_SETTINGS_GET_MSG_LEN; + break; + case BLE_MESH_MODEL_OP_SENSOR_SETTING_GET: + length = BLE_MESH_SENSOR_SETTING_GET_MSG_LEN; + break; + case BLE_MESH_MODEL_OP_SENSOR_GET: + length = BLE_MESH_SENSOR_GET_MSG_LEN; + break; + case BLE_MESH_MODEL_OP_SENSOR_COLUMN_GET: { + struct bt_mesh_sensor_column_get *value; + value = (struct bt_mesh_sensor_column_get *)get; + if (!value->raw_value_x) { + BT_ERR("%s, Sensor column_get is NULL", __func__); + return -EINVAL; + } + length = (2 + 2 + value->raw_value_x->len + 4); + break; + } + case BLE_MESH_MODEL_OP_SENSOR_SERIES_GET: { + struct bt_mesh_sensor_series_get *value; + value = (struct bt_mesh_sensor_series_get *)get; + if (value->op_en) { + if (!value->raw_value_x1 || !value->raw_value_x2) { + BT_ERR("%s, Sensor series_get is NULL", __func__); + return -EINVAL; + } + } + if (value->op_en) { + length = value->raw_value_x1->len + value->raw_value_x2->len; + } + length += (2 + 2 + 4); + break; + } + default: + BT_ERR("%s, Not a Sensor Client Get message opcode", __func__); + return -EINVAL; + } + + return sensor_act_state(common, get, length, true); +} + +int bt_mesh_sensor_client_set_state(struct bt_mesh_common_param *common, void *set, void *status) +{ + bt_mesh_sensor_client_t *client = NULL; + u16_t length = 0; + bool need_ack = false; + + if (!common || !common->model || !set) { + BT_ERR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + client = (bt_mesh_sensor_client_t *)common->model->user_data; + if (!client || !client->internal_data) { + BT_ERR("%s, Sensor Client user data is NULL", __func__); + return -EINVAL; + } + + switch (common->opcode) { + case BLE_MESH_MODEL_OP_SENSOR_CADENCE_SET: + need_ack = true; + case BLE_MESH_MODEL_OP_SENSOR_CADENCE_SET_UNACK: { + struct bt_mesh_sensor_cadence_set *value; + value = (struct bt_mesh_sensor_cadence_set *)set; + if (!value->status_trigger_delta_down || !value->status_trigger_delta_up || + !value->fast_cadence_low || !value->fast_cadence_high) { + BT_ERR("%s, Sensor cadence_set is NULL", __func__); + return -EINVAL; + } + length = value->status_trigger_delta_down->len + \ + value->status_trigger_delta_up->len + \ + value->fast_cadence_low->len + \ + value->fast_cadence_high->len; + length += (1 + 2 + 1 + 1 + 4); + break; + } + case BLE_MESH_MODEL_OP_SENSOR_SETTING_SET: + need_ack = true; + case BLE_MESH_MODEL_OP_SENSOR_SETTING_SET_UNACK: { + struct bt_mesh_sensor_setting_set *value; + value = (struct bt_mesh_sensor_setting_set *)set; + if (!value->sensor_setting_raw) { + BT_ERR("%s, Sensor setting_raw is NULL", __func__); + return -EINVAL; + } + length = (1 + 2 + 2 + value->sensor_setting_raw->len + 4); + break; + } + default: + BT_ERR("%s, Not a Sensor Client Set message opcode", __func__); + return -EINVAL; + } + + return sensor_act_state(common, set, length, need_ack); +} + +int bt_mesh_sensor_cli_init(struct bt_mesh_model *model, bool primary) +{ + sensor_internal_data_t *internal = NULL; + bt_mesh_sensor_client_t *client = NULL; + + BT_DBG("primary %u", primary); + + if (!model) { + BT_ERR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + client = (bt_mesh_sensor_client_t *)model->user_data; + if (!client) { + BT_ERR("%s, Sensor Client user_data is NULL", __func__); + return -EINVAL; + } + + /* TODO: call osi_free() when deinit function is invoked*/ + internal = osi_calloc(sizeof(sensor_internal_data_t)); + if (!internal) { + BT_ERR("%s, Failed to allocate memory", __func__); + return -ENOMEM; + } + + sys_slist_init(&internal->queue); + + client->model = model; + client->op_pair_size = ARRAY_SIZE(sensor_op_pair); + client->op_pair = sensor_op_pair; + client->internal_data = internal; + + return 0; +} \ No newline at end of file diff --git a/components/bt/ble_mesh/mesh_models/time_scene_client.c b/components/bt/ble_mesh/mesh_models/time_scene_client.c new file mode 100644 index 0000000000..3296edf925 --- /dev/null +++ b/components/bt/ble_mesh/mesh_models/time_scene_client.c @@ -0,0 +1,694 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 +#include + +#include "osi/allocator.h" +#include "sdkconfig.h" + +#include "mesh_types.h" +#include "mesh_kernel.h" +#include "mesh_trace.h" + +#include "mesh.h" +#include "model_opcode.h" +#include "mesh_common.h" +#include "time_scene_client.h" + +#include "btc_ble_mesh_time_scene_model.h" + +/** The following are the macro definitions of time and client + * scene model messages length, and a message is composed of + * three parts: Opcode + msg_value + MIC + */ +/* Time client messages length */ +#define BLE_MESH_TIME_SET_MSG_LEN (1 + 10 + 4) +#define BLE_MESH_TIME_ZONE_SET_MSG_LEN (2 + 6 + 4) +#define BLE_MESH_TAI_UTC_DELTA_SET_MSG_LEN (2 + 7 + 4) +#define BLE_MESH_TIME_ROLE_SET_MSG_LEN (2 + 1 + 4) + +/* Scene client messages length */ +#define BLE_MESH_SCENE_STORE_MSG_LEN (2 + 2 + 4) +#define BLE_MESH_SCENE_RECALL_MSG_LEN (2 + 5 + 4) +#define BLE_MESH_SCENE_GET_MSG_LEN (2 + 0 + 4) +#define BLE_MESH_SCENE_REGISTER_GET_MSG_LEN (2 + 0 + 4) +#define BLE_MESH_SCENE_DELETE_MSG_LEN (2 + 2 + 4) + +/* Scheduler client messages length */ +#define BLE_MESH_SCHEDULER_ACT_GET_MSG_LEN (2 + 1 + 4) +#define BLE_MESH_SCHEDULER_ACT_SET_MSG_LEN (1 + 10 + 4) + +#define BLE_MESH_SCENE_GET_STATE_MSG_LEN (2 + 1 + 4) +#define BLE_MESH_SCENE_ACT_STATE_MSG_LEN (2 + 2 + 4) + +static const bt_mesh_client_op_pair_t time_scene_op_pair[] = { + { BLE_MESH_MODEL_OP_TIME_GET, BLE_MESH_MODEL_OP_TIME_STATUS }, + { BLE_MESH_MODEL_OP_TIME_SET, BLE_MESH_MODEL_OP_TIME_STATUS }, + { BLE_MESH_MODEL_OP_TIME_ZONE_GET, BLE_MESH_MODEL_OP_TIME_ZONE_STATUS }, + { BLE_MESH_MODEL_OP_TIME_ZONE_SET, BLE_MESH_MODEL_OP_TIME_ZONE_STATUS }, + { BLE_MESH_MODEL_OP_TAI_UTC_DELTA_GET, BLE_MESH_MODEL_OP_TAI_UTC_DELTA_STATUS }, + { BLE_MESH_MODEL_OP_TAI_UTC_DELTA_SET, BLE_MESH_MODEL_OP_TAI_UTC_DELTA_STATUS }, + { BLE_MESH_MODEL_OP_TIME_ROLE_GET, BLE_MESH_MODEL_OP_TIME_ROLE_STATUS }, + { BLE_MESH_MODEL_OP_TIME_ROLE_SET, BLE_MESH_MODEL_OP_TIME_ROLE_STATUS }, + { BLE_MESH_MODEL_OP_SCENE_STORE, BLE_MESH_MODEL_OP_SCENE_REGISTER_STATUS }, + { BLE_MESH_MODEL_OP_SCENE_RECALL, BLE_MESH_MODEL_OP_SCENE_STATUS }, + { BLE_MESH_MODEL_OP_SCENE_GET, BLE_MESH_MODEL_OP_SCENE_STATUS }, + { BLE_MESH_MODEL_OP_SCENE_REGISTER_GET, BLE_MESH_MODEL_OP_SCENE_REGISTER_STATUS }, + { BLE_MESH_MODEL_OP_SCENE_DELETE, BLE_MESH_MODEL_OP_SCENE_REGISTER_STATUS }, + { BLE_MESH_MODEL_OP_SCHEDULER_GET, BLE_MESH_MODEL_OP_SCHEDULER_STATUS }, + { BLE_MESH_MODEL_OP_SCHEDULER_ACT_GET, BLE_MESH_MODEL_OP_SCHEDULER_ACT_STATUS }, + { BLE_MESH_MODEL_OP_SCHEDULER_ACT_SET, BLE_MESH_MODEL_OP_SCHEDULER_ACT_STATUS }, +}; + +static void timeout_handler(struct k_work *work) +{ + time_scene_internal_data_t *internal = NULL; + bt_mesh_time_scene_client_t *client = NULL; + bt_mesh_client_node_t *node = NULL; + + BT_WARN("Receive time scene status message timeout"); + + node = CONTAINER_OF(work, bt_mesh_client_node_t, timer.work); + if (!node || !node->ctx.model) { + BT_ERR("%s, Invalid parameter", __func__); + return; + } + + client = (bt_mesh_time_scene_client_t *)node->ctx.model->user_data; + if (!client) { + BT_ERR("%s, Time Scene Client user_data is NULL", __func__); + return; + } + + internal = (time_scene_internal_data_t *)client->internal_data; + if (!internal) { + BT_ERR("%s, Time Scene Client internal_data is NULL", __func__); + return; + } + + bt_mesh_callback_time_scene_status_to_btc(node->opcode, 0x03, node->ctx.model, + &node->ctx, NULL, 0); + + bt_mesh_client_free_node(&internal->queue, node); + + return; +} + +static void time_scene_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + time_scene_internal_data_t *internal = NULL; + bt_mesh_time_scene_client_t *client = NULL; + bt_mesh_client_node_t *node = NULL; + u8_t *val = NULL; + u8_t evt = 0xFF; + u32_t rsp = 0; + size_t len = 0; + + BT_DBG("%s, len %d, bytes %s", __func__, buf->len, bt_hex(buf->data, buf->len)); + + client = (bt_mesh_time_scene_client_t *)model->user_data; + if (!client) { + BT_ERR("%s, Time Scene Client user_data is NULL", __func__); + return; + } + + internal = (time_scene_internal_data_t *)client->internal_data; + if (!internal) { + BT_ERR("%s, Time Scene Client internal_data is NULL", __func__); + return; + } + + rsp = ctx->recv_op; + switch (rsp) { + case BLE_MESH_MODEL_OP_TIME_STATUS: { + struct bt_mesh_time_status *status = NULL; + if (buf->len != 5 && buf->len != 10) { + BT_ERR("%s, Invalid Time Status length %d", __func__, buf->len); + return; + } + status = osi_calloc(sizeof(struct bt_mesh_time_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + memcpy(status->tai_seconds, buf->data, 5); + net_buf_simple_pull(buf, 5); + status->sub_second = net_buf_simple_pull_u8(buf); + status->uncertainty = net_buf_simple_pull_u8(buf); + u16_t temp = net_buf_simple_pull_le16(buf); + status->time_authority = temp & BIT(0); + status->tai_utc_delta = temp >> 15; + status->time_zone_offset = net_buf_simple_pull_u8(buf); + val = (u8_t *)status; + len = sizeof(struct bt_mesh_time_status); + break; + } + case BLE_MESH_MODEL_OP_TIME_ZONE_STATUS: { + struct bt_mesh_time_zone_status *status = NULL; + if (buf->len != 7) { + BT_ERR("%s, Invalid Time Zone Status length %d", __func__, buf->len); + return; + } + status = osi_calloc(sizeof(struct bt_mesh_time_zone_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->time_zone_offset_curr = net_buf_simple_pull_u8(buf); + status->time_zone_offset_new = net_buf_simple_pull_u8(buf); + memcpy(status->tai_zone_change, buf->data, 5); + net_buf_simple_pull(buf, 5); + val = (u8_t *)status; + len = sizeof(struct bt_mesh_time_zone_status); + break; + } + case BLE_MESH_MODEL_OP_TAI_UTC_DELTA_STATUS: { + struct bt_mesh_tai_utc_delta_status *status = NULL; + if (buf->len != 9) { + BT_ERR("%s, Invalid TAI UTC Delta Status length %d", __func__, buf->len); + return; + } + status = osi_calloc(sizeof(struct bt_mesh_tai_utc_delta_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + u16_t temp = net_buf_simple_pull_le16(buf); + status->tai_utc_delta_curr = temp & BIT_MASK(15); + status->padding_1 = (temp >> 15) & BIT(0); + temp = net_buf_simple_pull_le16(buf); + status->tai_utc_delta_new = temp & BIT_MASK(15); + status->padding_2 = (temp >> 15) & BIT(0); + memcpy(status->tai_delta_change, buf->data, 5); + net_buf_simple_pull(buf, 5); + val = (u8_t *)status; + len = sizeof(struct bt_mesh_tai_utc_delta_status); + break; + } + case BLE_MESH_MODEL_OP_TIME_ROLE_STATUS: { + struct bt_mesh_time_role_status *status = NULL; + if (buf->len != 1) { + BT_ERR("%s, Invalid Time Role Status length %d", __func__, buf->len); + return; + } + status = osi_calloc(sizeof(struct bt_mesh_time_role_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->time_role = net_buf_simple_pull_u8(buf); + val = (u8_t *)status; + len = sizeof(struct bt_mesh_time_role_status); + break; + } + case BLE_MESH_MODEL_OP_SCENE_STATUS: { + struct bt_mesh_scene_status *status = NULL; + if (buf->len != 3 && buf->len != 6) { + BT_ERR("%s, Invalid Scene Status length %d", __func__, buf->len); + return; + } + status = osi_calloc(sizeof(struct bt_mesh_scene_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->status_code = net_buf_simple_pull_u8(buf); + status->current_scene = net_buf_simple_pull_le16(buf); + if (buf->len) { + status->op_en = true; + status->target_scene = net_buf_simple_pull_le16(buf); + status->remain_time = net_buf_simple_pull_u8(buf); + } + val = (u8_t *)status; + len = sizeof(struct bt_mesh_scene_status); + break; + } + case BLE_MESH_MODEL_OP_SCENE_REGISTER_STATUS: { + struct bt_mesh_scene_register_status *status = NULL; + status = osi_calloc(sizeof(struct bt_mesh_scene_register_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->status_code = net_buf_simple_pull_u8(buf); + status->current_scene = net_buf_simple_pull_le16(buf); + status->scenes = bt_mesh_alloc_buf(buf->len); + if (!status->scenes) { + BT_ERR("%s, Failed to allocate memory", __func__); + osi_free(status); + return; + } + net_buf_simple_add_mem(status->scenes, buf->data, buf->len); + val = (u8_t *)status; + len = sizeof(struct bt_mesh_scene_register_status); + break; + } + case BLE_MESH_MODEL_OP_SCHEDULER_STATUS: { + struct bt_mesh_scheduler_status *status = NULL; + if (buf->len != 2) { + BT_ERR("%s, Invalid Scheduler Status length %d", __func__, buf->len); + return; + } + status = osi_calloc(sizeof(struct bt_mesh_scheduler_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + status->schedules = net_buf_simple_pull_le16(buf); + val = (u8_t *)status; + len = sizeof(struct bt_mesh_scheduler_status); + break; + } + case BLE_MESH_MODEL_OP_SCHEDULER_ACT_STATUS: { + struct bt_mesh_scheduler_act_status *status = NULL; + if (buf->len != 10) { + BT_ERR("%s, Invalid Scheduler Action Status length %d", __func__, buf->len); + return; + } + status = osi_calloc(sizeof(struct bt_mesh_scheduler_act_status)); + if (!status) { + BT_ERR("%s, Failed to allocate memory", __func__); + return; + } + memcpy(status, buf->data, offsetof(struct bt_mesh_scheduler_act_status, scene_number)); + net_buf_simple_pull(buf, offsetof(struct bt_mesh_scheduler_act_status, scene_number)); + status->scene_number = net_buf_simple_pull_le16(buf); + val = (u8_t *)status; + len = sizeof(struct bt_mesh_scheduler_act_status); + break; + } + default: + BT_ERR("%s, Not a Time Scene Status message opcode", __func__); + return; + } + + buf->data = val; + buf->len = len; + node = bt_mesh_is_model_message_publish(model, ctx, buf, true); + if (!node) { + BT_DBG("Unexpected time scene status message 0x%x", rsp); + } else { + switch (node->opcode) { + case BLE_MESH_MODEL_OP_TIME_GET: + case BLE_MESH_MODEL_OP_TIME_ZONE_GET: + case BLE_MESH_MODEL_OP_TAI_UTC_DELTA_GET: + case BLE_MESH_MODEL_OP_TIME_ROLE_GET: + case BLE_MESH_MODEL_OP_SCENE_GET: + case BLE_MESH_MODEL_OP_SCENE_REGISTER_GET: + case BLE_MESH_MODEL_OP_SCHEDULER_GET: + case BLE_MESH_MODEL_OP_SCHEDULER_ACT_GET: + evt = 0x00; + break; + case BLE_MESH_MODEL_OP_TIME_SET: + case BLE_MESH_MODEL_OP_TIME_ZONE_SET: + case BLE_MESH_MODEL_OP_TAI_UTC_DELTA_SET: + case BLE_MESH_MODEL_OP_TIME_ROLE_SET: + case BLE_MESH_MODEL_OP_SCENE_STORE: + case BLE_MESH_MODEL_OP_SCENE_RECALL: + case BLE_MESH_MODEL_OP_SCENE_DELETE: + case BLE_MESH_MODEL_OP_SCHEDULER_ACT_SET: + evt = 0x01; + break; + default: + break; + } + + bt_mesh_callback_time_scene_status_to_btc(node->opcode, evt, model, ctx, val, len); + // Don't forget to release the node at the end. + bt_mesh_client_free_node(&internal->queue, node); + } + + switch (rsp) { + case BLE_MESH_MODEL_OP_SCENE_REGISTER_STATUS: { + struct bt_mesh_scene_register_status *status; + status = (struct bt_mesh_scene_register_status *)val; + bt_mesh_free_buf(status->scenes); + break; + } + default: + break; + } + + osi_free(val); + + return; +} + +const struct bt_mesh_model_op time_cli_op[] = { + { BLE_MESH_MODEL_OP_TIME_STATUS, 5, time_scene_status }, + { BLE_MESH_MODEL_OP_TIME_ZONE_STATUS, 7, time_scene_status }, + { BLE_MESH_MODEL_OP_TAI_UTC_DELTA_STATUS, 9, time_scene_status }, + { BLE_MESH_MODEL_OP_TIME_ROLE_STATUS, 1, time_scene_status }, + BLE_MESH_MODEL_OP_END, +}; + +const struct bt_mesh_model_op scene_cli_op[] = { + { BLE_MESH_MODEL_OP_SCENE_STATUS, 3, time_scene_status }, + { BLE_MESH_MODEL_OP_SCENE_REGISTER_STATUS, 3, time_scene_status }, + BLE_MESH_MODEL_OP_END, +}; + +const struct bt_mesh_model_op scheduler_cli_op[] = { + { BLE_MESH_MODEL_OP_SCHEDULER_STATUS, 2, time_scene_status }, + { BLE_MESH_MODEL_OP_SCHEDULER_ACT_STATUS, 10, time_scene_status }, + BLE_MESH_MODEL_OP_END, +}; + +static int time_scene_get_state(struct bt_mesh_common_param *common, void *value) +{ + NET_BUF_SIMPLE_DEFINE(msg, BLE_MESH_SCENE_GET_STATE_MSG_LEN); + int err; + + bt_mesh_model_msg_init(&msg, common->opcode); + + if (value) { + switch (common->opcode) { + case BLE_MESH_MODEL_OP_SCHEDULER_ACT_GET: { + struct bt_mesh_scheduler_act_get *get; + get = (struct bt_mesh_scheduler_act_get *)value; + net_buf_simple_add_u8(&msg, get->index); + break; + } + default: + BT_DBG("This time scene message should be sent with NULL get pointer"); + break; + } + } + + err = bt_mesh_client_send_msg(common->model, common->opcode, &common->ctx, &msg, + timeout_handler, common->msg_timeout, true, + common->cb, common->cb_data); + if (err) { + BT_ERR("%s, Failed to send Time Scene Get message (err %d)", __func__, err); + } + + return err; +} + +static int time_scene_set_state(struct bt_mesh_common_param *common, + void *value, u16_t value_len, bool need_ack) +{ + struct net_buf_simple *msg = NULL; + int err; + + msg = bt_mesh_alloc_buf(value_len); + if (!msg) { + BT_ERR("%s, Failed to allocate memory", __func__); + return -ENOMEM; + } + + bt_mesh_model_msg_init(msg, common->opcode); + + switch (common->opcode) { + case BLE_MESH_MODEL_OP_TIME_SET: { + struct bt_mesh_time_set *set; + set = (struct bt_mesh_time_set *)value; + net_buf_simple_add_mem(msg, set->tai_seconds, 5); + net_buf_simple_add_u8(msg, set->sub_second); + net_buf_simple_add_u8(msg, set->uncertainty); + net_buf_simple_add_le16(msg, set->tai_utc_delta << 1 | set->time_authority); + net_buf_simple_add_u8(msg, set->time_zone_offset); + break; + } + case BLE_MESH_MODEL_OP_TIME_ZONE_SET: { + struct bt_mesh_time_zone_set *set; + set = (struct bt_mesh_time_zone_set *)value; + net_buf_simple_add_u8(msg, set->time_zone_offset_new); + net_buf_simple_add_mem(msg, set->tai_zone_change, 5); + break; + } + case BLE_MESH_MODEL_OP_TAI_UTC_DELTA_SET: { + struct bt_mesh_tai_utc_delta_set *set; + set = (struct bt_mesh_tai_utc_delta_set *)value; + net_buf_simple_add_le16(msg, set->padding << 15 | set->tai_utc_delta_new); + net_buf_simple_add_mem(msg, set->tai_delta_change, 5); + break; + } + case BLE_MESH_MODEL_OP_TIME_ROLE_SET: { + struct bt_mesh_time_role_set *set; + set = (struct bt_mesh_time_role_set *)value; + net_buf_simple_add_u8(msg, set->time_role); + break; + } + case BLE_MESH_MODEL_OP_SCENE_STORE: + case BLE_MESH_MODEL_OP_SCENE_STORE_UNACK: { + struct bt_mesh_scene_store *set; + set = (struct bt_mesh_scene_store *)value; + net_buf_simple_add_le16(msg, set->scene_number); + break; + } + case BLE_MESH_MODEL_OP_SCENE_RECALL: + case BLE_MESH_MODEL_OP_SCENE_RECALL_UNACK: { + struct bt_mesh_scene_recall *set; + set = (struct bt_mesh_scene_recall *)value; + net_buf_simple_add_le16(msg, set->scene_number); + net_buf_simple_add_u8(msg, set->tid); + if (set->op_en) { + net_buf_simple_add_u8(msg, set->trans_time); + net_buf_simple_add_u8(msg, set->delay); + } + break; + } + case BLE_MESH_MODEL_OP_SCENE_DELETE: + case BLE_MESH_MODEL_OP_SCENE_DELETE_UNACK: { + struct bt_mesh_scene_delete *set; + set = (struct bt_mesh_scene_delete *)value; + net_buf_simple_add_le16(msg, set->scene_number); + break; + } + case BLE_MESH_MODEL_OP_SCHEDULER_ACT_SET: + case BLE_MESH_MODEL_OP_SCHEDULER_ACT_SET_UNACK: { + struct bt_mesh_scheduler_act_set *set; + set = (struct bt_mesh_scheduler_act_set *)value; + net_buf_simple_add_mem(msg, set, offsetof(struct bt_mesh_scheduler_act_set, scene_number)); + net_buf_simple_add_le16(msg, set->scene_number); + break; + } + default: + BT_ERR("%s, Not a Time Scene Client set message opcode", __func__); + err = -EINVAL; + goto end; + } + + err = bt_mesh_client_send_msg(common->model, common->opcode, &common->ctx, msg, + timeout_handler, common->msg_timeout, need_ack, + common->cb, common->cb_data); + if (err) { + BT_ERR("%s, Failed to send Time Scene Set message (err %d)", __func__, err); + } + +end: + bt_mesh_free_buf(msg); + return err; +} + +int bt_mesh_time_scene_client_get_state(struct bt_mesh_common_param *common, void *get, void *status) +{ + bt_mesh_time_scene_client_t *client = NULL; + + if (!common || !common->model) { + BT_ERR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + client = (bt_mesh_time_scene_client_t *)common->model->user_data; + if (!client || !client->internal_data) { + BT_ERR("%s, Time Scene Client user data is NULL", __func__); + return -EINVAL; + } + + switch (common->opcode) { + case BLE_MESH_MODEL_OP_TIME_GET: + case BLE_MESH_MODEL_OP_TIME_ZONE_GET: + case BLE_MESH_MODEL_OP_TAI_UTC_DELTA_GET: + case BLE_MESH_MODEL_OP_TIME_ROLE_GET: + case BLE_MESH_MODEL_OP_SCENE_GET: + case BLE_MESH_MODEL_OP_SCENE_REGISTER_GET: + case BLE_MESH_MODEL_OP_SCHEDULER_GET: + break; + case BLE_MESH_MODEL_OP_SCHEDULER_ACT_GET: + if (!get) { + BT_ERR("%s, Scheduler action index is NULL", __func__); + return -EINVAL; + } + break; + default: + BT_ERR("%s, Not a Time Scene Client Get message opcode", __func__); + return -EINVAL; + } + + return time_scene_get_state(common, get); +} + +int bt_mesh_time_scene_client_set_state(struct bt_mesh_common_param *common, void *set, void *status) +{ + bt_mesh_time_scene_client_t *client = NULL; + u16_t length = 0; + bool need_ack = false; + + if (!common || !common->model || !set) { + BT_ERR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + client = (bt_mesh_time_scene_client_t *)common->model->user_data; + if (!client || !client->internal_data) { + BT_ERR("%s, Time Scene Client user data is NULL", __func__); + return -EINVAL; + } + + switch (common->opcode) { + case BLE_MESH_MODEL_OP_TIME_SET: + need_ack = true; + length = BLE_MESH_TIME_SET_MSG_LEN; + break; + case BLE_MESH_MODEL_OP_TIME_ZONE_SET: + need_ack = true; + length = BLE_MESH_TIME_ZONE_SET_MSG_LEN; + break; + case BLE_MESH_MODEL_OP_TAI_UTC_DELTA_SET: { + struct bt_mesh_tai_utc_delta_set *value; + value = (struct bt_mesh_tai_utc_delta_set *)set; + if (value->padding) { + BT_ERR("%s, Non-zero padding value is prohibited", __func__); + return -EINVAL; + } + need_ack = true; + length = BLE_MESH_TAI_UTC_DELTA_SET_MSG_LEN; + break; + } + case BLE_MESH_MODEL_OP_TIME_ROLE_SET: { + struct bt_mesh_time_role_set *value; + value = (struct bt_mesh_time_role_set *)set; + if (value->time_role > 0x03) { + BT_ERR("%s, Time role value is prohibited", __func__); + return -EINVAL; + } + need_ack = true; + length = BLE_MESH_TIME_ROLE_SET_MSG_LEN; + break; + } + case BLE_MESH_MODEL_OP_SCENE_STORE: + need_ack = true; + case BLE_MESH_MODEL_OP_SCENE_STORE_UNACK: { + struct bt_mesh_scene_store *value; + value = (struct bt_mesh_scene_store *)set; + if (!value->scene_number) { + BT_ERR("%s, Scene store scene_number 0x0000 is prohibited", __func__); + return -EINVAL; + } + length = BLE_MESH_SCENE_STORE_MSG_LEN; + break; + } + case BLE_MESH_MODEL_OP_SCENE_RECALL: + need_ack = true; + case BLE_MESH_MODEL_OP_SCENE_RECALL_UNACK: { + struct bt_mesh_scene_recall *value; + value = (struct bt_mesh_scene_recall *)set; + if (!value->scene_number) { + BT_ERR("%s, Scene recall scene_number 0x0000 is prohibited", __func__); + return -EINVAL; + } + if (value->op_en) { + if ((value->trans_time & 0x3F) > 0x3E) { + BT_ERR("%s, Invalid Scene Recall transition time", __func__); + return -EINVAL; + } + } + length = BLE_MESH_SCENE_RECALL_MSG_LEN; + break; + } + case BLE_MESH_MODEL_OP_SCENE_DELETE: + need_ack = true; + case BLE_MESH_MODEL_OP_SCENE_DELETE_UNACK: { + length = BLE_MESH_SCENE_DELETE_MSG_LEN; + break; + } + case BLE_MESH_MODEL_OP_SCHEDULER_ACT_SET: + need_ack = true; + case BLE_MESH_MODEL_OP_SCHEDULER_ACT_SET_UNACK: { + struct bt_mesh_scheduler_act_set *value; + value = (struct bt_mesh_scheduler_act_set *)set; + if (value->year > 0x64) { + BT_ERR("%s, Scheduler register year value is prohibited", __func__); + return -EINVAL; + } + if (value->hour > 0x19) { + BT_ERR("%s, Scheduler register hour value is prohibited", __func__); + return -EINVAL; + } + length = BLE_MESH_SCHEDULER_ACT_SET_MSG_LEN; + break; + } + default: + BT_ERR("%s, Not a Time Scene Set message opcode", __func__); + return -EINVAL; + } + + return time_scene_set_state(common, set, length, need_ack); +} + +static int time_scene_client_init(struct bt_mesh_model *model, bool primary) +{ + time_scene_internal_data_t *internal = NULL; + bt_mesh_time_scene_client_t *client = NULL; + + BT_DBG("primary %u", primary); + + if (!model) { + BT_ERR("%s, Invalid parameter", __func__); + return -EINVAL; + } + + client = (bt_mesh_time_scene_client_t *)model->user_data; + if (!client) { + BT_ERR("%s, Time Scene Client user_data is NULL", __func__); + return -EINVAL; + } + + /* TODO: call osi_free() when deinit function is invoked*/ + internal = osi_calloc(sizeof(time_scene_internal_data_t)); + if (!internal) { + BT_ERR("%s, Failed to allocate memory", __func__); + return -ENOMEM; + } + + sys_slist_init(&internal->queue); + + client->model = model; + client->op_pair_size = ARRAY_SIZE(time_scene_op_pair); + client->op_pair = time_scene_op_pair; + client->internal_data = internal; + + return 0; +} + +int bt_mesh_time_cli_init(struct bt_mesh_model *model, bool primary) +{ + return time_scene_client_init(model, primary); +} + +int bt_mesh_scene_cli_init(struct bt_mesh_model *model, bool primary) +{ + return time_scene_client_init(model, primary); +} + +int bt_mesh_scheduler_cli_init(struct bt_mesh_model *model, bool primary) +{ + return time_scene_client_init(model, primary); +} \ No newline at end of file diff --git a/components/bt/bluedroid/btc/core/btc_task.c b/components/bt/bluedroid/btc/core/btc_task.c index df1e1ddfdc..edfc4a63f1 100644 --- a/components/bt/bluedroid/btc/core/btc_task.c +++ b/components/bt/bluedroid/btc/core/btc_task.c @@ -49,6 +49,15 @@ #include "btc_hf_client.h" #endif /* #if BTC_HF_CLIENT_INCLUDED */ #endif /* #if CLASSIC_BT_INCLUDED */ +#if CONFIG_BLE_MESH +#include "btc_ble_mesh_prov.h" +#include "btc_ble_mesh_health_model.h" +#include "btc_ble_mesh_config_model.h" +#include "btc_ble_mesh_generic_model.h" +#include "btc_ble_mesh_lighting_model.h" +#include "btc_ble_mesh_sensor_model.h" +#include "btc_ble_mesh_time_scene_model.h" +#endif /* #if CONFIG_BLE_MESH */ #define BTC_TASK_PINNED_TO_CORE (TASK_PINNED_TO_CORE) #define BTC_TASK_STACK_SIZE (BT_BTC_TASK_STACK_SIZE + BT_TASK_EXTRA_STACK_SIZE) //by menuconfig @@ -98,6 +107,18 @@ static const btc_func_t profile_tab[BTC_PID_NUM] = { [BTC_PID_HF_CLIENT] = {btc_hf_client_call_handler, btc_hf_client_cb_handler}, #endif /* #if BTC_HF_CLIENT_INCLUDED */ #endif /* #if CLASSIC_BT_INCLUDED */ +#if CONFIG_BLE_MESH + [BTC_PID_PROV] = {btc_mesh_prov_call_handler, btc_mesh_prov_cb_handler}, + [BTC_PID_MODEL] = {btc_mesh_model_call_handler, btc_mesh_model_cb_handler}, + [BTC_PID_HEALTH_CLIENT] = {btc_mesh_health_client_call_handler, btc_mesh_health_client_cb_handler}, + [BTC_PID_HEALTH_SERVER] = {btc_mesh_health_server_call_handler, btc_mesh_health_server_cb_handler}, + [BTC_PID_CFG_CLIENT] = {btc_mesh_cfg_client_call_handler, btc_mesh_cfg_client_cb_handler}, + [BTC_PID_CFG_SERVER] = {NULL , btc_mesh_cfg_server_cb_handler}, + [BTC_PID_GENERIC_CLIENT] = {btc_mesh_generic_client_call_handler, btc_mesh_generic_client_cb_handler}, + [BTC_PID_LIGHT_CLIENT] = {btc_mesh_light_client_call_handler, btc_mesh_light_client_cb_handler}, + [BTC_PID_SENSOR_CLIENT] = {btc_mesh_sensor_client_call_handler, btc_mesh_sensor_client_cb_handler}, + [BTC_PID_TIME_SCENE_CLIENT] = {btc_mesh_time_scene_client_call_handler, btc_mesh_time_scene_client_cb_handler}, +#endif /* #if CONFIG_BLE_MESH */ }; /***************************************************************************** diff --git a/components/bt/bluedroid/btc/include/btc/btc_task.h b/components/bt/bluedroid/btc/include/btc/btc_task.h index 0ed02d911a..7c5ec80072 100644 --- a/components/bt/bluedroid/btc/include/btc/btc_task.h +++ b/components/bt/bluedroid/btc/include/btc/btc_task.h @@ -64,6 +64,18 @@ typedef enum { BTC_PID_HF_CLIENT, #endif /* BTC_HF_CLIENT_INCLUDED */ #endif /* CLASSIC_BT_INCLUDED */ +#if CONFIG_BLE_MESH + BTC_PID_PROV, + BTC_PID_MODEL, + BTC_PID_HEALTH_CLIENT, + BTC_PID_HEALTH_SERVER, + BTC_PID_CFG_CLIENT, + BTC_PID_CFG_SERVER, + BTC_PID_GENERIC_CLIENT, + BTC_PID_LIGHT_CLIENT, + BTC_PID_SENSOR_CLIENT, + BTC_PID_TIME_SCENE_CLIENT, +#endif /* CONFIG_BLE_MESH */ BTC_PID_NUM, } btc_pid_t; //btc profile id diff --git a/components/bt/component.mk b/components/bt/component.mk index b77e8f6640..529da89139 100644 --- a/components/bt/component.mk +++ b/components/bt/component.mk @@ -72,7 +72,19 @@ COMPONENT_PRIV_INCLUDEDIRS += bluedroid/bta/include \ bluedroid/utils/include \ bluedroid/common/include -COMPONENT_ADD_INCLUDEDIRS += bluedroid/api/include/api +COMPONENT_ADD_INCLUDEDIRS += bluedroid/api/include/api \ + bluedroid/osi/include \ + +ifdef CONFIG_BLE_MESH + COMPONENT_ADD_INCLUDEDIRS += ble_mesh/mesh_core \ + ble_mesh/mesh_core/include \ + ble_mesh/mesh_core/settings \ + ble_mesh/btc/include \ + ble_mesh/mesh_models/include \ + ble_mesh/api/core/include \ + ble_mesh/api/models/include \ + ble_mesh/api +endif COMPONENT_SRCDIRS += bluedroid/bta/dm \ bluedroid/bta/gatt \ @@ -122,6 +134,15 @@ COMPONENT_SRCDIRS += bluedroid/bta/dm \ bluedroid/api \ bluedroid +ifdef CONFIG_BLE_MESH + COMPONENT_SRCDIRS += ble_mesh/mesh_core \ + ble_mesh/mesh_core/settings \ + ble_mesh/btc \ + ble_mesh/mesh_models \ + ble_mesh/api/core \ + ble_mesh/api/models +endif + ifeq ($(GCC_NOT_5_2_0), 1) bluedroid/bta/sdp/bta_sdp_act.o: CFLAGS += -Wno-unused-const-variable bluedroid/btc/core/btc_config.o: CFLAGS += -Wno-unused-const-variable diff --git a/docs/en/COPYRIGHT.rst b/docs/en/COPYRIGHT.rst index e9aa5a4ed8..cb4272af64 100644 --- a/docs/en/COPYRIGHT.rst +++ b/docs/en/COPYRIGHT.rst @@ -56,6 +56,7 @@ These third party libraries can be included into the application (firmware) prod * :component:`Asio `, Copyright (c) 2003-2018 Christopher M. Kohlhoff is licensed under the Boost Software License. * :component:`ESP-MQTT ` MQTT Package (contiki-mqtt) - Copyright (c) 2014, Stephen Robinson, MQTT-ESP - Tuan PM is licensed under Apache License 2.0. +* :component:`BLE Mesh ` is adapted from Zephyr Project, Copyright (c) 2017-2018 Intel Corporation and licensed under Apache License 2.0 Build Tools ----------- @@ -158,3 +159,4 @@ Copyright (C) 2011, ChaN, all right reserved. .. _spiffs: https://github.com/pellepl/spiffs .. _asio: https://github.com/chriskohlhoff/asio .. _mqtt: https://github.com/espressif/esp-mqtt +.. _zephyr: https://github.com/zephyrproject-rtos/zephyr diff --git a/examples/bluetooth/ble_mesh/ble_mesh_client_model/CMakeLists.txt b/examples/bluetooth/ble_mesh/ble_mesh_client_model/CMakeLists.txt new file mode 100644 index 0000000000..f384288f31 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_client_model/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(ble_mesh_client_model) diff --git a/examples/bluetooth/ble_mesh/ble_mesh_client_model/Makefile b/examples/bluetooth/ble_mesh/ble_mesh_client_model/Makefile new file mode 100644 index 0000000000..384ad8ed5e --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_client_model/Makefile @@ -0,0 +1,10 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := ble_mesh_client_model + +COMPONENT_ADD_INCLUDEDIRS := components/include + +include $(IDF_PATH)/make/project.mk diff --git a/examples/bluetooth/ble_mesh/ble_mesh_client_model/README.md b/examples/bluetooth/ble_mesh/ble_mesh_client_model/README.md new file mode 100644 index 0000000000..38aa2caee1 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_client_model/README.md @@ -0,0 +1,14 @@ +ESP BLE Mesh Client Model Demo +======================== + +This demo shows how to use the Generic OnOff Client Model to get/set the generic on/off state. The basic procedures are as follows: + +1. Download and run this demo. +2. Use any app for BLE Mesh to provision this device as well as the device running the Generic OnOff Server demo. +3. After both onoff client and server devices are provisioned, use UART1 to input the unicast address of the element within the server device. +4. The Generic OnOff Client will start to get and set Generic OnOff states periodically. + +>**Notes:** +> +>1. The NetKey index and AppKey index are fixed to 0x0000 in this demo. +>2. If the client device is re-provisioned, but the server device is not, the first few get/set messages from the client will be treated as replay attacks. To avoid this, both devices should be re-provisioned prior to transmitting messages. \ No newline at end of file diff --git a/examples/bluetooth/ble_mesh/ble_mesh_client_model/main/CMakeLists.txt b/examples/bluetooth/ble_mesh/ble_mesh_client_model/main/CMakeLists.txt new file mode 100644 index 0000000000..1eb2d87ed2 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_client_model/main/CMakeLists.txt @@ -0,0 +1,6 @@ +set(COMPONENT_SRCS "ble_mesh_demo_main.c" + "board.c") + +set(COMPONENT_ADD_INCLUDEDIRS ".") + +register_component() diff --git a/examples/bluetooth/ble_mesh/ble_mesh_client_model/main/Kconfig.projbuild b/examples/bluetooth/ble_mesh/ble_mesh_client_model/main/Kconfig.projbuild new file mode 100644 index 0000000000..3bde11ff6d --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_client_model/main/Kconfig.projbuild @@ -0,0 +1,22 @@ +menu "Example Configuration" + + choice BLE_MESH_EXAMPLE_BOARD + prompt "Board selection for BLE Mesh" + default BLE_MESH_ESP_WROOM_32 + help + Select this option to choose the board for BLE Mesh. The default is ESP32-WROOM-32 + + config BLE_MESH_ESP_WROOM_32 + bool "ESP32-WROOM-32" + + config BLE_MESH_ESP_WROVER + bool "ESP32-WROVER" + endchoice + + config BLE_MESH_PATCH_FOR_SLAB_APP_1_1_0 + bool "Fix bug of Silicon Lab Android App v1.1.0 when reconnection will cause sequence number to recount from 0" + default y + help + It is an ad hoc solution and needs further modifications. + +endmenu diff --git a/examples/bluetooth/ble_mesh/ble_mesh_client_model/main/ble_mesh_demo_main.c b/examples/bluetooth/ble_mesh/ble_mesh_client_model/main/ble_mesh_demo_main.c new file mode 100644 index 0000000000..3dde33207d --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_client_model/main/ble_mesh_demo_main.c @@ -0,0 +1,532 @@ +/* main.c - Application main entry point */ + +/* + * Copyright (c) 2017 Intel Corporation + * Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include "esp_log.h" +#include "nvs_flash.h" + +#include "esp_bt.h" +#include "esp_bt_main.h" +#include "esp_bt_device.h" +#include "esp_ble_mesh_common_api.h" +#include "esp_ble_mesh_provisioning_api.h" +#include "esp_ble_mesh_networking_api.h" +#include "esp_ble_mesh_config_model_api.h" +#include "esp_ble_mesh_generic_model_api.h" + +#include "board.h" + +#define TAG "ble_mesh_client" + +#define CID_ESP 0x02E5 + +#define MSG_SEND_TTL 3 +#define MSG_SEND_REL false +#define MSG_TIMEOUT 0 /* 0 indicates that timeout value from menuconfig will be used */ +#define MSG_ROLE ROLE_NODE + +extern struct _led_state led_state[3]; + +static uint8_t dev_uuid[16] = { 0xdd, 0xdd }; +static uint16_t node_net_idx = ESP_BLE_MESH_KEY_UNUSED; +static uint16_t node_app_idx = ESP_BLE_MESH_KEY_UNUSED; +static uint8_t remote_onoff = LED_OFF; + +/* The remote node address shall be input through UART1, see board.c */ +uint16_t remote_addr = ESP_BLE_MESH_ADDR_UNASSIGNED; + +static esp_ble_mesh_client_t onoff_client; + +static esp_ble_mesh_cfg_srv_t config_server = { + .relay = ESP_BLE_MESH_RELAY_DISABLED, + .beacon = ESP_BLE_MESH_BEACON_ENABLED, +#if defined(CONFIG_BLE_MESH_FRIEND) + .friend_state = ESP_BLE_MESH_FRIEND_ENABLED, +#else + .friend_state = ESP_BLE_MESH_FRIEND_NOT_SUPPORTED, +#endif +#if defined(CONFIG_BLE_MESH_GATT_PROXY) + .gatt_proxy = ESP_BLE_MESH_GATT_PROXY_ENABLED, +#else + .gatt_proxy = ESP_BLE_MESH_GATT_PROXY_NOT_SUPPORTED, +#endif + .default_ttl = 7, + + /* 3 transmissions with 20ms interval */ + .net_transmit = ESP_BLE_MESH_TRANSMIT(2, 20), + .relay_retransmit = ESP_BLE_MESH_TRANSMIT(2, 20), +}; + +ESP_BLE_MESH_MODEL_PUB_DEFINE(onoff_srv_pub, 2 + 1, MSG_ROLE); +ESP_BLE_MESH_MODEL_PUB_DEFINE(onoff_cli_pub, 2 + 1, MSG_ROLE); + +static esp_ble_mesh_model_op_t onoff_op[] = { + ESP_BLE_MESH_MODEL_OP(ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_GET, 0), + ESP_BLE_MESH_MODEL_OP(ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET, 2), + ESP_BLE_MESH_MODEL_OP(ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK, 2), + /* Each model operation struct array must use this terminator + * as the end tag of the operation uint. */ + ESP_BLE_MESH_MODEL_OP_END, +}; + +static esp_ble_mesh_model_t root_models[] = { + ESP_BLE_MESH_MODEL_CFG_SRV(&config_server), + ESP_BLE_MESH_SIG_MODEL(ESP_BLE_MESH_MODEL_ID_GEN_ONOFF_SRV, onoff_op, + &onoff_srv_pub, &led_state[0]), + ESP_BLE_MESH_MODEL_GEN_ONOFF_CLI(&onoff_cli_pub, &onoff_client), +}; + +static esp_ble_mesh_elem_t elements[] = { + ESP_BLE_MESH_ELEMENT(0, root_models, ESP_BLE_MESH_MODEL_NONE), +}; + +static esp_ble_mesh_comp_t composition = { + .cid = CID_ESP, + .elements = elements, + .element_count = ARRAY_SIZE(elements), +}; + +/* Disable OOB security for SILabs Android app */ +static esp_ble_mesh_prov_t provision = { + .uuid = dev_uuid, +#if 0 + .output_size = 4, + .output_actions = ESP_BLE_MESH_DISPLAY_NUMBER, + .input_actions = ESP_BLE_MESH_PUSH, + .input_size = 4, +#else + .output_size = 0, + .output_actions = 0, +#endif +}; + +static int output_number(esp_ble_mesh_output_action_t action, uint32_t number) +{ + board_output_number(action, number); + return 0; +} + +static void prov_complete(uint16_t net_idx, uint16_t addr, uint8_t flags, uint32_t iv_index) +{ + ESP_LOGI(TAG, "net_idx: 0x%04x, addr: 0x%04x", net_idx, addr); + ESP_LOGI(TAG, "flags: 0x%02x, iv_index: 0x%08x", flags, iv_index); + board_prov_complete(); + node_net_idx = net_idx; +} + +static void gen_onoff_get_handler(esp_ble_mesh_model_t *model, + esp_ble_mesh_msg_ctx_t *ctx, + uint16_t length, uint8_t *data) +{ + struct _led_state *led = (struct _led_state *)model->user_data; + uint8_t send_data; + esp_err_t err; + + ESP_LOGI(TAG, "%s, addr 0x%04x onoff 0x%02x", __func__, model->element->element_addr, led->current); + + send_data = led->current; + /* Send Generic OnOff Status as a response to Generic OnOff Get */ + err = esp_ble_mesh_server_model_send_msg(model, ctx, ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_STATUS, + sizeof(send_data), &send_data); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Generic OnOff Status failed", __func__); + return; + } +} + +static void gen_onoff_set_unack_handler(esp_ble_mesh_model_t *model, + esp_ble_mesh_msg_ctx_t *ctx, + uint16_t length, uint8_t *data) +{ + struct _led_state *led = (struct _led_state *)model->user_data; + uint8_t prev_onoff; + esp_err_t err; + + ESP_LOGI(TAG, "%s, addr 0x%02x onoff 0x%02x", __func__, model->element->element_addr, led->current); + + prev_onoff = led->previous; + led->current = data[0]; + remote_onoff = led->current; + + board_led_operation(led->pin, led->current); + + /* If Generic OnOff state is changed, and the publish address of Generic OnOff Server + * model is valid, Generic OnOff Status will be published. + */ + if (prev_onoff != led->current && model->pub->publish_addr != ESP_BLE_MESH_ADDR_UNASSIGNED) { + ESP_LOGI(TAG, "Publish previous 0x%02x current 0x%02x", prev_onoff, led->current); + err = esp_ble_mesh_model_publish(model, ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_STATUS, + sizeof(led->current), &led->current, ROLE_NODE); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Publish Generic OnOff Status failed", __func__); + return; + } + } +} + +static void gen_onoff_set_handler(esp_ble_mesh_model_t *model, + esp_ble_mesh_msg_ctx_t *ctx, + uint16_t length, uint8_t *data) +{ + struct net_buf_simple *msg = model->pub->msg; + struct _led_state *led = (struct _led_state *)model->user_data; + uint8_t prev_onoff, send_data; + esp_err_t err; + + ESP_LOGI(TAG, "%s, addr 0x%02x onoff 0x%02x", __func__, model->element->element_addr, led->current); + + prev_onoff = led->previous; + led->current = data[0]; + remote_onoff = led->current; + + board_led_operation(led->pin, led->current); + + send_data = led->current; + /* Send Generic OnOff Status as a response to Generic OnOff Get */ + err = esp_ble_mesh_server_model_send_msg(model, ctx, ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_STATUS, + sizeof(send_data), &send_data); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Generic OnOff Status failed", __func__); + return; + } + + /* If Generic OnOff state is changed, and the publish address of Generic OnOff Server + * model is valid, Generic OnOff Status will be published. + */ + if (prev_onoff != led->current && model->pub->publish_addr != ESP_BLE_MESH_ADDR_UNASSIGNED) { + ESP_LOGI(TAG, "Publish previous 0x%02x current 0x%02x", prev_onoff, led->current); + bt_mesh_model_msg_init(msg, ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_STATUS); + err = esp_ble_mesh_model_publish(model, ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_STATUS, + sizeof(send_data), &send_data, ROLE_NODE); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Publish Generic OnOff Status failed", __func__); + return; + } + } +} + +static char *esp_ble_mesh_prov_event_to_str(esp_ble_mesh_prov_cb_event_t event) +{ + switch (event) { + case ESP_BLE_MESH_PROV_REGISTER_COMP_EVT: + return "ESP_BLE_MESH_PROV_REGISTER_COMP_EVT"; + case ESP_BLE_MESH_NODE_PROV_ENABLE_COMP_EVT: + return "ESP_BLE_MESH_NODE_PROV_ENABLE_COMP_EVT"; + case ESP_BLE_MESH_NODE_PROV_LINK_OPEN_EVT: + return "ESP_BLE_MESH_NODE_PROV_LINK_OPEN_EVT"; + case ESP_BLE_MESH_NODE_PROV_LINK_CLOSE_EVT: + return "ESP_BLE_MESH_NODE_PROV_LINK_CLOSE_EVT"; + case ESP_BLE_MESH_NODE_PROV_OUTPUT_NUMBER_EVT: + return "ESP_BLE_MESH_NODE_PROV_OUTPUT_NUMBER_EVT"; + case ESP_BLE_MESH_NODE_PROV_OUTPUT_STRING_EVT: + return "ESP_BLE_MESH_NODE_PROV_OUTPUT_STRING_EVT"; + case ESP_BLE_MESH_NODE_PROV_INPUT_EVT: + return "ESP_BLE_MESH_NODE_PROV_INPUT_EVT"; + case ESP_BLE_MESH_NODE_PROV_COMPLETE_EVT: + return "ESP_BLE_MESH_NODE_PROV_COMPLETE_EVT"; + case ESP_BLE_MESH_NODE_PROV_RESET_EVT: + return "ESP_BLE_MESH_NODE_PROV_RESET_EVT"; + default: + return "Invalid BLE Mesh provision event"; + } + + return NULL; +} + +static void esp_ble_mesh_prov_cb(esp_ble_mesh_prov_cb_event_t event, + esp_ble_mesh_prov_cb_param_t *param) +{ + ESP_LOGI(TAG, "%s, event = %s", __func__, esp_ble_mesh_prov_event_to_str(event)); + + switch (event) { + case ESP_BLE_MESH_PROV_REGISTER_COMP_EVT: + ESP_LOGI(TAG, "ESP_BLE_MESH_PROV_REGISTER_COMP_EVT, err_code %d", param->prov_register_comp.err_code); + break; + case ESP_BLE_MESH_NODE_PROV_ENABLE_COMP_EVT: + ESP_LOGI(TAG, "ESP_BLE_MESH_NODE_PROV_ENABLE_COMP_EVT, err_code %d", param->node_prov_enable_comp.err_code); + break; + case ESP_BLE_MESH_NODE_PROV_LINK_OPEN_EVT: + ESP_LOGI(TAG, "ESP_BLE_MESH_NODE_PROV_LINK_OPEN_EVT, bearer %s", + param->node_prov_link_open.bearer == ESP_BLE_MESH_PROV_ADV ? "PB-ADV" : "PB-GATT"); + break; + case ESP_BLE_MESH_NODE_PROV_LINK_CLOSE_EVT: + ESP_LOGI(TAG, "ESP_BLE_MESH_NODE_PROV_LINK_CLOSE_EVT, bearer %s", + param->node_prov_link_close.bearer == ESP_BLE_MESH_PROV_ADV ? "PB-ADV" : "PB-GATT"); + break; + case ESP_BLE_MESH_NODE_PROV_OUTPUT_NUMBER_EVT: + output_number(param->node_prov_output_num.action, param->node_prov_output_num.number); + break; + case ESP_BLE_MESH_NODE_PROV_OUTPUT_STRING_EVT: + break; + case ESP_BLE_MESH_NODE_PROV_INPUT_EVT: + break; + case ESP_BLE_MESH_NODE_PROV_COMPLETE_EVT: + prov_complete(param->node_prov_complete.net_idx, param->node_prov_complete.addr, + param->node_prov_complete.flags, param->node_prov_complete.iv_index); + break; + case ESP_BLE_MESH_NODE_PROV_RESET_EVT: + break; + case ESP_BLE_MESH_NODE_SET_UNPROV_DEV_NAME_COMP_EVT: + ESP_LOGI(TAG, "ESP_BLE_MESH_NODE_SET_UNPROV_DEV_NAME_COMP_EVT, err_code %d", param->node_set_unprov_dev_name_comp.err_code); + break; + default: + break; + } + + return; +} + +static esp_err_t esp_ble_mesh_set_msg_common(esp_ble_mesh_client_common_param_t *common, + esp_ble_mesh_generic_client_set_state_t *set, + esp_ble_mesh_model_t *model, uint32_t opcode, + uint8_t onoff) +{ + if (!common || !set || !model) { + return ESP_ERR_INVALID_ARG; + } + + common->opcode = opcode; + common->model = model; + common->ctx.net_idx = node_net_idx; + common->ctx.app_idx = node_app_idx; + common->ctx.addr = remote_addr; + common->ctx.send_ttl = MSG_SEND_TTL; + common->ctx.send_rel = MSG_SEND_REL; + common->msg_timeout = MSG_TIMEOUT; + common->msg_role = MSG_ROLE; + set->onoff_set.op_en = false; + set->onoff_set.onoff = onoff; + set->onoff_set.tid = 0x0; + + return ESP_OK; +} + + +static void esp_ble_mesh_model_cb(esp_ble_mesh_model_cb_event_t event, + esp_ble_mesh_model_cb_param_t *param) +{ + esp_ble_mesh_client_common_param_t common = {0}; + int err; + + switch (event) { + case ESP_BLE_MESH_MODEL_OPERATION_EVT: { + if (!param->model_operation.model || !param->model_operation.model->op || !param->model_operation.ctx) { + ESP_LOGE(TAG, "ESP_BLE_MESH_MODEL_OPERATION_EVT parameter is NULL"); + return; + } + if (node_app_idx == ESP_BLE_MESH_KEY_UNUSED) { + /* Generic OnOff Server/Client Models need to bind with the same app key */ + node_app_idx = param->model_operation.ctx->app_idx; + } + switch (param->model_operation.opcode) { + case ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_GET: + gen_onoff_get_handler(param->model_operation.model, param->model_operation.ctx, + param->model_operation.length, param->model_operation.msg); + break; + case ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET: { + esp_ble_mesh_generic_client_set_state_t set_state = {0}; + gen_onoff_set_handler(param->model_operation.model, param->model_operation.ctx, + param->model_operation.length, param->model_operation.msg); + /* This node has a Generic OnOff Client and Server Model. + * When Generic OnOff Server Model receives a Generic OnOff Set message, after + * this message is been handled, the Generic OnOff Client Model will send the + * Generic OnOff Set message to another node(contains Generic OnOff Server Model) + * identified by the remote_addr. + */ + esp_ble_mesh_set_msg_common(&common, &set_state, onoff_client.model, + ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET, remote_onoff); + err = esp_ble_mesh_generic_client_set_state(&common, &set_state); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Generic OnOff Set failed", __func__); + return; + } + break; + } + case ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK: { + esp_ble_mesh_generic_client_set_state_t set_state = {0}; + gen_onoff_set_unack_handler(param->model_operation.model, param->model_operation.ctx, + param->model_operation.length, param->model_operation.msg); + /* This node has a Generic OnOff Client and Server Model. + * When Generic OnOff Client Model receives a Generic OnOff Set Unack message, + * after this message is been handled, the Generic OnOff Client Model will send + * the Generic OnOff Set Unack message to another node(contains Generic OnOff + * Server Model) identified by the remote_addr. + */ + esp_ble_mesh_set_msg_common(&common, &set_state, onoff_client.model, + ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK, remote_onoff); + err = esp_ble_mesh_generic_client_set_state(&common, &set_state); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Generic OnOff Set Unack failed", __func__); + return; + } + break; + } + default: + break; + } + break; + } + case ESP_BLE_MESH_MODEL_SEND_COMP_EVT: + break; + case ESP_BLE_MESH_MODEL_PUBLISH_COMP_EVT: + break; + default: + break; + } + return; +} + +static void esp_ble_mesh_generic_cb(esp_ble_mesh_generic_client_cb_event_t event, + esp_ble_mesh_generic_client_cb_param_t *param) +{ + uint32_t opcode; + int err; + + ESP_LOGI(TAG, "%s: event is %d, error code is %d, opcode is 0x%x", + __func__, event, param->error_code, param->params->opcode); + + opcode = param->params->opcode; + + switch (event) { + case ESP_BLE_MESH_GENERIC_CLIENT_GET_STATE_EVT: + ESP_LOGI(TAG, "ESP_BLE_MESH_GENERIC_CLIENT_GET_STATE_EVT"); + switch (opcode) { + case ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_GET: + ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_GET: onoff %d", param->status_cb.onoff_status.present_onoff); + break; + default: + break; + } + break; + case ESP_BLE_MESH_GENERIC_CLIENT_SET_STATE_EVT: + ESP_LOGI(TAG, "ESP_BLE_MESH_GENERIC_CLIENT_SET_STATE_EVT"); + switch (opcode) { + case ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET: + ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET: onoff %d", param->status_cb.onoff_status.present_onoff); + break; + default: + break; + } + break; + case ESP_BLE_MESH_GENERIC_CLIENT_PUBLISH_EVT: + ESP_LOGI(TAG, "ESP_BLE_MESH_GENERIC_CLIENT_PUBLISH_EVT"); + break; + case ESP_BLE_MESH_GENERIC_CLIENT_TIMEOUT_EVT: { + ESP_LOGI(TAG, "ESP_BLE_MESH_GENERIC_CLIENT_TIMEOUT_EVT"); + switch (opcode) { + case ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET: { + esp_ble_mesh_client_common_param_t common = {0}; + esp_ble_mesh_generic_client_set_state_t set_state = {0}; + /* If failed to get the response of Generic OnOff Set, resend Generic OnOff Set */ + esp_ble_mesh_set_msg_common(&common, &set_state, onoff_client.model, + ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET, remote_onoff); + err = esp_ble_mesh_generic_client_set_state(&common, &set_state); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Generic OnOff Set failed", __func__); + return; + } + break; + } + default: + break; + } + break; + } + default: + break; + } + return; +} + +static int ble_mesh_init(void) +{ + int err = 0; + + memcpy(dev_uuid + 2, esp_bt_dev_get_address(), ESP_BD_ADDR_LEN); + + esp_ble_mesh_register_prov_callback(esp_ble_mesh_prov_cb); + esp_ble_mesh_register_custom_model_callback(esp_ble_mesh_model_cb); + esp_ble_mesh_register_generic_client_callback(esp_ble_mesh_generic_cb); + + err = esp_ble_mesh_init(&provision, &composition); + if (err) { + ESP_LOGE(TAG, "Initializing mesh failed (err %d)", err); + return err; + } + + esp_ble_mesh_node_prov_enable(ESP_BLE_MESH_PROV_ADV | ESP_BLE_MESH_PROV_GATT); + + ESP_LOGI(TAG, "BLE Mesh Node initialized"); + + board_led_operation(LED_G, LED_ON); + + return err; +} + +static esp_err_t bluetooth_init(void) +{ + esp_err_t ret; + + ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK(ret); + + ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT)); + + esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + ret = esp_bt_controller_init(&bt_cfg); + if (ret) { + ESP_LOGE(TAG, "%s initialize controller failed", __func__); + return ret; + } + + ret = esp_bt_controller_enable(ESP_BT_MODE_BLE); + if (ret) { + ESP_LOGE(TAG, "%s enable controller failed", __func__); + return ret; + } + ret = esp_bluedroid_init(); + if (ret) { + ESP_LOGE(TAG, "%s init bluetooth failed", __func__); + return ret; + } + ret = esp_bluedroid_enable(); + if (ret) { + ESP_LOGE(TAG, "%s enable bluetooth failed", __func__); + return ret; + } + + return ret; +} + +void app_main(void) +{ + int err; + + ESP_LOGI(TAG, "Initializing..."); + + board_init(); + + err = bluetooth_init(); + if (err) { + ESP_LOGE(TAG, "esp32_bluetooth_init failed (err %d)", err); + return; + } + + /* Initialize the Bluetooth Mesh Subsystem */ + err = ble_mesh_init(); + if (err) { + ESP_LOGE(TAG, "Bluetooth mesh init failed (err %d)", err); + } +} diff --git a/examples/bluetooth/ble_mesh/ble_mesh_client_model/main/board.c b/examples/bluetooth/ble_mesh/ble_mesh_client_model/main/board.c new file mode 100644 index 0000000000..d81bc58815 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_client_model/main/board.c @@ -0,0 +1,115 @@ +/* board.c - Board-specific hooks */ + +/* + * Copyright (c) 2017 Intel Corporation + * Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" + +#include "driver/uart.h" +#include "esp_log.h" + +#include "esp_ble_mesh_provisioning_api.h" +#include "board.h" + +#define TAG "BOARD" + +#define MESH_UART_NUM UART_NUM_1 +#define MESH_UART (&UART1) + +#define UART_BUF_SIZE 128 + +#define UART1_TX_PIN GPIO_NUM_16 +#define UART1_RX_PIN GPIO_NUM_17 + +extern uint16_t remote_addr; + +struct _led_state led_state[3] = { + { LED_OFF, LED_OFF, LED_R, "red" }, + { LED_OFF, LED_OFF, LED_G, "green" }, + { LED_OFF, LED_OFF, LED_B, "blue" }, +}; + +void board_output_number(esp_ble_mesh_output_action_t action, uint32_t number) +{ + ESP_LOGI(TAG, "Board output number %d", number); +} + +void board_prov_complete(void) +{ + board_led_operation(LED_G, LED_OFF); +} + +void board_led_operation(uint8_t pin, uint8_t onoff) +{ + for (int i = 0; i < ARRAY_SIZE(led_state); i++) { + if (led_state[i].pin != pin) { + continue; + } + if (onoff == led_state[i].previous) { + ESP_LOGW(TAG, "led %s is already %s", + led_state[i].name, (onoff ? "on" : "off")); + return; + } + gpio_set_level(pin, onoff); + led_state[i].previous = onoff; + return; + } + ESP_LOGE(TAG, "LED is not found!"); +} + +static void board_uart_task(void *p) +{ + uint8_t *data = calloc(1, UART_BUF_SIZE); + uint32_t input; + + while (1) { + int len = uart_read_bytes(MESH_UART_NUM, data, UART_BUF_SIZE, 100 / portTICK_RATE_MS); + if (len > 0) { + input = strtoul((const char *)data, NULL, 16); + remote_addr = input & 0xFFFF; + ESP_LOGI(TAG, "%s: input 0x%08x, remote_addr 0x%04x", __func__, input, remote_addr); + memset(data, 0, UART_BUF_SIZE); + } + } + + vTaskDelete(NULL); +} + +static void board_led_init(void) +{ + for (int i = 0; i < ARRAY_SIZE(led_state); i++) { + gpio_pad_select_gpio(led_state[i].pin); + gpio_set_direction(led_state[i].pin, GPIO_MODE_OUTPUT); + gpio_set_level(led_state[i].pin, LED_OFF); + led_state[i].previous = LED_OFF; + } +} + +static void board_uart_init(void) +{ + uart_config_t uart_config = { + .baud_rate = 115200, + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE + }; + uart_param_config(MESH_UART_NUM, &uart_config); + uart_set_pin(MESH_UART_NUM, UART1_TX_PIN, UART1_RX_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); + uart_driver_install(MESH_UART_NUM, UART_BUF_SIZE * 2, 0, 0, NULL, 0); +} + +void board_init(void) +{ + board_led_init(); + board_uart_init(); + xTaskCreate(board_uart_task, "board_uart_task", 2048, NULL, 5, NULL); +} diff --git a/examples/bluetooth/ble_mesh/ble_mesh_client_model/main/board.h b/examples/bluetooth/ble_mesh/ble_mesh_client_model/main/board.h new file mode 100644 index 0000000000..4ae04b2ed4 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_client_model/main/board.h @@ -0,0 +1,42 @@ +/* board.h - Board-specific hooks */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef _BOARD_H_ +#define _BOARD_H_ + +#include "driver/gpio.h" +#include "esp_ble_mesh_defs.h" + +#if defined(CONFIG_BLE_MESH_ESP_WROOM_32) +#define LED_R GPIO_NUM_25 +#define LED_G GPIO_NUM_26 +#define LED_B GPIO_NUM_27 +#elif defined(CONFIG_BLE_MESH_ESP_WROVER) +#define LED_R GPIO_NUM_0 +#define LED_G GPIO_NUM_2 +#define LED_B GPIO_NUM_4 +#endif + +#define LED_ON 1 +#define LED_OFF 0 + +struct _led_state { + uint8_t current; + uint8_t previous; + uint8_t pin; + char *name; +}; + +void board_output_number(esp_ble_mesh_output_action_t action, uint32_t number); + +void board_prov_complete(void); + +void board_led_operation(uint8_t pin, uint8_t onoff); + +void board_init(void); + +#endif diff --git a/examples/bluetooth/ble_mesh/ble_mesh_client_model/main/component.mk b/examples/bluetooth/ble_mesh/ble_mesh_client_model/main/component.mk new file mode 100644 index 0000000000..a98f634eae --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_client_model/main/component.mk @@ -0,0 +1,4 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/examples/bluetooth/ble_mesh/ble_mesh_client_model/sdkconfig.defaults b/examples/bluetooth/ble_mesh/ble_mesh_client_model/sdkconfig.defaults new file mode 100644 index 0000000000..e1aee38f72 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_client_model/sdkconfig.defaults @@ -0,0 +1,45 @@ +# Override some defaults so BT stack is enabled +# by default in this example +CONFIG_BT_ENABLED=y +CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY=y +CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY= +CONFIG_BTDM_CONTROLLER_MODE_BTDM= +CONFIG_BTDM_CONTROLLER_MODEM_SLEEP=n +CONFIG_BLE_SCAN_DUPLICATE=y +CONFIG_SCAN_DUPLICATE_TYPE=2 +CONFIG_DUPLICATE_SCAN_CACHE_SIZE=200 +CONFIG_BLE_MESH_SCAN_DUPLICATE_EN=y +CONFIG_MESH_DUPLICATE_SCAN_CACHE_SIZE=200 +CONFIG_BTDM_CONTROLLER_FULL_SCAN_SUPPORTED=y +CONFIG_GATTS_ENABLE=y +CONFIG_GATTS_SEND_SERVICE_CHANGE_MANUAL=y +CONFIG_BLE_MESH=y +CONFIG_BLE_MESH_HCI_5_0=y +CONFIG_BLE_MESH_USE_DUPLICATE_SCAN=y +CONFIG_BLE_MESH_NODE=y +CONFIG_BLE_MESH_PROV=y +CONFIG_BLE_MESH_NET_BUF_POOL_USAGE=y +CONFIG_BLE_MESH_PROXY=y +CONFIG_BLE_MESH_PB_GATT=y +CONFIG_BLE_MESH_GATT_PROXY=y +CONFIG_BLE_MESH_NODE_ID_TIMEOUT=60 +CONFIG_BLE_MESH_PROXY_FILTER_SIZE=1 +CONFIG_BLE_MESH_IV_UPDATE_TEST= +CONFIG_BLE_MESH_SUBNET_COUNT=1 +CONFIG_BLE_MESH_APP_KEY_COUNT=1 +CONFIG_BLE_MESH_MODEL_KEY_COUNT=1 +CONFIG_BLE_MESH_MODEL_GROUP_COUNT=1 +CONFIG_BLE_MESH_LABEL_COUNT=1 +CONFIG_BLE_MESH_CRPL=10 +CONFIG_BLE_MESH_MSG_CACHE_SIZE=10 +CONFIG_BLE_MESH_ADV_BUF_COUNT=60 +CONFIG_BLE_MESH_TX_SEG_MSG_COUNT=6 +CONFIG_BLE_MESH_RX_SEG_MSG_COUNT=1 +CONFIG_BLE_MESH_RX_SDU_MAX=384 +CONFIG_BLE_MESH_TX_SEG_MAX=32 +CONFIG_BLE_MESH_RELAY=y +CONFIG_BLE_MESH_LOW_POWER= +CONFIG_BLE_MESH_FRIEND= +CONFIG_BLE_MESH_CFG_CLI= +CONFIG_BLE_MESH_GENERIC_ONOFF_CLI=y +CONFIG_BTU_TASK_STACK_SIZE=4512 \ No newline at end of file diff --git a/examples/bluetooth/ble_mesh/ble_mesh_client_model/tutorial/ble_mesh_client_model.md b/examples/bluetooth/ble_mesh/ble_mesh_client_model/tutorial/ble_mesh_client_model.md new file mode 100644 index 0000000000..49697c2ab0 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_client_model/tutorial/ble_mesh_client_model.md @@ -0,0 +1,252 @@ +# 1. Introduction +## 1.1 Demo Function + +1. This demo forwards the message sent by the nRF Mesh app. +2. The user enters the address of the destination node and use it to forwarded packet. +3. The types of the forwarded message include: + * `ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_GET`, + * `ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET`, + * `ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK`. +4. The destination node reports its Onoff state with the `ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_STATUS` message. + +Example: The nRF Mesh app sends a `ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET` message to the node that runs the `ble_mesh_client_model` project. Then this node sends a `ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET` message to the destination node that runs the `ble_mesh_node` project. The address of the destination node is entered by the user via the serial port. + +## 1.1.1 What You Need + +* 1 x Device that runs the `ble_mesh_client_model` project. +* 1 x Device that runs the `ble_mesh_node` project. +* 1 x Phone that installs the nRF Mesh app for controlling these two devices + +## 1.2 Node Composition + +This demo has only one element, in which the following three Models are implemented: + +- **Configuration Server Model** is mainly to represent a mesh network configuration, such as its AppKey, Relay State, TTL State, Subscription List State, etc. +- **Generic OnOff Client Model** controls a Generic OnOff Server via messages defined by the Generic OnOff Model, that is, turning on and off the lights. +- **Generic OnOff Server Model** implements the nodes' Onoff state. + +## 1.3 Message Sequence + +You can choose from the 4 message sequences described below: + +1. Acknowledged Get +2. Acknowledged Set +3. Unacknowledged Set +4. Acknowledged Set with Periodic Publishing + +![Packet interaction](images/message.png) + +## 2. Code Analysis + +Code initialization part reference [Initializing the Bluetooth and Initializing the BLE Mesh](../../ble_mesh_wifi_coexist/tutorial%20%20%20%20%20%20/ble_mesh_wifi_coexist.md) + +### 2.1 Model Definition + +#### 2.1.1 Generic OnOff Server Model + +```c +//Allocating memory for publishing messages. +ESP_BLE_MESH_MODEL_PUB_DEFINE(onoff_srv_pub, 2 + 1, MSG_ROLE); +//Registering the minimum length of messages. For example, the minimum length of the ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET message is registered as 2 octets. +static esp_ble_mesh_model_op_t onoff_op[] = { + { ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_GET, 0, 0}, + { ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET, 2, 0}, + { ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK, 2, 0}, + ESP_BLE_MESH_MODEL_OP_END, +}; +//Registering the Generic Onoff Server Model. +static esp_ble_mesh_model_t root_models[] = { + //onoff server's onoff state init + ESP_BLE_MESH_SIG_MODEL(ESP_BLE_MESH_MODEL_ID_GEN_ONOFF_SRV, onoff_op, + &onoff_srv_pub, &led_state[0]), +}; +``` +#### 2.1.2 Generic OnOff Client Model + +```c +//Allocating memory for publishing messages. +ESP_BLE_MESH_MODEL_PUB_DEFINE(onoff_cli_pub, 2 + 1, MSG_ROLE); +//Registering the Generic Onoff Client Model. +static esp_ble_mesh_model_t root_models[] = { + ESP_BLE_MESH_MODEL_GEN_ONOFF_CLI(&onoff_cli_pub, &onoff_client), +}; +``` + +### 2.2 Model Callback Function + +#### 2.2.1 The Callback function for the Generic Onoff Client Model + +```c +esp_ble_mesh_register_generic_client_callback(esp_ble_mesh_generic_cb); + +``` +1. The callback function will be triggered when the Client Model: + + * Receives a message that indicates the Onoff state of the Sever Model; Or + * Calls any APIs that the Server Model send messages. + +2. The events that the callback function handle: + +| Event name | Opcode |Description | +| ------------- | ------------|------------------------------------------- | +| ESP_BLE_MESH_GENERIC_CLIENT_GET_STATE_EVT | ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_GET |The event triggered when the Generic Onoff Client Model receives acknowledgment after sending the `ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_GET` message | +| ESP_BLE_MESH_GENERIC_CLIENT_SET_STATE_EVT | ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET | The event triggered when the Generic Onoff Client Model receives acknowledgment after sending the `ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET` message | +| ESP_BLE_MESH_GENERIC_CLIENT_PUBLISH_EVT | NA | The event triggered when the Generic Onoff Client Model receives publishing messages. | +| ESP_BLE_MESH_GENERIC_CLIENT_TIMEOUT_EVT | ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET | The event triggered when API (that send messages) calling times out | + + +#### 2.2.2 The Callback function for the Generic Onoff Server Model + +```c +esp_ble_mesh_register_custom_model_callback(esp_ble_mesh_model_cb); + +``` +1. The callback function will be triggered when the Server Model: + + * Receives a message that indicates operating the Onoff state of the Server Model from the Generic Onoff Client Model; Or + * Calls any APIs that the Server Model send messages. + +2. The events of the callback function: + +| Event Name | Opcode | Description | +|-------------------------------------|-------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| ESP_BLE_MESH_MODEL_OPERATION_EVT | ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_GET | The event triggered when the Generic Onoff Server Model receives the ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_GET message that **gets** the Onoff state of the server from the Client Model | +| ESP_BLE_MESH_MODEL_OPERATION_EVT | ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET | The event triggered when the Generic Onoff Server Model receives the ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET message that **sets** the Onoff state of the server from the Client Model | +| ESP_BLE_MESH_MODEL_OPERATION_EVT | ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK | The event triggered when the Generic Onoff Server Model receives the ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK message that **sets** the Onoff state of the server from the Client Model | +| ESP_BLE_MESH_MODEL_SEND_COMP_EVT | NA | The event triggered when the `esp_ble_mesh_server_model_send_msg` API calling completes | +| ESP_BLE_MESH_MODEL_PUBLISH_COMP_EVT | NA | The event triggered when the `esp_ble_mesh_model_publish` API calling completes | + + +### 2.3 Model that Sends Message +#### 2.3.1 Message Control + +The `esp_ble_mesh_set_msg_common` function is used to set the message controlling parameters. + +| Parameter Name |Description | +| ----------------------|------------------------- | +| `opcode` | The message opcode | +| `model` | The pointer to the client Model struct | +| `ctx.net_idx` | The NetKey Index of the subnet through which the message is sent | +| `ctx.app_idx` | The AppKey Index for message encryption | +| `ctx.addr` | The address of the destination nodes | +| `ctx.send_ttl`| The TTL State, which determines how many times a message will be relayed | +| `ctx.send_rel`| This parameter determines if the Model will wait for an acknowledgement after sending a message | +| `msg_timeout` | The maximum time the Model will wait for an acknowledgement | +| `msg_role` | The role of message (node/provisioner) | + +> Note: +> +> Please check the `ESP_BLE_MESH_MODEL_SEND_COMP_EVT` event to see if the message is sent successfully. +> This event is just for sending sig Model and vendor Model messages, not for all Models. + +#### 2.3.2 The Generic Onoff Client sends message + +The Generic Onoff Client Model calls the `esp_ble_mesh_generic_client_get_state` API to get the state of the server Model, such as the Onoff state. + +```c +esp_ble_mesh_generic_client_get_state_t get_state = {0}; +esp_ble_mesh_set_msg_common(&common, node, onoff_client.model, ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_GET); +err = esp_ble_mesh_generic_client_get_state(&common, &get_state); +if (err) { + ESP_LOGE(TAG, "%s: Generic OnOff Get failed", __func__); + return; +} +``` + +The Generic Onoff Client Model calls the `esp_ble_mesh_generic_client_set_state` API to set the state of the server Model, such as the Onoff state. + +```c +esp_ble_mesh_generic_client_set_state_t set_state = {0}; +esp_ble_mesh_set_msg_common(&common, &set_state, onoff_client.model, + ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET, remote_onoff); +err = esp_ble_mesh_generic_client_set_state(&common, &set_state); +if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Generic OnOff Set failed", __func__); + return; +} +``` + +#### 2.3.3 The Generic Onoff Server sends message + +The Generic Onoff Server Model has to bind its Appkey before calling the `esp_ble_mesh_server_model_send_msg` API to send a message. + +```c +err = esp_ble_mesh_server_model_send_msg(model, ctx, ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_STATUS, + sizeof(send_data), &send_data); +``` +The Generic Onoff Server Model calls the `esp_ble_mesh_model_publish` API to publish messages. Only the Models that have subscribed to this destination address receive the published messages. + +```c +err = esp_ble_mesh_model_publish(model, ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_STATUS, + sizeof(led->current), &led->current, ROLE_NODE); +``` + +### 2.4 Users Enter the Address of the Destination Node via Serial Port + +Please connect your devices and enters the address of the destination node via the serial port. + +Users can adjust the address of the destination node. + + +> Note: +> Please connect the pin 16 and pin 17 of the device that runs the `ble_mesh_client_model` project to the USB-to-UART tool. + +```c +#define UART1_TX_PIN GPIO_NUM_16 +#define UART1_RX_PIN GPIO_NUM_17 +``` + +The `board_uart_task` task is used to receive commands sent via the serial port, among which, the`remote_addr` represents the address of destination node that the message is forwarded to. Please enters hexadecimal string, such as 5, for this parameter. The address will be converted to 0x05 automatically. + +```c +static void board_uart_task(void *p) +{ + uint8_t *data = calloc(1, UART_BUF_SIZE); + uint32_t input; + + while (1) { + int len = uart_read_bytes(MESH_UART_NUM, data, UART_BUF_SIZE, 100 / portTICK_RATE_MS); + if (len > 0) { + input = strtoul((const char *)data, NULL, 16); + remote_addr = input & 0xFFFF; + ESP_LOGI(TAG, "%s: input 0x%08x, remote_addr 0x%04x", __func__, input, remote_addr); + memset(data, 0, UART_BUF_SIZE); + } + } + + vTaskDelete(NULL); +} +``` + + +# 3. Timing Sequence Diagram + +The steps for this demo: + +1. The nRF Mesh App provisionings the unprovisioned devices into nodes; +2. The nRF Mesh App adds a Appkey to these nodes, and bind the Models of these nodes to this Appkey. +3. The nRF Mesh App sends a controlling message to the Generic Onoff Client Model. Then the Client Model forwards this message to the server Model of the other node. + +The timing sequence diagram of this demo is shown below: + +![Packet interaction](images/picture5.png)
+ +>Note: +> +>The node **only forwards the message after it receives the controlling message sent by the app**. That is said, the node will **not** forwards messages to the other nodes every time the user enters the address of the destination node through the serial port. + + +# 4. The nRF Mesh App + +![Packet interaction](images/app.png) + +1. Scan the unprovisioned devices. +2. Identity the the capability of the unprovisioned devices. +3. Provisioning the unprovisioned devices. +4. Check if the Mesh node has been configured successful. +5. Configure the Models of the nodes. +6. Click on the Generic On Off Client option. +7. Bind the Generic On Off Client Model to the Appkey. +8. Check if the binding is successfully. +9. Bind the Generic On Off Server Model to the Appkey. +10. Send controlling messages. diff --git a/examples/bluetooth/ble_mesh/ble_mesh_client_model/tutorial/images/app.png b/examples/bluetooth/ble_mesh/ble_mesh_client_model/tutorial/images/app.png new file mode 100644 index 0000000000000000000000000000000000000000..9701c13ac91fcd87be1a2fac5f38255b0c79d33b GIT binary patch literal 443575 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

-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%0D?oY(^k| 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 literal 0 HcmV?d00001 diff --git a/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/CMakeLists.txt b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/CMakeLists.txt new file mode 100644 index 0000000000..74361e558c --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(ble_mesh_console_node) diff --git a/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/Makefile b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/Makefile new file mode 100644 index 0000000000..209aa2e0cc --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/Makefile @@ -0,0 +1,10 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := ble_mesh_console_node + +COMPONENT_ADD_INCLUDEDIRS := components/include + +include $(IDF_PATH)/make/project.mk diff --git a/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/README.md b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/README.md new file mode 100644 index 0000000000..0bb260c816 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/README.md @@ -0,0 +1,9 @@ +# ble mesh node console demo +## Introduction +This demo implements ble mesh node basic features.Based on this demo, node can be scaned and proved by provisioner, reply get/set message to provisioner. + +Demo steps: +1. Build the ble mesh node console demo with sdkconfig.default +2. register node and set oob info, load model to init ble mesh node +3. enable bearer, so that it can be scaned and provisioned by provisioner + diff --git a/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/CMakeLists.txt b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/CMakeLists.txt new file mode 100644 index 0000000000..2275f9a4e0 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/CMakeLists.txt @@ -0,0 +1,12 @@ +set(COMPONENT_SRCS "ble_mesh_adapter.c" + "ble_mesh_cfg_srv_model.c" + "ble_mesh_console_lib.c" + "ble_mesh_console_main.c" + "ble_mesh_console_system.c" + "ble_mesh_register_node_cmd.c" + "ble_mesh_register_server_cmd.c" + "register_bluetooth.c") + +set(COMPONENT_ADD_INCLUDEDIRS ".") + +register_component() diff --git a/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/ble_mesh_adapter.c b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/ble_mesh_adapter.c new file mode 100644 index 0000000000..4123756b8c --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/ble_mesh_adapter.c @@ -0,0 +1,164 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 "ble_mesh_adapter.h" + +esp_ble_mesh_model_t *ble_mesh_get_model(uint16_t model_id) +{ + esp_ble_mesh_model_t *model = NULL; + + switch (model_id) { + case ESP_BLE_MESH_MODEL_ID_CONFIG_SRV: + model = &config_server_models[0]; + break; + case ESP_BLE_MESH_MODEL_ID_CONFIG_CLI: + model = &config_client_models[0]; + break; + case ESP_BLE_MESH_MODEL_ID_GEN_ONOFF_SRV: + model = &gen_onoff_srv_models[1]; + break; +#if (CONFIG_BLE_MESH_GENERIC_ONOFF_CLI) + case ESP_BLE_MESH_MODEL_ID_GEN_ONOFF_CLI: + model = &gen_onoff_cli_models[1]; + break; +#endif + case ESP_BLE_MESH_VND_MODEL_ID_TEST_PERF_CLI: + model = &test_perf_cli_models[0]; + break; + case ESP_BLE_MESH_VND_MODEL_ID_TEST_PERF_SRV: + model = &test_perf_srv_models[0]; + break; + } + return model; +} + +esp_ble_mesh_comp_t *ble_mesh_get_component(uint16_t model_id) +{ + esp_ble_mesh_comp_t *comp = NULL; + + switch (model_id) { + case ESP_BLE_MESH_MODEL_ID_CONFIG_SRV: + comp = &config_server_comp; + break; + case ESP_BLE_MESH_MODEL_ID_CONFIG_CLI: + comp = &config_client_comp; + break; + case ESP_BLE_MESH_MODEL_ID_GEN_ONOFF_SRV: + comp = &gen_onoff_srv_comp; + break; +#if (CONFIG_BLE_MESH_GENERIC_ONOFF_CLI) + case ESP_BLE_MESH_MODEL_ID_GEN_ONOFF_CLI: + comp = &gen_onoff_cli_comp; + break; +#endif + case ESP_BLE_MESH_VND_MODEL_ID_TEST_PERF_CLI: + comp = &test_perf_cli_comp; + break; + case ESP_BLE_MESH_VND_MODEL_ID_TEST_PERF_SRV: + comp = &test_perf_srv_comp; + break; + } + return comp; +} + +void ble_mesh_node_init() +{ + uint16_t i; + + for (i = 0; i < NODE_MAX_GROUP_CONFIG; i++) { + ble_mesh_node_prestore_params[i].net_idx = 0xFFFF; + ble_mesh_node_prestore_params[i].unicast_addr = 0xFFFF; + } + + ble_mesh_node_sema = xSemaphoreCreateMutex(); + if (!ble_mesh_node_sema) { + ESP_LOGE(TAG, "%s init fail, mesh node semaphore create fail", __func__); + } +} + +void ble_mesh_set_node_prestore_params(uint16_t netkey_index, uint16_t unicast_addr) +{ + uint16_t i; + xSemaphoreTake(ble_mesh_node_sema, portMAX_DELAY); + for (i = 0; i < NODE_MAX_GROUP_CONFIG; i++) { + if (ble_mesh_node_prestore_params[i].net_idx != 0xFFFF && ble_mesh_node_prestore_params[i].unicast_addr != 0xFFFF) { + ble_mesh_node_prestore_params[i].net_idx = netkey_index; + ble_mesh_node_prestore_params[i].unicast_addr = unicast_addr; + } + } + xSemaphoreGive(ble_mesh_node_sema); +} + +void ble_mesh_node_statistics_get() +{ + xSemaphoreTake(ble_mesh_node_sema, portMAX_DELAY); + ESP_LOGI(TAG, "statistics:%d,%d\n", ble_mesh_node_statistics.statistics, ble_mesh_node_statistics.package_num); + xSemaphoreGive(ble_mesh_node_sema); +} + +int ble_mesh_node_statistics_accumultate(uint8_t *data, uint32_t value, uint16_t type) +{ + uint16_t i; + uint16_t sequence_num = (data[0] << 8) | data[1]; + + xSemaphoreTake(ble_mesh_node_sema, portMAX_DELAY); + for (i = 0; i < ble_mesh_node_statistics.total_package_num; i++) { + if (ble_mesh_node_statistics.package_index[i] == sequence_num) { + xSemaphoreGive(ble_mesh_node_sema); + return 1; + } + } + + // package type wrong + if (data[2] != type) { + xSemaphoreGive(ble_mesh_node_sema); + return 1; + } + + for (i = 0; i < ble_mesh_node_statistics.total_package_num; i++) { + if (ble_mesh_node_statistics.package_index[i] == 0) { + ble_mesh_node_statistics.package_index[i] = sequence_num; + ble_mesh_node_statistics.package_num += 1; + ble_mesh_node_statistics.statistics += value; + break; + } + } + xSemaphoreGive(ble_mesh_node_sema); + return 0; +} + +int ble_mesh_node_statistics_init(uint16_t package_num) +{ + uint16_t i; + + ble_mesh_node_statistics.package_index = malloc(sizeof(uint16_t) * package_num); + ble_mesh_node_statistics.total_package_num = package_num; + if (ble_mesh_node_statistics.package_index == NULL) { + ESP_LOGE(TAG, " %s, %d malloc fail\n", __func__, __LINE__); + return 1; + } + + ble_mesh_node_statistics.package_num = 0; + for (i = 0; i < package_num; i++) { + ble_mesh_node_statistics.package_index[i] = 0; + } + return 0; +} + +void ble_mesh_node_statistics_destroy() +{ + if (ble_mesh_node_statistics.package_index != NULL) { + free(ble_mesh_node_statistics.package_index); + } +} diff --git a/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/ble_mesh_adapter.h b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/ble_mesh_adapter.h new file mode 100644 index 0000000000..a5122336e2 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/ble_mesh_adapter.h @@ -0,0 +1,97 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#ifndef _BLE_MESH_ADAPTER_H_ +#define _BLE_MESH_ADAPTER_H_ + +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" + +#include "ble_mesh_console_lib.h" +#include "ble_mesh_cfg_srv_model.h" + +#define TAG "ble_mesh_node_console" + +typedef enum { + VENDOR_MODEL_PERF_OPERATION_TYPE_GET = 1, + VENDOR_MODEL_PERF_OPERATION_TYPE_SET, + VENDOR_MODEL_PERF_OPERATION_TYPE_SET_UNACK +} ble_mesh_perf_operation_type; + +typedef struct { + uint8_t current; + uint8_t previous; + char *name; +} ble_mesh_node_status; + +typedef struct { + uint32_t statistics; + uint32_t package_num; + uint16_t *package_index; + uint32_t total_package_num; +} ble_mesh_node_statistics_t; +ble_mesh_node_statistics_t ble_mesh_node_statistics; + +extern SemaphoreHandle_t ble_mesh_node_sema; + +#define arg_int_to_value(src_msg, dst_msg, message) do { \ + if (src_msg->count != 0) {\ + ESP_LOGD(TAG, "\n%s, %s\n", __func__, message);\ + dst_msg = src_msg->ival[0];\ + } \ +} while(0) \ + +#define ble_mesh_node_get_value(index, key, value) do { \ + uint16_t _index = 0; \ + xSemaphoreTake(ble_mesh_node_sema, portMAX_DELAY); \ + for (_index = 0; _index < NODE_MAX_GROUP_CONFIG; _index) { \ + if (node_set_prestore_params[_index].key == value) { \ + break; \ + } \ + } \ + index = _index; \ + xSemaphoreGive(ble_mesh_node_sema); \ +} while(0) \ + +#define ble_mesh_node_set_state(status) do { \ + xSemaphoreTake(ble_mesh_node_sema, portMAX_DELAY); \ + node_status.previous = node_status.current; \ + node_status.current = status; \ + xSemaphoreGive(ble_mesh_node_sema); \ +}while(0) \ + +#define ble_mesh_node_get_state(status) do { \ + xSemaphoreTake(ble_mesh_node_sema, portMAX_DELAY); \ + status = node_status.current; \ + xSemaphoreGive(ble_mesh_node_sema); \ +}while(0) \ + +#define ble_mesh_callback_check_err_code(err_code, message) do { \ + if (err_code == ESP_OK) { \ + ESP_LOGI(TAG, "%s,OK\n", message); \ + } else { \ + ESP_LOGE(TAG, "%s,Fail,%d\n", message, err_code); \ + } \ +}while(0) \ + +void ble_mesh_node_init(); +void ble_mesh_set_node_prestore_params(uint16_t netkey_index, uint16_t unicast_addr); +esp_ble_mesh_model_t *ble_mesh_get_model(uint16_t model_id); +esp_ble_mesh_comp_t *ble_mesh_get_component(uint16_t model_id); +void ble_mesh_node_statistics_get(); +int ble_mesh_node_statistics_accumultate(uint8_t *data, uint32_t value, uint16_t type); +int ble_mesh_node_statistics_init(uint16_t package_num); +void ble_mesh_node_statistics_destroy(); + +#endif //_BLE_MESH_ADAOTER_H_ diff --git a/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/ble_mesh_cfg_srv_model.c b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/ble_mesh_cfg_srv_model.c new file mode 100644 index 0000000000..2e7c938460 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/ble_mesh_cfg_srv_model.c @@ -0,0 +1,208 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 "ble_mesh_cfg_srv_model.h" + +uint8_t dev_uuid[16] = {0xdd, 0xdd}; + +#if CONFIG_BLE_MESH_NODE +esp_ble_mesh_prov_t prov = { + .uuid = dev_uuid, +}; +#endif //CONFIG_BLE_MESH_NODE + +#if CONFIG_BLE_MESH_PROVISIONER +esp_ble_mesh_prov_t prov = { + .prov_uuid = dev_uuid, + .prov_unicast_addr = 0x0001, + .prov_start_address = 0x0005, + .prov_attention = 0x00, + .prov_algorithm = 0x00, + .prov_pub_key_oob = 0x00, + .prov_pub_key_oob_cb = NULL, + .prov_static_oob_val = NULL, + .prov_static_oob_len = 0x00, + .prov_input_num = NULL, + .prov_output_num = NULL, + .flags = 0x00, + .iv_index = 0x00, +}; +#endif //CONFIG_BLE_MESH_PROVISIONER + +ESP_BLE_MESH_MODEL_PUB_DEFINE(model_pub_config, 2 + 1, ROLE_NODE); + +esp_ble_mesh_model_pub_t vendor_model_pub_config; + +// configure server module +esp_ble_mesh_cfg_srv_t cfg_srv = { + .relay = ESP_BLE_MESH_RELAY_ENABLED, + .beacon = ESP_BLE_MESH_BEACON_ENABLED, +#if defined(CONFIG_BLE_MESH_FRIEND) + .friend_state = ESP_BLE_MESH_FRIEND_ENABLED, +#else + .friend_state = ESP_BLE_MESH_FRIEND_NOT_SUPPORTED, +#endif +#if defined(CONFIG_BLE_MESH_GATT_PROXY) + .gatt_proxy = ESP_BLE_MESH_GATT_PROXY_ENABLED, +#else + .gatt_proxy = ESP_BLE_MESH_GATT_PROXY_NOT_SUPPORTED, +#endif + .default_ttl = 7, + + /* 3 transmissions with 20ms interval */ + .net_transmit = ESP_BLE_MESH_TRANSMIT(2, 20), + .relay_retransmit = ESP_BLE_MESH_TRANSMIT(0, 20), +}; + +esp_ble_mesh_model_t config_server_models[] = { + ESP_BLE_MESH_MODEL_CFG_SRV(&cfg_srv), +}; + +esp_ble_mesh_elem_t config_server_elements[] = { + ESP_BLE_MESH_ELEMENT(0, config_server_models, ESP_BLE_MESH_MODEL_NONE), +}; + +esp_ble_mesh_comp_t config_server_comp = { + .cid = CID_ESP, + .elements = config_server_elements, + .element_count = ARRAY_SIZE(config_server_elements), +}; + +// config client model +esp_ble_mesh_model_t config_client_models[] = { + ESP_BLE_MESH_MODEL_CFG_SRV(&cfg_srv), + ESP_BLE_MESH_MODEL_CFG_CLI(&cfg_cli), +}; + +esp_ble_mesh_elem_t config_client_elements[] = { + ESP_BLE_MESH_ELEMENT(0, config_client_models, ESP_BLE_MESH_MODEL_NONE), +}; + +esp_ble_mesh_comp_t config_client_comp = { + .cid = CID_ESP, + .elements = config_client_elements, + .element_count = ARRAY_SIZE(config_client_elements), +}; + +// configure special module +esp_ble_mesh_model_op_t gen_onoff_srv_model_op_config[] = { + ESP_BLE_MESH_MODEL_OP(ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_GET, 0), + ESP_BLE_MESH_MODEL_OP(ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET, 2), + ESP_BLE_MESH_MODEL_OP(ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK, 2), + ESP_BLE_MESH_MODEL_OP_END, +}; + +esp_ble_mesh_model_t gen_onoff_srv_models[] = { + ESP_BLE_MESH_MODEL_CFG_SRV(&cfg_srv), + ESP_BLE_MESH_SIG_MODEL(ESP_BLE_MESH_MODEL_ID_GEN_ONOFF_SRV, gen_onoff_srv_model_op_config, &model_pub_config, NULL), +}; + +esp_ble_mesh_elem_t gen_onoff_srv_elements[] = { + ESP_BLE_MESH_ELEMENT(0, gen_onoff_srv_models, ESP_BLE_MESH_MODEL_NONE), +}; + +esp_ble_mesh_comp_t gen_onoff_srv_comp = { + .cid = CID_ESP, + .elements = gen_onoff_srv_elements, + .element_count = ARRAY_SIZE(gen_onoff_srv_elements), +}; + +// config generic onoff client +#if (CONFIG_BLE_MESH_GENERIC_ONOFF_CLI) + +esp_ble_mesh_client_t gen_onoff_cli; + +esp_ble_mesh_model_t gen_onoff_cli_models[] = { + ESP_BLE_MESH_MODEL_CFG_SRV(&cfg_srv), + ESP_BLE_MESH_MODEL_CFG_CLI(&cfg_cli), + ESP_BLE_MESH_MODEL_GEN_ONOFF_CLI(&model_pub_config, &gen_onoff_cli), +}; + +esp_ble_mesh_elem_t gen_onoff_cli_elements[] = { + ESP_BLE_MESH_ELEMENT(0, gen_onoff_cli_models, ESP_BLE_MESH_MODEL_NONE), +}; + +esp_ble_mesh_comp_t gen_onoff_cli_comp = { + .cid = CID_ESP, + .elements = gen_onoff_cli_elements, + .element_count = ARRAY_SIZE(gen_onoff_cli_elements), +}; +#endif //CONFIG_BLE_MESH_GENERIC_ONOFF_CLI + +//CONFIG VENDOR MODEL TEST PERFORMANCE +#define ESP_BLE_MESH_VND_MODEL_ID_TEST_PERF_SRV 0x2000 +#define ESP_BLE_MESH_VND_MODEL_ID_TEST_PERF_CLI 0x2001 + +#define ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_GET ESP_BLE_MESH_MODEL_OP_3(0x01, CID_ESP) +#define ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_SET ESP_BLE_MESH_MODEL_OP_3(0x02, CID_ESP) +#define ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_SET_UNACK ESP_BLE_MESH_MODEL_OP_3(0x03, CID_ESP) +#define ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_STATUS ESP_BLE_MESH_MODEL_OP_3(0x04, CID_ESP) + +esp_ble_mesh_client_op_pair_t test_perf_cli_op_pair[] = { + {ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_GET, ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_STATUS}, + {ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_SET, ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_STATUS}, + {ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_SET_UNACK, ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_STATUS}, +}; + +esp_ble_mesh_client_t test_perf_cli = { + .op_pair_size = ARRAY_SIZE(test_perf_cli_op_pair), + .op_pair = test_perf_cli_op_pair, +}; + +esp_ble_mesh_model_op_t test_perf_srv_op[] = { + ESP_BLE_MESH_MODEL_OP(ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_GET, 1), + ESP_BLE_MESH_MODEL_OP(ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_SET, 1), + ESP_BLE_MESH_MODEL_OP(ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_SET_UNACK, 1), + ESP_BLE_MESH_MODEL_OP_END, +}; + +esp_ble_mesh_model_op_t test_perf_cli_op[] = { + ESP_BLE_MESH_MODEL_OP(ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_STATUS, 1), + ESP_BLE_MESH_MODEL_OP_END, +}; + +esp_ble_mesh_model_t config_models[] = { + ESP_BLE_MESH_MODEL_CFG_SRV(&cfg_srv), + ESP_BLE_MESH_MODEL_CFG_CLI(&cfg_cli), +}; + +esp_ble_mesh_model_t test_perf_cli_models[] = { + ESP_BLE_MESH_VENDOR_MODEL(CID_ESP, ESP_BLE_MESH_VND_MODEL_ID_TEST_PERF_CLI, + test_perf_cli_op, &vendor_model_pub_config, &test_perf_cli), +}; + +esp_ble_mesh_elem_t test_perf_cli_elements[] = { + ESP_BLE_MESH_ELEMENT(0, config_models, test_perf_cli_models), +}; + +esp_ble_mesh_comp_t test_perf_cli_comp = { + .cid = CID_ESP, + .elements = test_perf_cli_elements, + .element_count = ARRAY_SIZE(test_perf_cli_elements), +}; + +esp_ble_mesh_model_t test_perf_srv_models[] = { + ESP_BLE_MESH_VENDOR_MODEL(CID_ESP, ESP_BLE_MESH_VND_MODEL_ID_TEST_PERF_SRV, + test_perf_srv_op, NULL, NULL), +}; + +esp_ble_mesh_elem_t test_perf_srv_elements[] = { + ESP_BLE_MESH_ELEMENT(0, config_models, test_perf_srv_models), +}; + +esp_ble_mesh_comp_t test_perf_srv_comp = { + .cid = CID_ESP, + .elements = test_perf_srv_elements, + .element_count = ARRAY_SIZE(test_perf_srv_elements), +}; diff --git a/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/ble_mesh_cfg_srv_model.h b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/ble_mesh_cfg_srv_model.h new file mode 100644 index 0000000000..9e43333eb9 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/ble_mesh_cfg_srv_model.h @@ -0,0 +1,107 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#ifndef _BLE_MESH_CFG_SRV_MODEL_H_ +#define _BLE_MESH_CFG_SRV_MODEL_H_ + +#include "esp_ble_mesh_defs.h" +#include "esp_ble_mesh_config_model_api.h" + +#if (CONFIG_BLE_MESH_GENERIC_ONOFF_CLI) +#include "esp_ble_mesh_generic_model_api.h" +#endif //CONFIG_BLE_MESH_GENERIC_ONOFF_CLI + +#define NODE_MAX_GROUP_CONFIG 3 +#define CID_ESP 0x02C4 + +extern uint8_t dev_uuid[16]; + +typedef struct { + uint16_t net_idx; + uint16_t unicast_addr; +} ble_mesh_node_config_params; +ble_mesh_node_config_params ble_mesh_node_prestore_params[NODE_MAX_GROUP_CONFIG]; + +extern esp_ble_mesh_prov_t prov; + +extern esp_ble_mesh_model_pub_t vendor_model_pub_config; + +// configure server module +extern esp_ble_mesh_cfg_srv_t cfg_srv; + +extern esp_ble_mesh_model_t config_server_models[]; + +extern esp_ble_mesh_elem_t config_server_elements[]; + +extern esp_ble_mesh_comp_t config_server_comp; + +// config client model +esp_ble_mesh_client_t cfg_cli; +extern esp_ble_mesh_model_t config_client_models[]; + +extern esp_ble_mesh_elem_t config_client_elements[]; + +extern esp_ble_mesh_comp_t config_client_comp; + +// configure special module +extern esp_ble_mesh_model_op_t gen_onoff_srv_model_op_config[]; + +extern esp_ble_mesh_model_t gen_onoff_srv_models[]; + +extern esp_ble_mesh_elem_t gen_onoff_srv_elements[]; + +extern esp_ble_mesh_comp_t gen_onoff_srv_comp; + +// config generic onoff client +#if (CONFIG_BLE_MESH_GENERIC_ONOFF_CLI) + +extern esp_ble_mesh_client_t gen_onoff_cli; + +extern esp_ble_mesh_model_t gen_onoff_cli_models[]; + +extern esp_ble_mesh_elem_t gen_onoff_cli_elements[]; + +extern esp_ble_mesh_comp_t gen_onoff_cli_comp; +#endif //CONFIG_BLE_MESH_GENERIC_ONOFF_CLI + +//CONFIG VENDOR MODEL TEST PERFORMANCE +#define ESP_BLE_MESH_VND_MODEL_ID_TEST_PERF_SRV 0x2000 +#define ESP_BLE_MESH_VND_MODEL_ID_TEST_PERF_CLI 0x2001 + +#define ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_GET ESP_BLE_MESH_MODEL_OP_3(0x01, CID_ESP) +#define ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_SET ESP_BLE_MESH_MODEL_OP_3(0x02, CID_ESP) +#define ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_SET_UNACK ESP_BLE_MESH_MODEL_OP_3(0x03, CID_ESP) +#define ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_STATUS ESP_BLE_MESH_MODEL_OP_3(0x04, CID_ESP) + +extern esp_ble_mesh_client_t test_perf_cli; + +extern esp_ble_mesh_model_op_t test_perf_srv_op[]; + +extern esp_ble_mesh_model_op_t test_perf_cli_op[]; + +extern esp_ble_mesh_model_t config_models[]; + +extern esp_ble_mesh_model_t test_perf_cli_models[]; + +extern esp_ble_mesh_elem_t test_perf_cli_elements[]; + +extern esp_ble_mesh_comp_t test_perf_cli_comp; + +extern esp_ble_mesh_model_t test_perf_srv_models[]; + +extern esp_ble_mesh_elem_t test_perf_srv_elements[]; + +extern esp_ble_mesh_comp_t test_perf_srv_comp; + +#endif //_BLE_MESH_CFG_SRV_MODEL_H_ diff --git a/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/ble_mesh_console_decl.h b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/ble_mesh_console_decl.h new file mode 100644 index 0000000000..22ab3a7f59 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/ble_mesh_console_decl.h @@ -0,0 +1,28 @@ +/* Console example — declarations of command registration functions. + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#pragma once + +#include "esp_ble_mesh_defs.h" + +// Register system functions +void register_system(); + +// Register blutooth +void register_bluetooth(); + +// Register mesh node cmd +void ble_mesh_register_mesh_node(); + +// Register mesh config server and generic server operation cmd +void ble_mesh_register_server(); + +#if (CONFIG_BLE_MESH_CFG_CLI) +// Register mesh config client operation cmd +void ble_mesh_register_configuration_client_model(); +#endif diff --git a/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/ble_mesh_console_lib.c b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/ble_mesh_console_lib.c new file mode 100644 index 0000000000..88b728bbab --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/ble_mesh_console_lib.c @@ -0,0 +1,128 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 "ble_mesh_console_lib.h" + +static int hex2num(char c); +static int hex2byte(const char *hex); + +static int hex2num(char c) +{ + if (c >= '0' && c <= '9') { + return c - '0'; + } + if (c >= 'a' && c <= 'f') { + return c - 'a' + 10; + } + if (c >= 'A' && c <= 'F') { + return c - 'A' + 10; + } + return -1; +} + +static int hex2byte(const char *hex) +{ + int a, b; + a = hex2num(*hex++); + if (a < 0) { + return -1; + } + b = hex2num(*hex++); + if (b < 0) { + return -1; + } + return (a << 4) | b; +} + +int hexstr_2_bin(const char *hex, uint8_t *buf, uint32_t len) +{ + uint32_t i; + int a; + const char *ipos = hex; + uint8_t *opos = buf; + + for (i = 0; i < len; i++) { + a = hex2byte(ipos); + if (a < 0) { + return -1; + } + *opos ++ = a; + ipos += 2; + } + return 0; +} + +int get_value_string(char *value_in, char *buf) +{ + int result = -1; + + uint16_t length = strlen(value_in); + for(int i = 0; i 2) { + if (value_in[0] == '0' && value_in[1] == 'x') { + buf[(length - 2) / 2] = 0; + result = hexstr_2_bin(&value_in[2], (uint8_t *)buf, (length - 2) / 2); + length = (length - 2) / 2; + } else { + strcpy(buf, value_in); + result = 0; + } + } else { + strcpy(buf, value_in); + result = 0; + } + + return result; +} + +bool str_2_mac(uint8_t *str, uint8_t *dest) +{ + uint8_t loop = 0; + uint8_t tmp = 0; + uint8_t *src_p = str; + + if (strlen((char *)src_p) != 17) { // must be like 12:34:56:78:90:AB + return false; + } + + for (loop = 0; loop < 17 ; loop++) { + if (loop % 3 == 2) { + if (src_p[loop] != ':') { + return false; + } + + continue; + } + + if ((src_p[loop] >= '0') && (src_p[loop] <= '9')) { + tmp = tmp * 16 + src_p[loop] - '0'; + } else if ((src_p[loop] >= 'A') && (src_p[loop] <= 'F')) { + tmp = tmp * 16 + src_p[loop] - 'A' + 10; + } else if ((src_p[loop] >= 'a') && (src_p[loop] <= 'f')) { + tmp = tmp * 16 + src_p[loop] - 'a' + 10; + } else { + return false; + } + + if (loop % 3 == 1) { + *dest++ = tmp; + tmp = 0; + } + } + + return true; +} \ No newline at end of file diff --git a/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/ble_mesh_console_lib.h b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/ble_mesh_console_lib.h new file mode 100644 index 0000000000..8f8449eca4 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/ble_mesh_console_lib.h @@ -0,0 +1,31 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#ifndef _BLE_MESH_CONSOLE_LIB_H_ +#define _BLE_MESH_CONSOLE_LIB_H_ + +#include +#include + +#include "esp_system.h" +#include "esp_log.h" +#include "nvs_flash.h" +#include "esp_console.h" +#include "argtable3/argtable3.h" + +bool str_2_mac(uint8_t *str, uint8_t *dest); +int hexstr_2_bin(const char *hex, uint8_t *buf, uint32_t len); +int get_value_string(char *value_in, char *buf); + +#endif //_BLE_MESH_CONSOLE_LIB_H_ \ No newline at end of file diff --git a/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/ble_mesh_console_main.c b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/ble_mesh_console_main.c new file mode 100644 index 0000000000..e629cf3054 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/ble_mesh_console_main.c @@ -0,0 +1,215 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 + +#include "esp_system.h" +#include "esp_log.h" +#include "esp_vfs_dev.h" +#include "driver/uart.h" +#include "nvs.h" +#include "nvs_flash.h" + +#include "esp_bt.h" +#include "esp_bt_main.h" + +#include "esp_console.h" +#include "linenoise/linenoise.h" +#include "argtable3/argtable3.h" + +#include "ble_mesh_console_decl.h" + +#if CONFIG_STORE_HISTORY + +#define MOUNT_PATH "/data" +#define HISTORY_PATH MOUNT_PATH "/history.txt" + +static void initialize_filesystem() +{ + static wl_handle_t wl_handle; + const esp_vfs_fat_mount_config_t mount_config = { + .max_files = 4, + .format_if_mount_failed = true + }; + esp_err_t err = esp_vfs_fat_spiflash_mount(MOUNT_PATH, "storage", &mount_config, &wl_handle); + if (err != ESP_OK) { + printf("Failed to mount FATFS (0x%x)", err); + return; + } +} +#endif // CONFIG_STORE_HISTORY + +static void initialize_console() +{ + /* Disable buffering on stdin and stdout */ + setvbuf(stdin, NULL, _IONBF, 0); + setvbuf(stdout, NULL, _IONBF, 0); + + /* Minicom, screen, idf_monitor send CR when ENTER key is pressed */ + esp_vfs_dev_uart_set_rx_line_endings(ESP_LINE_ENDINGS_CR); + /* Move the caret to the beginning of the next line on '\n' */ + esp_vfs_dev_uart_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF); + + /* Install UART driver for interrupt-driven reads and writes */ + ESP_ERROR_CHECK( uart_driver_install(CONFIG_CONSOLE_UART_NUM, + 256, 0, 0, NULL, 0) ); + + /* Tell VFS to use UART driver */ + esp_vfs_dev_uart_use_driver(CONFIG_CONSOLE_UART_NUM); + + /* Initialize the console */ + esp_console_config_t console_config = { + .max_cmdline_args = 20, + .max_cmdline_length = 256, +#if CONFIG_LOG_COLORS + .hint_color = atoi(LOG_COLOR_CYAN) +#endif + }; + ESP_ERROR_CHECK( esp_console_init(&console_config) ); + + /* Configure linenoise line completion library */ + /* Enable multiline editing. If not set, long commands will scroll within + * single line. + */ + linenoiseSetMultiLine(1); + + /* Tell linenoise where to get command completions and hints */ + linenoiseSetCompletionCallback(&esp_console_get_completion); + linenoiseSetHintsCallback((linenoiseHintsCallback *) &esp_console_get_hint); + + /* Set command history size */ + linenoiseHistorySetMaxLen(100); + +#if CONFIG_STORE_HISTORY + /* Load command history from filesystem */ + linenoiseHistoryLoad(HISTORY_PATH); +#endif +} + + +esp_err_t bluetooth_init(void) +{ + esp_err_t ret; + + esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + ret = esp_bt_controller_init(&bt_cfg); + if (ret) { + printf("%s initialize controller failed\n", __func__); + return ret; + } + + ret = esp_bt_controller_enable(ESP_BT_MODE_BLE); + if (ret) { + printf("%s enable controller failed\n", __func__); + return ret; + } + ret = esp_bluedroid_init(); + if (ret) { + printf("%s init bluetooth failed\n", __func__); + return ret; + } + ret = esp_bluedroid_enable(); + if (ret) { + printf("%s enable bluetooth failed\n", __func__); + return ret; + } + + return ret; +} + +void app_main(void) +{ + esp_err_t res; + + nvs_flash_init(); + + // init and enable bluetooth + res = bluetooth_init(); + if (res) { + printf("esp32_bluetooth_init failed (ret %d)", res); + } + +#if CONFIG_STORE_HISTORY + initialize_filesystem(); +#endif + + initialize_console(); + + /* Register commands */ + esp_console_register_help_command(); + register_system(); + register_bluetooth(); + ble_mesh_register_mesh_node(); + ble_mesh_register_server(); + + /* Prompt to be printed before each line. + * This can be customized, made dynamic, etc. + */ + const char *prompt = LOG_COLOR_I "esp32> " LOG_RESET_COLOR; + + printf("\n" + "This is an example of ESP-IDF console component.\n" + "Type 'help' to get the list of commands.\n" + "Use UP/DOWN arrows to navigate through command history.\n" + "Press TAB when typing command name to auto-complete.\n"); + + /* Figure out if the terminal supports escape sequences */ + int probe_status = linenoiseProbe(); + if (probe_status) { /* zero indicates success */ + printf("\n" + "Your terminal application does not support escape sequences.\n" + "Line editing and history features are disabled.\n" + "On Windows, try using Putty instead.\n"); + linenoiseSetDumbMode(1); +#if CONFIG_LOG_COLORS + /* Since the terminal doesn't support escape sequences, + * don't use color codes in the prompt. + */ + prompt = "esp32> "; +#endif //CONFIG_LOG_COLORS + } + + /* Main loop */ + while (true) { + /* Get a line using linenoise. + * The line is returned when ENTER is pressed. + */ + char *line = linenoise(prompt); + if (line == NULL) { /* Ignore empty lines */ + continue; + } + /* Add the command to the history */ + linenoiseHistoryAdd(line); +#if CONFIG_STORE_HISTORY + /* Save command history to filesystem */ + linenoiseHistorySave(HISTORY_PATH); +#endif + + /* Try to run the command */ + int ret; + esp_err_t err = esp_console_run(line, &ret); + if (err == ESP_ERR_NOT_FOUND) { + printf("Unrecognized command\n"); + } else if (err == ESP_ERR_INVALID_ARG) { + // command was empty + } else if (err == ESP_OK && ret != ESP_OK) { + printf("\nCommand returned non-zero error code: 0x%x\n", ret); + } else if (err != ESP_OK) { + printf("Internal error: 0x%x\n", err); + } + /* linenoise allocates line buffer on the heap, so need to free it */ + linenoiseFree(line); + } +} diff --git a/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/ble_mesh_console_system.c b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/ble_mesh_console_system.c new file mode 100644 index 0000000000..cc8805dbf2 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/ble_mesh_console_system.c @@ -0,0 +1,183 @@ +/* Console example — various system commands + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include +#include +#include + +#include "esp_log.h" +#include "esp_console.h" +#include "esp_system.h" +#include "esp_sleep.h" +#include "driver/rtc_io.h" +#include "soc/rtc_cntl_reg.h" +#include "argtable3/argtable3.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#include "ble_mesh_console_decl.h" + +#if CONFIG_IDF_CMAKE +#define CONFIG_ESPTOOLPY_PORT "Which is choosen by Users for CMake" +#endif + +static void register_free(); +static void register_restart(); +static void register_make(); + +void register_system() +{ + register_free(); + register_restart(); + register_make(); +} + +/** 'restart' command restarts the program */ + +static int restart(int argc, char **argv) +{ + printf("%s, %s", __func__, "Restarting"); + esp_restart(); +} + +static void register_restart() +{ + const esp_console_cmd_t cmd = { + .command = "restart", + .help = "Restart the program", + .hint = NULL, + .func = &restart, + }; + ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) ); +} + +/** 'free' command prints available heap memory */ + +static int free_mem(int argc, char **argv) +{ + printf("%d\n", esp_get_free_heap_size()); + return 0; +} + +static void register_free() +{ + const esp_console_cmd_t cmd = { + .command = "free", + .help = "Get the total size of heap memory available", + .hint = NULL, + .func = &free_mem, + }; + ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) ); +} + +static int make(int argc, char **argv) +{ + int count = REG_READ(RTC_CNTL_STORE0_REG); + if (++count >= 3) { + printf("This is not the console you are looking for.\n"); + return 0; + } + REG_WRITE(RTC_CNTL_STORE0_REG, count); + + const char *make_output = + R"(LD build/console.elf +esptool.py v2.1-beta1 +)"; + + const char* flash_output[] = { +R"(Flashing binaries to serial port )" CONFIG_ESPTOOLPY_PORT R"( (app at offset 0x10000)... +esptool.py v2.1-beta1 +Connecting.... +)", +R"(Chip is ESP32D0WDQ6 (revision 0) +Uploading stub... +Running stub... +Stub running... +Changing baud rate to 921600 +Changed. +Configuring flash size... +Auto-detected Flash size: 4MB +Flash params set to 0x0220 +Compressed 15712 bytes to 9345... +)", +R"(Wrote 15712 bytes (9345 compressed) at 0x00001000 in 0.1 seconds (effective 1126.9 kbit/s)... +Hash of data verified. +Compressed 333776 bytes to 197830... +)", +R"(Wrote 333776 bytes (197830 compressed) at 0x00010000 in 3.3 seconds (effective 810.3 kbit/s)... +Hash of data verified. +Compressed 3072 bytes to 82... +)", +R"(Wrote 3072 bytes (82 compressed) at 0x00008000 in 0.0 seconds (effective 1588.4 kbit/s)... +Hash of data verified. +Leaving... +Hard resetting... +)" + }; + + const char* monitor_output = +R"(MONITOR +)" LOG_COLOR_W R"(--- idf_monitor on )" CONFIG_ESPTOOLPY_PORT R"( 115200 --- +--- Quit: Ctrl+] | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H -- +)" LOG_RESET_COLOR; + + bool need_make = false; + bool need_flash = false; + bool need_monitor = false; + for (int i = 1; i < argc; ++i) { + if (strcmp(argv[i], "all") == 0) { + need_make = true; + } else if (strcmp(argv[i], "flash") == 0) { + need_make = true; + need_flash = true; + } else if (strcmp(argv[i], "monitor") == 0) { + need_monitor = true; + } else if (argv[i][0] == '-') { + /* probably -j option */ + } else if (isdigit((int) argv[i][0])) { + /* might be an argument to -j */ + } else { + printf("make: *** No rule to make target `%s'. Stop.\n", argv[i]); + /* Technically this is an error, but let's not spoil the output */ + return 0; + } + } + if (argc == 1) { + need_make = true; + } + if (need_make) { + printf("%s", make_output); + } + if (need_flash) { + size_t n_items = sizeof(flash_output) / sizeof(flash_output[0]); + for (int i = 0; i < n_items; ++i) { + printf("%s", flash_output[i]); + vTaskDelay(200/portTICK_PERIOD_MS); + } + } + if (need_monitor) { + printf("%s", monitor_output); + esp_restart(); + } + return 0; +} + +static void register_make() +{ + const esp_console_cmd_t cmd = { + .command = "make", + .help = NULL, /* Do not include in 'help' output */ + .hint = "all | flash | monitor", + .func = &make, + }; + ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) ); +} + + diff --git a/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/ble_mesh_register_node_cmd.c b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/ble_mesh_register_node_cmd.c new file mode 100644 index 0000000000..da08884e35 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/ble_mesh_register_node_cmd.c @@ -0,0 +1,547 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 "esp_bt.h" +#include "soc/soc.h" + +#include "esp_bt_device.h" + +#include "test.h" +#include "esp_ble_mesh_networking_api.h" +#include "esp_ble_mesh_defs.h" +#include "esp_ble_mesh_common_api.h" +#include "esp_ble_mesh_provisioning_api.h" + +#include "ble_mesh_console_lib.h" +#include "ble_mesh_adapter.h" + +typedef struct { + struct arg_str *static_val; + struct arg_int *static_val_len; + struct arg_int *output_size; + struct arg_int *output_actions; + struct arg_int *input_size; + struct arg_int *input_actions; + struct arg_end *end; +} ble_mesh_prov_t; +static ble_mesh_prov_t oob; + +typedef struct { + struct arg_int *model_type; + struct arg_int *config_index; + struct arg_str *dev_uuid; + struct arg_int *pub_config; + struct arg_end *end; +} ble_mesh_comp_t; +static ble_mesh_comp_t component; + +typedef struct { + struct arg_int *bearer; + struct arg_int *enable; + struct arg_end *end; +} ble_mesh_bearer_t; +static ble_mesh_bearer_t bearer; + +typedef struct { + struct arg_str *action_type; + struct arg_int *package_num; + struct arg_end *end; +} ble_mesh_node_statistices_t; +ble_mesh_node_statistices_t node_statistices; + +typedef struct { + struct arg_str *action_type; + struct arg_int *tx_sense_power; + struct arg_end *end; +} ble_mesh_tx_sense_power; +static ble_mesh_tx_sense_power power_set; + +typedef struct { + struct arg_str *net_key; + struct arg_int *net_idx; + struct arg_int *unicast_addr; + struct arg_str *dev_key; + struct arg_str *app_key; + struct arg_int *app_idx; + struct arg_int *group_addr; + struct arg_end *end; +} ble_mesh_node_network_info_t; +ble_mesh_node_network_info_t node_network_info; + +ble_mesh_node_status node_status = { + .previous = 0x0, + .current = 0x0, +}; + +SemaphoreHandle_t ble_mesh_node_sema; + +void ble_mesh_register_node_cmd(); +// Register callback function +void ble_mesh_prov_cb(esp_ble_mesh_prov_cb_event_t event, esp_ble_mesh_prov_cb_param_t *param); +void ble_mesh_model_cb(esp_ble_mesh_model_cb_event_t event, esp_ble_mesh_model_cb_param_t *param); + + +void ble_mesh_register_mesh_node() +{ + ble_mesh_register_node_cmd(); +} + +int ble_mesh_register_node_cb() +{ + ESP_LOGD(TAG, "enter %s\n", __func__); + ble_mesh_node_init(); + esp_ble_mesh_register_prov_callback(ble_mesh_prov_cb); + esp_ble_mesh_register_custom_model_callback(ble_mesh_model_cb); + ESP_LOGI(TAG, "Node:Reg,OK"); + ESP_LOGD(TAG, "exit %s\n", __func__); + return 0; +} + +void ble_mesh_prov_cb(esp_ble_mesh_prov_cb_event_t event, esp_ble_mesh_prov_cb_param_t *param) +{ + ESP_LOGD(TAG, "enter %s, event = %d", __func__, event); + switch (event) { + case ESP_BLE_MESH_PROV_REGISTER_COMP_EVT: + ble_mesh_callback_check_err_code(param->prov_register_comp.err_code, "Provisioning:Register"); + break; + case ESP_BLE_MESH_NODE_PROV_ENABLE_COMP_EVT: + ble_mesh_callback_check_err_code(param->node_prov_enable_comp.err_code, "Node:EnBearer"); + break; + case ESP_BLE_MESH_NODE_PROV_DISABLE_COMP_EVT: + ble_mesh_callback_check_err_code(param->node_prov_disable_comp.err_code, "Node:DisBearer"); + break; + case ESP_BLE_MESH_NODE_PROV_LINK_OPEN_EVT: + ESP_LOGI(TAG, "Node:LinkOpen,OK,%d", param->node_prov_link_open.bearer); + break; + case ESP_BLE_MESH_NODE_PROV_LINK_CLOSE_EVT: + ESP_LOGI(TAG, "Node:LinkClose,OK,%d", param->node_prov_link_close.bearer); + break; + case ESP_BLE_MESH_NODE_PROV_OUTPUT_NUMBER_EVT: + ESP_LOGI(TAG, "Node:OutPut,%d,%d", param->node_prov_output_num.action, param->node_prov_output_num.number); + break; + case ESP_BLE_MESH_NODE_PROV_OUTPUT_STRING_EVT: + ESP_LOGI(TAG, "Node:OutPutStr,%s", param->node_prov_output_str.string); + break; + case ESP_BLE_MESH_NODE_PROV_INPUT_EVT: + ESP_LOGI(TAG, "Node:InPut,%d,%d", param->node_prov_input.action, param->node_prov_input.size); + break; + case ESP_BLE_MESH_NODE_PROV_COMPLETE_EVT: + ESP_LOGI(TAG, "Provisioning:Success,%d", param->node_prov_complete.addr); + ble_mesh_set_node_prestore_params(param->node_prov_complete.net_idx, param->node_prov_complete.addr); + break; + case ESP_BLE_MESH_NODE_PROV_RESET_EVT: + ESP_LOGI(TAG, "Node:Reset"); + break; + case ESP_BLE_MESH_NODE_PROV_INPUT_NUMBER_COMP_EVT: + ble_mesh_callback_check_err_code(param->node_prov_input_num_comp.err_code, "Node:InputNum"); + break; + case ESP_BLE_MESH_NODE_PROV_INPUT_STRING_COMP_EVT: + ble_mesh_callback_check_err_code(param->node_prov_input_str_comp.err_code, "Node:InputStr"); + break; + case ESP_BLE_MESH_NODE_SET_UNPROV_DEV_NAME_COMP_EVT: + ble_mesh_callback_check_err_code(param->node_set_unprov_dev_name_comp.err_code, "Node:SetName"); + break; + case ESP_BLE_MESH_NODE_PROXY_IDENTITY_ENABLE_COMP_EVT: + ble_mesh_callback_check_err_code(param->node_proxy_identity_enable_comp.err_code, "Node:ProxyIndentity"); + break; + case ESP_BLE_MESH_NODE_PROXY_GATT_ENABLE_COMP_EVT: + ble_mesh_callback_check_err_code(param->node_proxy_gatt_enable_comp.err_code, "Node:EnProxyGatt"); + break; + case ESP_BLE_MESH_NODE_PROXY_GATT_DISABLE_COMP_EVT: + ble_mesh_callback_check_err_code(param->node_proxy_gatt_disable_comp.err_code, "Node:DisProxyGatt"); + break; + default: + break; + } + ESP_LOGD(TAG, "exit %s", __func__); +} + +void ble_mesh_model_cb(esp_ble_mesh_model_cb_event_t event, esp_ble_mesh_model_cb_param_t *param) +{ + uint8_t status; + uint16_t result; + uint8_t data[4]; + + ESP_LOGD(TAG, "enter %s, event=%x\n", __func__, event); + printf("enter %s, event=%x\n", __func__, event); + switch (event) { + case ESP_BLE_MESH_MODEL_OPERATION_EVT: + if (param->model_operation.model != NULL && param->model_operation.model->op != NULL) { + if (param->model_operation.opcode == ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_GET) { + ESP_LOGI(TAG, "Node:GetStatus,Success"); + ble_mesh_node_get_state(status); + esp_ble_mesh_server_model_send_msg(param->model_operation.model, param->model_operation.ctx, ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_STATUS, + sizeof(status), &status); + } else if (param->model_operation.opcode == ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET) { + ESP_LOGI(TAG, "Node:SetAck,Success,%d,%d,%d", param->model_operation.msg[0], param->model_operation.ctx->recv_ttl, param->model_operation.length); + ble_mesh_node_set_state(param->model_operation.msg[0]); + ble_mesh_node_get_state(status); + esp_ble_mesh_server_model_send_msg(param->model_operation.model, param->model_operation.ctx, ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_STATUS, + sizeof(status), &status); + } else if (param->model_operation.opcode == ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK) { + ble_mesh_node_set_state(param->model_operation.msg[0]); + ESP_LOGI(TAG, "Node:SetUnAck,Success,%d,%d", param->model_operation.msg[0], param->model_operation.ctx->recv_ttl); + } else if (param->model_operation.opcode == ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_STATUS) { + ESP_LOGI(TAG, "Node:Status,Success,%d", param->model_operation.length); + } else if (param->model_operation.opcode == ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_SET) { + ESP_LOGI(TAG, "VendorModel:SetAck,Success,%d", param->model_operation.ctx->recv_ttl); + data[0] = param->model_operation.msg[0]; + data[1] = param->model_operation.msg[1]; + data[2] = param->model_operation.msg[2]; + data[3] = param->model_operation.ctx->recv_ttl; + result = ble_mesh_node_statistics_accumultate(param->model_operation.msg, param->model_operation.length, VENDOR_MODEL_PERF_OPERATION_TYPE_SET); + if (result == 0) { + esp_ble_mesh_server_model_send_msg(param->model_operation.model, param->model_operation.ctx, + ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_STATUS, sizeof(data), data); + } + } else if (param->model_operation.opcode == ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_SET_UNACK) { + ESP_LOGI(TAG, "VendorModel:SetUnAck,Success,%d,%d", param->model_operation.ctx->recv_ttl, param->model_operation.length); + result = ble_mesh_node_statistics_accumultate(param->model_operation.msg, param->model_operation.length, VENDOR_MODEL_PERF_OPERATION_TYPE_SET_UNACK); + if (result == 0) { + esp_ble_mesh_server_model_send_msg(param->model_operation.model, param->model_operation.ctx, + ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_STATUS, param->model_operation.length, param->model_operation.msg); + } + } + } + break; + case ESP_BLE_MESH_MODEL_SEND_COMP_EVT: + if (param->model_send_comp.err_code == ESP_OK) { + ESP_LOGI(TAG, "Node:ModelSend,OK"); + } else { + ESP_LOGE(TAG, "Node:ModelSend,Fail"); + } + break; + case ESP_BLE_MESH_MODEL_PUBLISH_COMP_EVT: + ESP_LOGI(TAG, "PublishSend,OK,0x%x,%d,", param->model_publish_comp.model->model_id, param->model_publish_comp.model->pub->msg->len); + break; + case ESP_BLE_MESH_MODEL_PUBLISH_UPDATE_EVT: + ESP_LOGI(TAG, "PublishUpdate,OK"); + break; + case ESP_BLE_MESH_CLIENT_MODEL_SEND_TIMEOUT_EVT: + ESP_LOGI(TAG, "Node:TimeOut"); + break; + case ESP_BLE_MESH_MODEL_EVT_MAX: + ESP_LOGI(TAG, "Node:MaxEvt"); + break; + default: + break; + } + + ESP_LOGD(TAG, "exit %s\n", __func__); +} + +int ble_mesh_power_set(int argc, char **argv) +{ + esp_err_t result = ESP_OK; + ESP_LOGD(TAG, "enter %s\n", __func__); + int nerrors = arg_parse(argc, argv, (void **) &power_set); + if (nerrors != 0) { + arg_print_errors(stderr, power_set.end, argv[0]); + return 1; + } + + if (strcmp(power_set.action_type->sval[0], "tx") == 0) { + result = esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_ADV, power_set.tx_sense_power->ival[0]); + } else if (strcmp(power_set.action_type->sval[0], "sense") == 0) { + uint32_t *reg = (uint32_t *)(0x6001c07c); + int reg_addr = 0x6001c07c; + uint32_t flag = 0x00FF0000; + uint32_t sense_new = power_set.tx_sense_power->ival[0]; + uint32_t reg_to_write = ((*reg) &= ~flag) | ((256 - sense_new) << 16); + REG_WRITE(reg_addr, reg_to_write); + + } + + if (result == ESP_OK) { + ESP_LOGI(TAG, "Node:SetPower,OK\n"); + } + ESP_LOGD(TAG, "exit %s\n", __func__); + return result; +} + +static int ble_mesh_load_oob(int argc, char **argv) +{ + uint8_t *static_val; + int nerrors = arg_parse(argc, argv, (void **) &oob); + + ESP_LOGD(TAG, "enter %s \n", __func__); + + if (nerrors != 0) { + arg_print_errors(stderr, oob.end, argv[0]); + return 1; + } + + //parsing prov + if (oob.static_val->count != 0) { + static_val = malloc(oob.static_val_len->ival[0] + 1); + get_value_string((char *)oob.static_val->sval[0], (char *)static_val); + prov.static_val = static_val; + } + + arg_int_to_value(oob.static_val_len, prov.static_val_len, "static_val_len"); + arg_int_to_value(oob.output_size, prov.output_size, "output_size"); + arg_int_to_value(oob.output_actions, prov.output_actions, "output_actions"); + arg_int_to_value(oob.input_size, prov.input_size, "input_size"); + arg_int_to_value(oob.input_actions, prov.input_actions, "input_action"); + + ESP_LOGI(TAG, "OOB:Load,OK\n"); + ESP_LOGD(TAG, "exit %s\n", __func__); + return 0; +} + +int ble_mesh_init(int argc, char **argv) +{ + int err; + esp_ble_mesh_comp_t *local_component = NULL; + uint8_t *device_uuid =NULL; + + int nerrors = arg_parse(argc, argv, (void **) &component); + if (nerrors != 0) { + arg_print_errors(stderr, component.end, argv[0]); + return 1; + } + + ESP_LOGD(TAG, "enter %s, module %x\n", __func__, component.model_type->ival[0]); + local_component = ble_mesh_get_component(component.model_type->ival[0]); + + if (component.dev_uuid->count != 0) { + device_uuid = malloc((16 + 1) * sizeof(uint8_t)); + if (device_uuid == NULL) { + ESP_LOGE(TAG, "ble mesh malloc failed, %d\n", __LINE__); + } + get_value_string((char *)component.dev_uuid->sval[0], (char *) device_uuid); + memcpy(dev_uuid, device_uuid, 16); + } else { + memcpy(dev_uuid, esp_bt_dev_get_address(), 6); + } + + err = esp_ble_mesh_init(&prov, local_component); + if (err) { + ESP_LOGE(TAG, "Initializing mesh failed (err %d)\n", err); + return err; + } + + free(device_uuid); + ESP_LOGD(TAG, "exit %s\n", __func__); + return err; +} + +int ble_mesh_node_enable_bearer(int argc, char **argv) +{ + esp_err_t err = 0; + + ESP_LOGD(TAG, "enter %s \n", __func__); + + int nerrors = arg_parse(argc, argv, (void **) &bearer); + if (nerrors != 0) { + arg_print_errors(stderr, bearer.end, argv[0]); + return 1; + } + + if (bearer.enable->count != 0) { + if (bearer.enable->ival[0]) { + //err = esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_ADV, ESP_PWR_LVL_N12); + err = esp_ble_mesh_node_prov_enable(bearer.bearer->ival[0]); + } else { + err = esp_ble_mesh_node_prov_disable(bearer.bearer->ival[0]); + } + } else { + return 1; + } + + ESP_LOGD(TAG, "exit %s\n", __func__); + return err; +} + +int ble_mesh_node_reset() +{ + esp_err_t err; + ESP_LOGD(TAG, "enter %s\n", __func__); + + err = esp_ble_mesh_node_local_reset(); + + ESP_LOGD(TAG, "exit %s\n", __func__); + return err; +} + +int ble_mesh_node_statistics_regist(int argc, char **argv) +{ + int result = ESP_OK; + + int nerrors = arg_parse(argc, argv, (void **) &node_statistices); + if (nerrors != 0) { + arg_print_errors(stderr, node_statistices.end, argv[0]); + return 1; + } + + ESP_LOGD(TAG, "enter %s\n", __func__); + + if (strcmp(node_statistices.action_type->sval[0], "init") == 0) { + result = ble_mesh_node_statistics_init(node_statistices.package_num->ival[0]); + ESP_LOGI(TAG, "Node:InitStatistics,OK\n"); + } else if (strcmp(node_statistices.action_type->sval[0], "get") == 0) { + ble_mesh_node_statistics_get(); + ESP_LOGI(TAG, "Node:GetStatistics,OK\n"); + } else if (strcmp(node_statistices.action_type->sval[0], "destroy") == 0) { + ble_mesh_node_statistics_destroy(); + ESP_LOGI(TAG, "Node:DestroyStatistics\n"); + } + + ESP_LOGD(TAG, "exit %s\n", __func__); + return result; +} + +int ble_mesh_node_enter_network_auto(int argc, char **argv) +{ + esp_err_t err = ESP_OK; + struct bt_mesh_device_network_info info = { + .flags = 0, + .iv_index = 0, + }; + + int nerrors = arg_parse(argc, argv, (void **) &node_network_info); + if (nerrors != 0) { + arg_print_errors(stderr, node_network_info.end, argv[0]); + return 1; + } + + ESP_LOGD(TAG, "enter %s\n", __func__); + + arg_int_to_value(node_network_info.net_idx, info.net_idx, "network key index"); + arg_int_to_value(node_network_info.unicast_addr, info.unicast_addr, "unicast address"); + arg_int_to_value(node_network_info.app_idx, info.app_idx, "appkey index"); + arg_int_to_value(node_network_info.group_addr, info.group_addr, "group address"); + err = get_value_string((char *)node_network_info.net_key->sval[0], (char *)info.net_key); + err = get_value_string((char *)node_network_info.dev_key->sval[0], (char *)info.dev_key); + err = get_value_string((char *)node_network_info.app_key->sval[0], (char *)info.app_key); + + err = bt_mesh_device_auto_enter_network(&info); + if (err == ESP_OK) { + ESP_LOGD(TAG, "NODE:EnNetwork,OK"); + } else { + ESP_LOGE(TAG, "NODE:EnNetwork,FAIL,%d", err); + } + + ESP_LOGD(TAG, "exit %s\n", __func__); + return err; +} + +void ble_mesh_register_node_cmd() +{ + const esp_console_cmd_t register_cmd = { + .command = "bmreg", + .help = "ble mesh: provisioner/node register callback", + .hint = NULL, + .func = &ble_mesh_register_node_cb, + }; + ESP_ERROR_CHECK(esp_console_cmd_register(®ister_cmd)); + + oob.static_val = arg_str0("s", NULL, "", "Static OOB value"); + oob.static_val_len = arg_int0("l", NULL, "", "Static OOB value length"); + oob.output_size = arg_int0("x", NULL, "", "Maximum size of Output OOB"); + oob.output_actions = arg_int0("o", NULL, "", "Supported Output OOB Actions"); + oob.input_size = arg_int0("y", NULL, "", "Maximum size of Input OOB"); + oob.input_actions = arg_int0("i", NULL, "", "Supported Input OOB Actions"); + oob.end = arg_end(1); + + const esp_console_cmd_t oob_cmd = { + .command = "bmoob", + .help = "ble mesh: provisioner/node config OOB parameters", + .hint = NULL, + .func = &ble_mesh_load_oob, + .argtable = &oob, + }; + ESP_ERROR_CHECK( esp_console_cmd_register(&oob_cmd) ); + + component.model_type = arg_int0("m", NULL, "", "mesh model"); + component.config_index = arg_int0("c", NULL, "", "mesh model op"); + component.config_index->ival[0] = 0; // set default value + component.pub_config = arg_int0("p", NULL, "", "publish message buffer"); + component.dev_uuid = arg_str0("d", NULL, "", "device uuid"); + component.end = arg_end(1); + + const esp_console_cmd_t model_cmd = { + .command = "bminit", + .help = "ble mesh: provisioner/node init", + .hint = NULL, + .func = &ble_mesh_init, + .argtable = &component, + }; + ESP_ERROR_CHECK( esp_console_cmd_register(&model_cmd) ); + + bearer.bearer = arg_int0("b", NULL, "", "supported bearer"); + bearer.enable = arg_int0("e", NULL, "", "bearers node supported"); + bearer.end = arg_end(1); + + const esp_console_cmd_t bearer_cmd = { + .command = "bmnbearer", + .help = "ble mesh node: enable/disable different bearer", + .hint = NULL, + .func = &ble_mesh_node_enable_bearer, + .argtable = &bearer, + }; + ESP_ERROR_CHECK(esp_console_cmd_register(&bearer_cmd)); + + const esp_console_cmd_t reset_cmd = { + .command = "bmnreset", + .help = "ble mesh node: reset", + .hint = NULL, + .func = &ble_mesh_node_reset, + }; + ESP_ERROR_CHECK(esp_console_cmd_register(&reset_cmd)); + + node_statistices.action_type = arg_str1("z", NULL, "", "action type"); + node_statistices.package_num = arg_int0("p", NULL, "", "package number"); + node_statistices.end = arg_end(1); + + const esp_console_cmd_t node_statistices_cmd = { + .command = "bmsperf", + .help = "ble mesh server: performance statistics", + .hint = NULL, + .func = &ble_mesh_node_statistics_regist, + .argtable = &node_statistices, + }; + ESP_ERROR_CHECK(esp_console_cmd_register(&node_statistices_cmd)); + + power_set.action_type = arg_str1("z", NULL, "", "action type"); + power_set.tx_sense_power = arg_int0("t", NULL, "", "tx power or sense"); + power_set.end = arg_end(1); + + const esp_console_cmd_t power_set_cmd = { + .command = "bmtxpower", + .help = "ble mesh: set tx power or sense", + .hint = NULL, + .func = &ble_mesh_power_set, + .argtable = &power_set, + }; + ESP_ERROR_CHECK(esp_console_cmd_register(&power_set_cmd)); + + node_network_info.net_key = arg_str1("k", NULL, "", "network key"); + node_network_info.net_idx = arg_int1("n", NULL, "", "network key index"); + node_network_info.unicast_addr = arg_int1("u", NULL, "", "unicast address"); + node_network_info.dev_key = arg_str1("d", NULL, "", "device key"); + node_network_info.app_key = arg_str1("a", NULL, "", "app key"); + node_network_info.app_idx = arg_int1("i", NULL, "", "appkey index"); + node_network_info.group_addr = arg_int1("g", NULL, "", "group address"); + node_network_info.end = arg_end(1); + + const esp_console_cmd_t node_network_info_cmd = { + .command = "bmnnwk", + .help = "ble mesh node: auto join network", + .hint = NULL, + .func = &ble_mesh_node_enter_network_auto, + .argtable = &node_network_info, + }; + ESP_ERROR_CHECK(esp_console_cmd_register(&node_network_info_cmd)); +} diff --git a/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/ble_mesh_register_server_cmd.c b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/ble_mesh_register_server_cmd.c new file mode 100644 index 0000000000..3abe85dc01 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/ble_mesh_register_server_cmd.c @@ -0,0 +1,83 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 "esp_ble_mesh_defs.h" +#include "esp_ble_mesh_networking_api.h" + +#include "ble_mesh_console_lib.h" +#include "ble_mesh_adapter.h" + +void ble_mesh_register_server_operation(); + +typedef struct { + struct arg_str *data; + struct arg_int *opcode; + struct arg_int *model; + struct arg_int *role; + struct arg_end *end; +} ble_mesh_publish_message; +ble_mesh_publish_message msg_publish; + +void ble_mesh_register_server() +{ + ble_mesh_register_server_operation(); +} + +int ble_mesh_module_publish_message(int argc, char **argv) +{ + esp_err_t err; + esp_ble_mesh_model_t *model = NULL; + uint8_t *data = NULL; + uint8_t device_role = ROLE_NODE; + uint16_t length = 0; + + ESP_LOGD(TAG, "enter %s \n", __func__); + + int nerrors = arg_parse(argc, argv, (void **) &msg_publish); + if (nerrors != 0) { + arg_print_errors(stderr, msg_publish.end, argv[0]); + return 1; + } + + data = malloc(strlen(msg_publish.data->sval[0])); + get_value_string((char *)msg_publish.data->sval[0], (char *) data); + + arg_int_to_value(msg_publish.role, device_role, "device role"); + model = ble_mesh_get_model(msg_publish.model->ival[0]); + + err = esp_ble_mesh_model_publish(model, msg_publish.opcode->ival[0], length, data, device_role); + + ESP_LOGD(TAG, "exit %s \n", __func__); + free(data); + return err; +} + +void ble_mesh_register_server_operation() +{ + msg_publish.data = arg_str1("d", NULL, "", "message data"); + msg_publish.opcode = arg_int1("o", NULL, "", "operation opcode"); + msg_publish.model = arg_int1("m", NULL, "", "module published to"); + msg_publish.role = arg_int1("r", NULL, "", "device role"); + msg_publish.end = arg_end(1); + + const esp_console_cmd_t msg_publish_cmd = { + .command = "bmpublish", + .help = "ble mesh: publish message", + .hint = NULL, + .func = &ble_mesh_module_publish_message, + .argtable = &msg_publish, + }; + ESP_ERROR_CHECK(esp_console_cmd_register(&msg_publish_cmd)); +} + diff --git a/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/component.mk b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/component.mk new file mode 100644 index 0000000000..0b9d7585e7 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/component.mk @@ -0,0 +1,5 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) + diff --git a/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/register_bluetooth.c b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/register_bluetooth.c new file mode 100644 index 0000000000..5afaca813b --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/main/register_bluetooth.c @@ -0,0 +1,45 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 "esp_bt_device.h" +#include "esp_console.h" + +#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] +#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" + +void register_ble_address(); + +void register_bluetooth() +{ + register_ble_address(); +} + +int bt_mac() +{ + const uint8_t *mac = esp_bt_dev_get_address(); + printf("+BTMAC:"MACSTR"\n", MAC2STR(mac)); + return 0; +} + +void register_ble_address() +{ + const esp_console_cmd_t cmd = { + .command = "btmac", + .help = "get BT mac address", + .hint = NULL, + .func = (esp_console_cmd_func_t)&bt_mac, + }; + ESP_ERROR_CHECK(esp_console_cmd_register(&cmd)); +} + diff --git a/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/sdkconfig.defaults b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/sdkconfig.defaults new file mode 100644 index 0000000000..732947bb16 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_node/sdkconfig.defaults @@ -0,0 +1,46 @@ +# Override some defaults so BT stack is enabled +# by default in this example +CONFIG_CONSOLE_UART_BAUDRATE=921600 +CONFIG_ESPTOOLPY_BAUD_921600B=y +CONFIG_MONITOR_BAUD_921600B=y +CONFIG_BT_ENABLED=y +CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY=y +CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY= +CONFIG_BTDM_CONTROLLER_MODE_BTDM= +CONFIG_BTDM_CONTROLLER_MODEM_SLEEP=n +CONFIG_BLE_SCAN_DUPLICATE=y +CONFIG_SCAN_DUPLICATE_TYPE=2 +CONFIG_DUPLICATE_SCAN_CACHE_SIZE=200 +CONFIG_BLE_MESH_SCAN_DUPLICATE_EN=y +CONFIG_MESH_DUPLICATE_SCAN_CACHE_SIZE=200 +CONFIG_BTDM_CONTROLLER_FULL_SCAN_SUPPORTED=y +CONFIG_GATTS_ENABLE=y +CONFIG_GATTS_SEND_SERVICE_CHANGE_MANUAL=y +CONFIG_BLE_MESH=y +CONFIG_BLE_MESH_HCI_5_0=y +CONFIG_BLE_MESH_USE_DUPLICATE_SCAN=y +CONFIG_BLE_MESH_NODE=y +CONFIG_BLE_MESH_PROV=y +CONFIG_BLE_MESH_NET_BUF_POOL_USAGE=y +CONFIG_BLE_MESH_PROXY=y +CONFIG_BLE_MESH_PB_GATT=y +CONFIG_BLE_MESH_GATT_PROXY=y +CONFIG_BLE_MESH_NODE_ID_TIMEOUT=60 +CONFIG_BLE_MESH_PROXY_FILTER_SIZE=1 +CONFIG_BLE_MESH_SUBNET_COUNT=1 +CONFIG_BLE_MESH_APP_KEY_COUNT=1 +CONFIG_BLE_MESH_MODEL_KEY_COUNT=1 +CONFIG_BLE_MESH_MODEL_GROUP_COUNT=1 +CONFIG_BLE_MESH_LABEL_COUNT=1 +CONFIG_BLE_MESH_CRPL=10 +CONFIG_BLE_MESH_MSG_CACHE_SIZE=10 +CONFIG_BLE_MESH_ADV_BUF_COUNT=60 +CONFIG_BLE_MESH_TX_SEG_MSG_COUNT=6 +CONFIG_BLE_MESH_RX_SEG_MSG_COUNT=1 +CONFIG_BLE_MESH_RX_SDU_MAX=384 +CONFIG_BLE_MESH_TX_SEG_MAX=32 +CONFIG_BLE_MESH_RELAY=y +CONFIG_BLE_MESH_LOW_POWER= +CONFIG_BLE_MESH_FRIEND= +CONFIG_BLE_MESH_CFG_CLI=y +CONFIG_BTU_TASK_STACK_SIZE=4512 \ No newline at end of file diff --git a/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/CMakeLists.txt b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/CMakeLists.txt new file mode 100644 index 0000000000..6ce28d7d57 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(ble_mesh_console_provisioner) diff --git a/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/Makefile b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/Makefile new file mode 100644 index 0000000000..6c7b6101fa --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/Makefile @@ -0,0 +1,10 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := ble_mesh_console_provisioner + +COMPONENT_ADD_INCLUDEDIRS := components/include + +include $(IDF_PATH)/make/project.mk diff --git a/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/README.md b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/README.md new file mode 100644 index 0000000000..3925d93cf4 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/README.md @@ -0,0 +1,10 @@ +# ble mesh provisioner demo +## Introduction +This demo implements ble mesh provisioner basic features.Based on this demo, provisioner can scan and prove unprovisioned device, send set/get message. Also can define new model. + +Demo steps: +1. Build the ble mesh provisioner demo with sdkconfig.default +2. register provisioner and set oob info, load model to init ble mesh provisioner +3. enable bearer, so that it can scan and prove unprovisioned devices +4. config appkey and other config info use config client model +5. send set/get message to nodes \ No newline at end of file diff --git a/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/CMakeLists.txt b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/CMakeLists.txt new file mode 100644 index 0000000000..339b93fc95 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/CMakeLists.txt @@ -0,0 +1,15 @@ +set(COMPONENT_SRCS "ble_mesh_adapter.c" + "ble_mesh_cfg_srv_model.c" + "ble_mesh_console_lib.c" + "ble_mesh_console_main.c" + "ble_mesh_console_system.c" + "ble_mesh_reg_cfg_client_cmd.c" + "ble_mesh_reg_gen_onoff_client_cmd.c" + "ble_mesh_reg_test_perf_client_cmd.c" + "ble_mesh_register_node_cmd.c" + "ble_mesh_register_provisioner_cmd.c" + "register_bluetooth.c") + +set(COMPONENT_ADD_INCLUDEDIRS ".") + +register_component() diff --git a/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_adapter.c b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_adapter.c new file mode 100644 index 0000000000..5f668718f3 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_adapter.c @@ -0,0 +1,300 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 "esp_ble_mesh_networking_api.h" +#include "ble_mesh_adapter.h" + +esp_ble_mesh_model_t *ble_mesh_get_model(uint16_t model_id) +{ + esp_ble_mesh_model_t *model = NULL; + + switch (model_id) { + case ESP_BLE_MESH_MODEL_ID_CONFIG_SRV: + model = config_server_models; + break; +#if (CONFIG_BLE_MESH_CFG_CLI) + case ESP_BLE_MESH_MODEL_ID_CONFIG_CLI: + model = &gen_onoff_cli_models[1]; + break; +#endif + case ESP_BLE_MESH_MODEL_ID_GEN_ONOFF_SRV: + model = &gen_onoff_srv_models[1]; + break; +#if (CONFIG_BLE_MESH_GENERIC_ONOFF_CLI) + case ESP_BLE_MESH_MODEL_ID_GEN_ONOFF_CLI: + model = &gen_onoff_cli_models[2]; + break; +#endif + case ESP_BLE_MESH_VND_MODEL_ID_TEST_PERF_CLI: + model = &test_perf_cli_models[0]; + break; + case ESP_BLE_MESH_VND_MODEL_ID_TEST_PERF_SRV: + model = &test_perf_srv_models[0]; + break; + } + + return model; +} + +esp_ble_mesh_comp_t *ble_mesh_get_component(uint16_t model_id) +{ + esp_ble_mesh_comp_t *comp = NULL; + + switch (model_id) { + case ESP_BLE_MESH_MODEL_ID_CONFIG_SRV: + comp = &config_server_comp; + break; + case ESP_BLE_MESH_MODEL_ID_CONFIG_CLI: + comp = &config_client_comp; + break; + case ESP_BLE_MESH_MODEL_ID_GEN_ONOFF_SRV: + comp = &gen_onoff_srv_comp; + break; +#if (CONFIG_BLE_MESH_GENERIC_ONOFF_CLI) + case ESP_BLE_MESH_MODEL_ID_GEN_ONOFF_CLI: + comp = &gen_onoff_cli_comp; + break; +#endif + case ESP_BLE_MESH_VND_MODEL_ID_TEST_PERF_CLI: + comp = &test_perf_cli_comp; + break; + case ESP_BLE_MESH_VND_MODEL_ID_TEST_PERF_SRV: + comp = &test_perf_srv_comp; + break; + } + + return comp; +} + +void ble_mesh_node_init() +{ + uint16_t i; + + for (i = 0; i < NODE_MAX_GROUP_CONFIG; i++) { + ble_mesh_node_prestore_params[i].net_idx = 0xFFFF; + ble_mesh_node_prestore_params[i].unicast_addr = 0xFFFF; + } + + ble_mesh_node_sema = xSemaphoreCreateMutex(); + if (!ble_mesh_node_sema) { + ESP_LOGE(TAG, "%s failed to init, failed to create mesh node semaphore", __func__); + } +} + +void ble_mesh_set_node_prestore_params(uint16_t netkey_index, uint16_t unicast_addr) +{ + uint16_t i; + xSemaphoreTake(ble_mesh_node_sema, portMAX_DELAY); + for (i = 0; i < NODE_MAX_GROUP_CONFIG; i++) { + if (ble_mesh_node_prestore_params[i].net_idx != 0xFFFF && ble_mesh_node_prestore_params[i].unicast_addr != 0xFFFF) { + ble_mesh_node_prestore_params[i].net_idx = netkey_index; + ble_mesh_node_prestore_params[i].unicast_addr = unicast_addr; + } + } + xSemaphoreGive(ble_mesh_node_sema); +} + +void ble_mesh_create_send_data(char *data, uint16_t byte_num, uint16_t sequence_num, uint32_t opcode) +{ + uint16_t i; + + // first two bytes are sequence num, third is type + data[0] = sequence_num >> 8; + data[1] = sequence_num & 0xFF; + switch (opcode) { + case ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_GET: + data[2] = VENDOR_MODEL_PERF_OPERATION_TYPE_GET; + break; + case ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_SET: + data[2] = VENDOR_MODEL_PERF_OPERATION_TYPE_SET; + break; + case ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_SET_UNACK: + data[2] = VENDOR_MODEL_PERF_OPERATION_TYPE_SET_UNACK; + break; + } + + for (i = 3; i < byte_num; i++) { + data[i] = i; + } +} + +void ble_mesh_test_performance_client_model_get() +{ + uint32_t i, j; + uint32_t sum_time = 0; + + xSemaphoreTake(ble_mesh_test_perf_sema, portMAX_DELAY); + + for (i = 0, j = 0; i < test_perf_statistics.test_num; i++) { + if (test_perf_statistics.time[i] != 0) { + sum_time += test_perf_statistics.time[i]; + j += 1; + } else { + continue; + } + + if (j == test_perf_statistics.test_num - 1) { + break; + } + } + + ESP_LOGI(TAG, "VendorModel:Statistics,%d,%d\n", + test_perf_statistics.statistics, (sum_time / (j + 1))); + + xSemaphoreGive(ble_mesh_test_perf_sema); +} + +void ble_mesh_test_performance_client_model_get_received_percent() +{ + uint32_t i, j; + uint32_t max_time = 1400; + uint32_t min_time = 0; + uint32_t time_level_num = 0; + typedef struct { + uint16_t time_level; + uint16_t time_num; + } statistics_time_performance; + statistics_time_performance *statistics_time_percent; + + xSemaphoreTake(ble_mesh_test_perf_sema, portMAX_DELAY); + + time_level_num = ((max_time - min_time) / 50 + 1); + statistics_time_percent = malloc(sizeof(statistics_time_performance) * time_level_num); + + for (j = 0; j < time_level_num; j++) { + statistics_time_percent[j].time_level = min_time + 50 * j; + statistics_time_percent[j].time_num = 0; + } + + for (i = 0; i < test_perf_statistics.test_num; i++) { + for (j = 0; j < time_level_num; j++) { + if (test_perf_statistics.time[i] > max_time) { + j -= 1; + break; + } + if (test_perf_statistics.time[i] >= min_time + 50 * j + && test_perf_statistics.time[i] < min_time + 50 * (j + 1)) { + statistics_time_percent[j].time_num += 1; + break; + } + } + } + + // for script match + ESP_LOGI(TAG, "VendorModel:Statistics"); + for (j = 0; j < time_level_num; j++) { + printf(",%d:%d", statistics_time_percent[j].time_level, statistics_time_percent[j].time_num); + } + printf("\n"); + + free(statistics_time_percent); + xSemaphoreGive(ble_mesh_test_perf_sema); +} + +void ble_mesh_test_performance_client_model_accumulate_statistics(uint32_t value) +{ + xSemaphoreTake(ble_mesh_test_perf_sema, portMAX_DELAY); + test_perf_statistics.statistics += value; + xSemaphoreGive(ble_mesh_test_perf_sema); +} + +int ble_mesh_test_performance_client_model_accumulate_time(uint16_t time, uint8_t *data, uint8_t ack_ttl, uint16_t length) +{ + uint16_t i; + uint16_t sequence_num = 0; + uint16_t node_received_ttl = 0; + xSemaphoreTake(ble_mesh_test_perf_sema, portMAX_DELAY); + + // received fail + if (length != test_perf_statistics.test_length) { + xSemaphoreGive(ble_mesh_test_perf_sema); + return 1; + } + + if (data != NULL) { + sequence_num = (data[0] << 8) | data[1]; + if (data[2] == VENDOR_MODEL_PERF_OPERATION_TYPE_SET) { + node_received_ttl = data[3]; + } + } + + for (i = 0; i < test_perf_statistics.test_num; i++) { + if (test_perf_statistics.package_index[i] == sequence_num) { + xSemaphoreGive(ble_mesh_test_perf_sema); + return 1; + } + } + + for (i = 0; i < test_perf_statistics.test_num; i++) { + if (test_perf_statistics.package_index[i] == 0) { + test_perf_statistics.package_index[i] = sequence_num; + if (data[2] == VENDOR_MODEL_PERF_OPERATION_TYPE_SET) { + if (node_received_ttl == test_perf_statistics.ttl && ack_ttl == test_perf_statistics.ttl) { + test_perf_statistics.time[i] = time; + } else { + test_perf_statistics.time[i] = 0; + } + } else if (data[2] == VENDOR_MODEL_PERF_OPERATION_TYPE_SET_UNACK) { + test_perf_statistics.time[i] = time; + } + break; + } + } + + xSemaphoreGive(ble_mesh_test_perf_sema); + return 0; +} + +int ble_mesh_test_performance_client_model_init(uint16_t node_num, uint32_t test_num, uint8_t ttl) +{ + uint16_t i; + + // malloc time + test_perf_statistics.time = malloc(test_num * sizeof(uint16_t)); + if (test_perf_statistics.time == NULL) { + ESP_LOGE(TAG, " %s %d, malloc fail\n", __func__, __LINE__); + return 1; + } + + test_perf_statistics.package_index = malloc(test_num * sizeof(uint16_t)); + if (test_perf_statistics.package_index == NULL) { + ESP_LOGE(TAG, " %s %d, malloc fail\n", __func__, __LINE__); + } + for (i = 0; i < test_num; i++) { + test_perf_statistics.time[i] = 0; + test_perf_statistics.package_index[i] = 0; + } + + test_perf_statistics.test_num = test_num; + test_perf_statistics.node_num = node_num; + test_perf_statistics.ttl = ttl; + test_perf_statistics.statistics = 0; + return 0; +} + +void ble_mesh_test_performance_client_model_destroy() +{ + if (test_perf_statistics.time != NULL) { + free(test_perf_statistics.time); + } + + if (test_perf_statistics.package_index != NULL) { + free(test_perf_statistics.package_index); + } + + test_perf_statistics.test_num = 0; + test_perf_statistics.ttl = 0; + test_perf_statistics.node_num = 0; + test_perf_statistics.statistics = 0; +} diff --git a/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_adapter.h b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_adapter.h new file mode 100644 index 0000000000..87bc7399a4 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_adapter.h @@ -0,0 +1,123 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#ifndef _BLE_MESH_ADAPTER_H_ +#define _BLE_MESH_ADAPTER_H_ + +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" + +#include "ble_mesh_console_lib.h" +#include "ble_mesh_cfg_srv_model.h" + +#define TAG "ble_mesh_prov_console" + +uint64_t start_time; +typedef enum { + VENDOR_MODEL_PERF_OPERATION_TYPE_GET = 1, + VENDOR_MODEL_PERF_OPERATION_TYPE_SET, + VENDOR_MODEL_PERF_OPERATION_TYPE_SET_UNACK +} ble_mesh_perf_operation_type; + +typedef struct { + uint8_t current; + uint8_t previous; + char *name; +} ble_mesh_node_status; + +typedef struct { + bool need_ack; + uint8_t ttl; + uint16_t length; + uint16_t test_num; + uint16_t address; + uint16_t app_idx; + uint16_t net_idx; + uint32_t opcode; + esp_ble_mesh_model_t *model; + esp_ble_mesh_dev_role_t device_role; +} ble_mesh_test_perf_throughput_data; + +typedef struct { + uint32_t statistics; + uint32_t test_num; + uint16_t test_length; + uint16_t node_num; + uint16_t *time; + uint16_t *package_index; + uint8_t ttl; +} ble_mesh_performance_statistics_t; +ble_mesh_performance_statistics_t test_perf_statistics; + +#define SEND_MESSAGE_TIMEOUT (30000/portTICK_RATE_MS) + +extern SemaphoreHandle_t ble_mesh_node_sema; +extern SemaphoreHandle_t ble_mesh_test_perf_send_sema; +extern SemaphoreHandle_t ble_mesh_test_perf_sema; + +#define arg_int_to_value(src_msg, dst_msg, message) do { \ + if (src_msg->count != 0) {\ + ESP_LOGD(TAG, " %s, %s\n", __func__, message);\ + dst_msg = src_msg->ival[0];\ + } \ +} while(0) \ + +#define ble_mesh_node_get_value(index, key, value) do { \ + uint16_t _index = 0; \ + xSemaphoreTake(ble_mesh_node_sema, portMAX_DELAY); \ + for (_index = 0; _index < NODE_MAX_GROUP_CONFIG; _index) { \ + if (node_set_prestore_params[_index].key == value) { \ + break; \ + } \ + } \ + index = _index; \ + xSemaphoreGive(ble_mesh_node_sema); \ +} while(0) \ + +#define ble_mesh_node_set_state(status) do { \ + xSemaphoreTake(ble_mesh_node_sema, portMAX_DELAY); \ + node_status.previous = node_status.current; \ + node_status.current = status; \ + xSemaphoreGive(ble_mesh_node_sema); \ +}while(0) \ + +#define ble_mesh_node_get_state(status) do { \ + xSemaphoreTake(ble_mesh_node_sema, portMAX_DELAY); \ + status = node_status.previous; \ + xSemaphoreGive(ble_mesh_node_sema); \ +}while(0) \ + +#define ble_mesh_callback_check_err_code(err_code, message) do { \ + if (err_code == ESP_OK) { \ + ESP_LOGI(TAG, "%s,OK\n", message); \ + } else { \ + ESP_LOGI(TAG, "%s,Fail,%d\n", message, err_code); \ + } \ +}while(0) \ + +void ble_mesh_node_init(); +void ble_mesh_set_node_prestore_params(uint16_t netkey_index, uint16_t unicast_addr); + +esp_ble_mesh_model_t *ble_mesh_get_model(uint16_t model_id); +esp_ble_mesh_comp_t *ble_mesh_get_component(uint16_t model_id); +void ble_mesh_create_send_data(char *data, uint16_t byte_num, uint16_t sequence_num, uint32_t opcode); + +void ble_mesh_test_performance_client_model_get(); +void ble_mesh_test_performance_client_model_get_received_percent(); +void ble_mesh_test_performance_client_model_accumulate_statistics(uint32_t value); +int ble_mesh_test_performance_client_model_accumulate_time(uint16_t time, uint8_t *data, uint8_t ack_ttl, uint16_t length); +int ble_mesh_test_performance_client_model_init(uint16_t node_num, uint32_t test_num, uint8_t ttl); +void ble_mesh_test_performance_client_model_destroy(); + +#endif //_BLE_MESH_ADAPTER_H_ \ No newline at end of file diff --git a/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_cfg_srv_model.c b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_cfg_srv_model.c new file mode 100644 index 0000000000..9518e74b42 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_cfg_srv_model.c @@ -0,0 +1,205 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 "ble_mesh_cfg_srv_model.h" + +uint8_t dev_uuid[16] = {0xdd, 0xdd}; + +#if CONFIG_BLE_MESH_NODE +esp_ble_mesh_prov_t prov = { + .uuid = dev_uuid, +}; +#endif //CONFIG_BLE_MESH_NODE + +#if CONFIG_BLE_MESH_PROVISIONER +esp_ble_mesh_prov_t prov = { + .prov_uuid = dev_uuid, + .prov_unicast_addr = 0x0001, + .prov_start_address = 0x0005, + .prov_attention = 0x00, + .prov_algorithm = 0x00, + .prov_pub_key_oob = 0x00, + .prov_static_oob_val = NULL, + .prov_static_oob_len = 0x00, + .flags = 0x00, + .iv_index = 0x00, +}; +#endif //CONFIG_BLE_MESH_PROVISIONER + +ESP_BLE_MESH_MODEL_PUB_DEFINE(model_pub_config, 2 + 1, ROLE_PROVISIONER); + +esp_ble_mesh_model_pub_t vendor_model_pub_config; + +// configure server module +esp_ble_mesh_cfg_srv_t cfg_srv = { + .relay = ESP_BLE_MESH_RELAY_ENABLED, + .beacon = ESP_BLE_MESH_BEACON_ENABLED, +#if defined(CONFIG_BLE_MESH_FRIEND) + .friend_state = ESP_BLE_MESH_FRIEND_ENABLED, +#else + .friend_state = ESP_BLE_MESH_FRIEND_NOT_SUPPORTED, +#endif +#if defined(CONFIG_BLE_MESH_GATT_PROXY) + .gatt_proxy = ESP_BLE_MESH_GATT_PROXY_ENABLED, +#else + .gatt_proxy = ESP_BLE_MESH_GATT_PROXY_NOT_SUPPORTED, +#endif + .default_ttl = 7, + + /* 3 transmissions with 20ms interval */ + .net_transmit = ESP_BLE_MESH_TRANSMIT(2, 20), + .relay_retransmit = ESP_BLE_MESH_TRANSMIT(0, 20), +}; + +esp_ble_mesh_model_t config_server_models[] = { + ESP_BLE_MESH_MODEL_CFG_SRV(&cfg_srv), +}; + +esp_ble_mesh_elem_t config_server_elements[] = { + ESP_BLE_MESH_ELEMENT(0, config_server_models, ESP_BLE_MESH_MODEL_NONE), +}; + +esp_ble_mesh_comp_t config_server_comp = { + .cid = CID_ESP, + .elements = config_server_elements, + .element_count = ARRAY_SIZE(config_server_elements), +}; + +// config client model +esp_ble_mesh_model_t config_client_models[] = { + ESP_BLE_MESH_MODEL_CFG_SRV(&cfg_srv), + ESP_BLE_MESH_MODEL_CFG_CLI(&cfg_cli), +}; + +esp_ble_mesh_elem_t config_client_elements[] = { + ESP_BLE_MESH_ELEMENT(0, config_client_models, ESP_BLE_MESH_MODEL_NONE), +}; + +esp_ble_mesh_comp_t config_client_comp = { + .cid = CID_ESP, + .elements = config_client_elements, + .element_count = ARRAY_SIZE(config_client_elements), +}; + +// configure special module +esp_ble_mesh_model_op_t gen_onoff_srv_model_op_config[] = { + ESP_BLE_MESH_MODEL_OP(ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_GET, 0), + ESP_BLE_MESH_MODEL_OP(ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET, 2), + ESP_BLE_MESH_MODEL_OP(ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK, 2), + ESP_BLE_MESH_MODEL_OP_END, +}; + +esp_ble_mesh_model_t gen_onoff_srv_models[] = { + ESP_BLE_MESH_MODEL_CFG_SRV(&cfg_srv), + ESP_BLE_MESH_SIG_MODEL(ESP_BLE_MESH_MODEL_ID_GEN_ONOFF_SRV, gen_onoff_srv_model_op_config, &model_pub_config, NULL), +}; + +esp_ble_mesh_elem_t gen_onoff_srv_elements[] = { + ESP_BLE_MESH_ELEMENT(0, gen_onoff_srv_models, ESP_BLE_MESH_MODEL_NONE), +}; + +esp_ble_mesh_comp_t gen_onoff_srv_comp = { + .cid = CID_ESP, + .elements = gen_onoff_srv_elements, + .element_count = ARRAY_SIZE(gen_onoff_srv_elements), +}; + +// config generic onoff client +#if (CONFIG_BLE_MESH_GENERIC_ONOFF_CLI) + +esp_ble_mesh_client_t gen_onoff_cli; + +esp_ble_mesh_model_t gen_onoff_cli_models[] = { + ESP_BLE_MESH_MODEL_CFG_SRV(&cfg_srv), + ESP_BLE_MESH_MODEL_CFG_CLI(&cfg_cli), + ESP_BLE_MESH_MODEL_GEN_ONOFF_CLI(&model_pub_config, &gen_onoff_cli), +}; + +esp_ble_mesh_elem_t gen_onoff_cli_elements[] = { + ESP_BLE_MESH_ELEMENT(0, gen_onoff_cli_models, ESP_BLE_MESH_MODEL_NONE), +}; + +esp_ble_mesh_comp_t gen_onoff_cli_comp = { + .cid = CID_ESP, + .elements = gen_onoff_cli_elements, + .element_count = ARRAY_SIZE(gen_onoff_cli_elements), +}; +#endif //CONFIG_BLE_MESH_GENERIC_ONOFF_CLI + +//CONFIG VENDOR MODEL TEST PERFORMANCE +#define ESP_BLE_MESH_VND_MODEL_ID_TEST_PERF_SRV 0x2000 +#define ESP_BLE_MESH_VND_MODEL_ID_TEST_PERF_CLI 0x2001 + +#define ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_GET ESP_BLE_MESH_MODEL_OP_3(0x01, CID_ESP) +#define ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_SET ESP_BLE_MESH_MODEL_OP_3(0x02, CID_ESP) +#define ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_SET_UNACK ESP_BLE_MESH_MODEL_OP_3(0x03, CID_ESP) +#define ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_STATUS ESP_BLE_MESH_MODEL_OP_3(0x04, CID_ESP) + +esp_ble_mesh_client_op_pair_t test_perf_cli_op_pair[] = { + {ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_GET, ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_STATUS}, + {ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_SET, ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_STATUS}, + {ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_SET_UNACK, ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_STATUS}, +}; + +esp_ble_mesh_client_t test_perf_cli = { + .op_pair_size = ARRAY_SIZE(test_perf_cli_op_pair), + .op_pair = test_perf_cli_op_pair, +}; + +esp_ble_mesh_model_op_t test_perf_srv_op[] = { + ESP_BLE_MESH_MODEL_OP(ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_GET, 1), + ESP_BLE_MESH_MODEL_OP(ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_SET, 1), + ESP_BLE_MESH_MODEL_OP(ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_SET_UNACK, 1), + ESP_BLE_MESH_MODEL_OP_END, +}; + +esp_ble_mesh_model_op_t test_perf_cli_op[] = { + ESP_BLE_MESH_MODEL_OP(ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_STATUS, 1), + ESP_BLE_MESH_MODEL_OP_END, +}; + +esp_ble_mesh_model_t config_models[] = { + ESP_BLE_MESH_MODEL_CFG_SRV(&cfg_srv), + ESP_BLE_MESH_MODEL_CFG_CLI(&cfg_cli), +}; + +esp_ble_mesh_model_t test_perf_cli_models[] = { + ESP_BLE_MESH_VENDOR_MODEL(CID_ESP, ESP_BLE_MESH_VND_MODEL_ID_TEST_PERF_CLI, + test_perf_cli_op, &vendor_model_pub_config, &test_perf_cli), +}; + +esp_ble_mesh_elem_t test_perf_cli_elements[] = { + ESP_BLE_MESH_ELEMENT(0, config_models, test_perf_cli_models), +}; + +esp_ble_mesh_comp_t test_perf_cli_comp = { + .cid = CID_ESP, + .elements = test_perf_cli_elements, + .element_count = ARRAY_SIZE(test_perf_cli_elements), +}; + +esp_ble_mesh_model_t test_perf_srv_models[] = { + ESP_BLE_MESH_VENDOR_MODEL(CID_ESP, ESP_BLE_MESH_VND_MODEL_ID_TEST_PERF_SRV, + test_perf_srv_op, NULL, NULL), +}; + +esp_ble_mesh_elem_t test_perf_srv_elements[] = { + ESP_BLE_MESH_ELEMENT(0, config_models, test_perf_srv_models), +}; + +esp_ble_mesh_comp_t test_perf_srv_comp = { + .cid = CID_ESP, + .elements = test_perf_srv_elements, + .element_count = ARRAY_SIZE(test_perf_srv_elements), +}; diff --git a/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_cfg_srv_model.h b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_cfg_srv_model.h new file mode 100644 index 0000000000..9e43333eb9 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_cfg_srv_model.h @@ -0,0 +1,107 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#ifndef _BLE_MESH_CFG_SRV_MODEL_H_ +#define _BLE_MESH_CFG_SRV_MODEL_H_ + +#include "esp_ble_mesh_defs.h" +#include "esp_ble_mesh_config_model_api.h" + +#if (CONFIG_BLE_MESH_GENERIC_ONOFF_CLI) +#include "esp_ble_mesh_generic_model_api.h" +#endif //CONFIG_BLE_MESH_GENERIC_ONOFF_CLI + +#define NODE_MAX_GROUP_CONFIG 3 +#define CID_ESP 0x02C4 + +extern uint8_t dev_uuid[16]; + +typedef struct { + uint16_t net_idx; + uint16_t unicast_addr; +} ble_mesh_node_config_params; +ble_mesh_node_config_params ble_mesh_node_prestore_params[NODE_MAX_GROUP_CONFIG]; + +extern esp_ble_mesh_prov_t prov; + +extern esp_ble_mesh_model_pub_t vendor_model_pub_config; + +// configure server module +extern esp_ble_mesh_cfg_srv_t cfg_srv; + +extern esp_ble_mesh_model_t config_server_models[]; + +extern esp_ble_mesh_elem_t config_server_elements[]; + +extern esp_ble_mesh_comp_t config_server_comp; + +// config client model +esp_ble_mesh_client_t cfg_cli; +extern esp_ble_mesh_model_t config_client_models[]; + +extern esp_ble_mesh_elem_t config_client_elements[]; + +extern esp_ble_mesh_comp_t config_client_comp; + +// configure special module +extern esp_ble_mesh_model_op_t gen_onoff_srv_model_op_config[]; + +extern esp_ble_mesh_model_t gen_onoff_srv_models[]; + +extern esp_ble_mesh_elem_t gen_onoff_srv_elements[]; + +extern esp_ble_mesh_comp_t gen_onoff_srv_comp; + +// config generic onoff client +#if (CONFIG_BLE_MESH_GENERIC_ONOFF_CLI) + +extern esp_ble_mesh_client_t gen_onoff_cli; + +extern esp_ble_mesh_model_t gen_onoff_cli_models[]; + +extern esp_ble_mesh_elem_t gen_onoff_cli_elements[]; + +extern esp_ble_mesh_comp_t gen_onoff_cli_comp; +#endif //CONFIG_BLE_MESH_GENERIC_ONOFF_CLI + +//CONFIG VENDOR MODEL TEST PERFORMANCE +#define ESP_BLE_MESH_VND_MODEL_ID_TEST_PERF_SRV 0x2000 +#define ESP_BLE_MESH_VND_MODEL_ID_TEST_PERF_CLI 0x2001 + +#define ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_GET ESP_BLE_MESH_MODEL_OP_3(0x01, CID_ESP) +#define ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_SET ESP_BLE_MESH_MODEL_OP_3(0x02, CID_ESP) +#define ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_SET_UNACK ESP_BLE_MESH_MODEL_OP_3(0x03, CID_ESP) +#define ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_STATUS ESP_BLE_MESH_MODEL_OP_3(0x04, CID_ESP) + +extern esp_ble_mesh_client_t test_perf_cli; + +extern esp_ble_mesh_model_op_t test_perf_srv_op[]; + +extern esp_ble_mesh_model_op_t test_perf_cli_op[]; + +extern esp_ble_mesh_model_t config_models[]; + +extern esp_ble_mesh_model_t test_perf_cli_models[]; + +extern esp_ble_mesh_elem_t test_perf_cli_elements[]; + +extern esp_ble_mesh_comp_t test_perf_cli_comp; + +extern esp_ble_mesh_model_t test_perf_srv_models[]; + +extern esp_ble_mesh_elem_t test_perf_srv_elements[]; + +extern esp_ble_mesh_comp_t test_perf_srv_comp; + +#endif //_BLE_MESH_CFG_SRV_MODEL_H_ diff --git a/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_console_decl.h b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_console_decl.h new file mode 100644 index 0000000000..90e52a61d9 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_console_decl.h @@ -0,0 +1,38 @@ +/* Console example — declarations of command registration functions. + + This example code is in the Public Domain (or CC0 licensed, at your option). + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#pragma once + +#include "esp_ble_mesh_defs.h" + +// Register system functions +void register_system(); + +// Register bluetooth +void register_bluetooth(); + +// Register mesh node cmd +void ble_mesh_register_mesh_node(); + +// Register Test Perf client cmd +void ble_mesh_register_mesh_test_performance_client(); + +#if (CONFIG_BLE_MESH_CFG_CLI) +// Register mesh config client operation cmd +void ble_mesh_register_configuration_client_model(); +#endif + +#if (CONFIG_BLE_MESH_GENERIC_ONOFF_CLI) +// Register mesh client operation cmd +void ble_mesh_register_gen_onoff_client(); +#endif + +#if CONFIG_BLE_MESH_PROVISIONER +// Regitster mesh provisioner cmd +void ble_mesh_register_mesh_provisioner(); +#endif diff --git a/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_console_lib.c b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_console_lib.c new file mode 100644 index 0000000000..d15ee3adc1 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_console_lib.c @@ -0,0 +1,124 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 "ble_mesh_console_lib.h" + +static int hex2num(char c); +static int hex2byte(const char *hex); + +static int hex2num(char c) +{ + if (c >= '0' && c <= '9') { + return c - '0'; + } + if (c >= 'a' && c <= 'f') { + return c - 'a' + 10; + } + if (c >= 'A' && c <= 'F') { + return c - 'A' + 10; + } + return -1; +} + +static int hex2byte(const char *hex) +{ + int a, b; + a = hex2num(*hex++); + if (a < 0) { + return -1; + } + b = hex2num(*hex++); + if (b < 0) { + return -1; + } + return (a << 4) | b; +} + +int hexstr_2_bin(const char *hex, uint8_t *buf, uint32_t len) +{ + uint32_t i; + int a; + const char *ipos = hex; + uint8_t *opos = buf; + + for (i = 0; i < len; i++) { + a = hex2byte(ipos); + if (a < 0) { + return -1; + } + *opos ++ = a; + ipos += 2; + } + return 0; +} + +int get_value_string(char *value_in, char *buf) +{ + int result = -1; + + uint16_t length = strlen(value_in); + + if (length > 2) { + if (value_in[0] == '0' && value_in[1] == 'x') { + buf[(length - 2) / 2] = 0; + result = hexstr_2_bin(&value_in[2], (uint8_t *)buf, (length - 2) / 2); + length = (length - 2) / 2; + } else { + strcpy(buf, value_in); + result = 0; + } + } else { + strcpy(buf, value_in); + result = 0; + } + return result; +} + +bool str_2_mac(uint8_t *str, uint8_t *dest) +{ + uint8_t loop = 0; + uint8_t tmp = 0; + uint8_t *src_p = str; + + if (strlen((char *)src_p) != 17) { // must be like 12:34:56:78:90:AB + return false; + } + + for (loop = 0; loop < 17 ; loop++) { + if (loop % 3 == 2) { + if (src_p[loop] != ':') { + return false; + } + + continue; + } + + if ((src_p[loop] >= '0') && (src_p[loop] <= '9')) { + tmp = tmp * 16 + src_p[loop] - '0'; + } else if ((src_p[loop] >= 'A') && (src_p[loop] <= 'F')) { + tmp = tmp * 16 + src_p[loop] - 'A' + 10; + } else if ((src_p[loop] >= 'a') && (src_p[loop] <= 'f')) { + tmp = tmp * 16 + src_p[loop] - 'a' + 10; + } else { + return false; + } + + if (loop % 3 == 1) { + *dest++ = tmp; + tmp = 0; + } + } + + return true; +} diff --git a/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_console_lib.h b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_console_lib.h new file mode 100644 index 0000000000..11dd05cb37 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_console_lib.h @@ -0,0 +1,29 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#ifndef _BLE_MESH_CONSOLE_LIB_H_ +#define _BLE_MESH_CONSOLE_LIB_H_ + +#include +#include + +#include "esp_system.h" +#include "esp_console.h" +#include "argtable3/argtable3.h" + +bool str_2_mac(uint8_t *str, uint8_t *dest); +int hexstr_2_bin(const char *hex, uint8_t *buf, uint32_t len); +int get_value_string(char *value_in, char *buf); + +#endif //_BLE_MESH_CONSOLE_LIB_H_ \ No newline at end of file diff --git a/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_console_main.c b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_console_main.c new file mode 100644 index 0000000000..f7c4eacf6b --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_console_main.c @@ -0,0 +1,228 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 + +#include "esp_system.h" +#include "esp_log.h" +#include "esp_console.h" +#include "esp_vfs_dev.h" +#include "driver/uart.h" +#include "linenoise/linenoise.h" +#include "argtable3/argtable3.h" + +#include "esp_vfs_fat.h" +#include "nvs.h" +#include "nvs_flash.h" + +#include "esp_bt.h" +#include "esp_bt_main.h" + +#include "ble_mesh_console_decl.h" + +#define TAG "ble_mesh_test" + +#if CONFIG_STORE_HISTORY + +#define MOUNT_PATH "/data" +#define HISTORY_PATH MOUNT_PATH "/history.txt" + +static void initialize_filesystem() +{ + static wl_handle_t wl_handle; + const esp_vfs_fat_mount_config_t mount_config = { + .max_files = 4, + .format_if_mount_failed = true + }; + esp_err_t err = esp_vfs_fat_spiflash_mount(MOUNT_PATH, "storage", &mount_config, &wl_handle); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to mount FATFS (0x%x)", err); + return; + } +} +#endif // CONFIG_STORE_HISTORY + +static void initialize_console() +{ + /* Disable buffering on stdin and stdout */ + setvbuf(stdin, NULL, _IONBF, 0); + setvbuf(stdout, NULL, _IONBF, 0); + + /* Minicom, screen, idf_monitor send CR when ENTER key is pressed */ + esp_vfs_dev_uart_set_rx_line_endings(ESP_LINE_ENDINGS_CR); + /* Move the caret to the beginning of the next line on '\n' */ + esp_vfs_dev_uart_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF); + + /* Install UART driver for interrupt-driven reads and writes */ + ESP_ERROR_CHECK( uart_driver_install(CONFIG_CONSOLE_UART_NUM, + 256, 0, 0, NULL, 0) ); + + /* Tell VFS to use UART driver */ + esp_vfs_dev_uart_use_driver(CONFIG_CONSOLE_UART_NUM); + + /* Initialize the console */ + esp_console_config_t console_config = { + .max_cmdline_args = 20, + .max_cmdline_length = 256, +#if CONFIG_LOG_COLORS + .hint_color = atoi(LOG_COLOR_CYAN) +#endif + }; + ESP_ERROR_CHECK( esp_console_init(&console_config) ); + + /* Configure linenoise line completion library */ + /* Enable multiline editing. If not set, long commands will scroll within + * a single line. + */ + linenoiseSetMultiLine(1); + + /* Tell linenoise where to get command completions and hints */ + linenoiseSetCompletionCallback(&esp_console_get_completion); + linenoiseSetHintsCallback((linenoiseHintsCallback *) &esp_console_get_hint); + + /* Set command history size */ + linenoiseHistorySetMaxLen(100); + +#if CONFIG_STORE_HISTORY + /* Load command history from filesystem */ + linenoiseHistoryLoad(HISTORY_PATH); +#endif +} + + +esp_err_t bluetooth_init(void) +{ + esp_err_t ret; + + esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + ret = esp_bt_controller_init(&bt_cfg); + if (ret) { + ESP_LOGE(TAG, "%s failed to initialize controller\n", __func__); + return ret; + } + + ret = esp_bt_controller_enable(ESP_BT_MODE_BLE); + if (ret) { + ESP_LOGE(TAG, "%s failed to enable controller\n", __func__); + return ret; + } + ret = esp_bluedroid_init(); + if (ret) { + ESP_LOGE(TAG, "%s failed to initialize bluetooth\n", __func__); + return ret; + } + ret = esp_bluedroid_enable(); + if (ret) { + ESP_LOGE(TAG, "%s failed to enable bluetooth\n", __func__); + return ret; + } + + return ret; +} + +void app_main(void) +{ + esp_err_t res; + + nvs_flash_init(); + + // init and enable bluetooth + res = bluetooth_init(); + if (res) { + printf("esp32_bluetooth_init failed (ret %d)", res); + } + +#if CONFIG_STORE_HISTORY + initialize_filesystem(); +#endif + + initialize_console(); + + /* Register commands */ + esp_console_register_help_command(); + register_system(); + register_bluetooth(); + ble_mesh_register_mesh_node(); + ble_mesh_register_mesh_test_performance_client(); +#if (CONFIG_BLE_MESH_GENERIC_ONOFF_CLI) + ble_mesh_register_gen_onoff_client(); +#endif +#if (CONFIG_BLE_MESH_PROVISIONER) + ble_mesh_register_mesh_provisioner(); +#endif +#if (CONFIG_BLE_MESH_CFG_CLI) + ble_mesh_register_configuration_client_model(); +#endif + + + /* Prompt to be printed before each line. + * This can be customized, made dynamic, etc. + */ + const char *prompt = LOG_COLOR_I "esp32> " LOG_RESET_COLOR; + + printf("\n" + "This is an example of an ESP-IDF console component.\n" + "Type 'help' to get the list of commands.\n" + "Use UP/DOWN arrows to navigate through the command history.\n" + "Press TAB when typing a command name to auto-complete.\n"); + + /* Figure out if the terminal supports escape sequences */ + int probe_status = linenoiseProbe(); + if (probe_status) { /* zero indicates OK */ + printf("\n" + "Your terminal application does not support escape sequences.\n" + "Line editing and history features are disabled.\n" + "On Windows, try using Putty instead.\n"); + linenoiseSetDumbMode(1); +#if CONFIG_LOG_COLORS + /* Since the terminal doesn't support escape sequences, + * don't use color codes in the prompt. + */ + prompt = "esp32> "; +#endif //CONFIG_LOG_COLORS + } + + /* Main loop */ + while (true) { + /* Get a line using linenoise. + * The line is returned when ENTER is pressed. + */ + char *line = linenoise(prompt); + if (line == NULL) { /* Ignore empty lines */ + continue; + } + /* Add the command to the history */ + linenoiseHistoryAdd(line); +#if CONFIG_STORE_HISTORY + /* Save command history to filesystem */ + linenoiseHistorySave(HISTORY_PATH); +#endif + + /* Try to run the command */ + int ret; + esp_err_t err = esp_console_run(line, &ret); + if (err == ESP_ERR_NOT_FOUND) { + printf("Unrecognized command\n"); + } else if (err == ESP_ERR_INVALID_ARG) { + // command was empty + } else if (err == ESP_OK && ret != ESP_OK) { + printf("\nCommand returned non-zero error code: 0x%x\n", ret); + } else if (err != ESP_OK) { + printf("Internal error: 0x%x\n", err); + } + /* linenoise allocates line buffer on the heap, so need to free it */ + linenoiseFree(line); + } +} diff --git a/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_console_system.c b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_console_system.c new file mode 100644 index 0000000000..d3dc6c12f9 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_console_system.c @@ -0,0 +1,179 @@ +/* Console example — various system commands + + This example code is in the Public Domain (or CC0 licensed, at your option). + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include +#include +#include + +#include "esp_log.h" +#include "esp_console.h" +#include "esp_system.h" +#include "esp_sleep.h" +#include "driver/rtc_io.h" +#include "argtable3/argtable3.h" + +#include "ble_mesh_console_decl.h" + +#if CONFIG_IDF_CMAKE +#define CONFIG_ESPTOOLPY_PORT "Which is choosen by Users for CMake" +#endif + +static void register_free(); +static void register_restart(); +static void register_make(); + +void register_system() +{ + register_free(); + register_restart(); + register_make(); +} + +/** 'restart' command restarts the program */ + +static int restart(int argc, char **argv) +{ + printf("%s, %s", __func__, "Restarting"); + esp_restart(); +} + +static void register_restart() +{ + const esp_console_cmd_t cmd = { + .command = "restart", + .help = "Restart the program", + .hint = NULL, + .func = &restart, + }; + ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) ); +} + +/** 'free' command prints available heap memory */ + +static int free_mem(int argc, char **argv) +{ + printf("%d\n", esp_get_free_heap_size()); + return 0; +} + +static void register_free() +{ + const esp_console_cmd_t cmd = { + .command = "free", + .help = "Get the total size of heap memory available", + .hint = NULL, + .func = &free_mem, + }; + ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) ); +} + +static int make(int argc, char **argv) +{ + int count = REG_READ(RTC_CNTL_STORE0_REG); + if (++count >= 3) { + printf("This is not the console you are looking for.\n"); + return 0; + } + REG_WRITE(RTC_CNTL_STORE0_REG, count); + + const char *make_output = + R"(LD build/console.elf +esptool.py v2.1-beta1 +)"; + + const char* flash_output[] = { +R"(Flashing binaries to serial port )" CONFIG_ESPTOOLPY_PORT R"( (app at offset 0x10000)... +esptool.py v2.1-beta1 +Connecting.... +)", +R"(Chip is ESP32D0WDQ6 (revision 0) +Uploading stub... +Running stub... +Stub running... +Changing baud rate to 921600 +Changed. +Configuring flash size... +Auto-detected Flash size: 4MB +Flash params set to 0x0220 +Compressed 15712 bytes to 9345... +)", +R"(Wrote 15712 bytes (9345 compressed) at 0x00001000 in 0.1 seconds (effective 1126.9 kbit/s)... +Hash of data verified. +Compressed 333776 bytes to 197830... +)", +R"(Wrote 333776 bytes (197830 compressed) at 0x00010000 in 3.3 seconds (effective 810.3 kbit/s)... +Hash of data verified. +Compressed 3072 bytes to 82... +)", +R"(Wrote 3072 bytes (82 compressed) at 0x00008000 in 0.0 seconds (effective 1588.4 kbit/s)... +Hash of data verified. +Leaving... +Hard resetting... +)" + }; + + const char* monitor_output = +R"(MONITOR +)" LOG_COLOR_W R"(--- idf_monitor on )" CONFIG_ESPTOOLPY_PORT R"( 115200 --- +--- Quit: Ctrl+] | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H -- +)" LOG_RESET_COLOR; + + bool need_make = false; + bool need_flash = false; + bool need_monitor = false; + for (int i = 1; i < argc; ++i) { + if (strcmp(argv[i], "all") == 0) { + need_make = true; + } else if (strcmp(argv[i], "flash") == 0) { + need_make = true; + need_flash = true; + } else if (strcmp(argv[i], "monitor") == 0) { + need_monitor = true; + } else if (argv[i][0] == '-') { + /* probably -j option */ + } else if (isdigit((int) argv[i][0])) { + /* might be an argument to -j */ + } else { + printf("make: *** No rule to make target `%s'. Stop.\n", argv[i]); + /* Technically this is an error, but let's not spoil the output */ + return 0; + } + } + if (argc == 1) { + need_make = true; + } + if (need_make) { + printf("%s", make_output); + } + if (need_flash) { + size_t n_items = sizeof(flash_output) / sizeof(flash_output[0]); + for (int i = 0; i < n_items; ++i) { + printf("%s", flash_output[i]); + vTaskDelay(200/portTICK_PERIOD_MS); + } + } + if (need_monitor) { + printf("%s", monitor_output); + esp_restart(); + } + return 0; +} + +static void register_make() +{ + const esp_console_cmd_t cmd = { + .command = "make", + .help = NULL, /* Do not include in 'help' output */ + .hint = "all | flash | monitor", + .func = &make, + }; + ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) ); +} + + diff --git a/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_reg_cfg_client_cmd.c b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_reg_cfg_client_cmd.c new file mode 100644 index 0000000000..fd2852de82 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_reg_cfg_client_cmd.c @@ -0,0 +1,390 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 "esp_ble_mesh_networking_api.h" +#include "ble_mesh_adapter.h" + +#if (CONFIG_BLE_MESH_CFG_CLI) +typedef struct { + struct arg_str *action_type; + struct arg_str *set_state; + struct arg_int *opcode; + struct arg_int *unicast_address; + struct arg_int *appkey_index; + struct arg_int *mod_id; + struct arg_int *addr; + struct arg_int *cid; + struct arg_int *value; + struct arg_int *relay_statue; + struct arg_int *relay_transmit; + struct arg_int *net_idx; + struct arg_end *end; +} ble_mesh_client_get_set_state_t; +ble_mesh_client_get_set_state_t configuration_client_model_operation; + +void ble_mesh_register_configuration_client_model_command(); +void ble_mesh_configuration_client_model_cb(esp_ble_mesh_cfg_client_cb_event_t event, + esp_ble_mesh_cfg_client_cb_param_t *param); + +void ble_mesh_register_configuration_client_model() +{ + ble_mesh_register_configuration_client_model_command(); +} + +void ble_mesh_configuration_client_model_cb(esp_ble_mesh_cfg_client_cb_event_t event, + esp_ble_mesh_cfg_client_cb_param_t *param) +{ + uint32_t opcode; + ESP_LOGD(TAG, "enter %s, event = %x\n, error_code = %x\n", __func__, event, param->error_code); + + if (!param->error_code) { + opcode = param->params->opcode; + switch (event) { + case ESP_BLE_MESH_CFG_CLIENT_GET_STATE_EVT: + switch (opcode) { + case ESP_BLE_MESH_MODEL_OP_BEACON_GET: + ESP_LOGI(TAG, "CfgClient:beacon,0x%x", param->status_cb.beacon_status.beacon); + break; + case ESP_BLE_MESH_MODEL_OP_COMPOSITION_DATA_GET: + ESP_LOGI(TAG, "CfgClient:page,0x%x,len,0x%x", param->status_cb.comp_data_status.page, param->status_cb.comp_data_status.composition_data->len); + break; + case ESP_BLE_MESH_MODEL_OP_DEFAULT_TTL_GET: + ESP_LOGI(TAG, "CfgClient:ttl,0x%x", param->status_cb.default_ttl_status.default_ttl); + break; + case ESP_BLE_MESH_MODEL_OP_GATT_PROXY_GET: + ESP_LOGI(TAG, "CfgClient:proxy,0x%x", param->status_cb.gatt_proxy_status.gatt_proxy); + break; + case ESP_BLE_MESH_MODEL_OP_RELAY_GET: + ESP_LOGI(TAG, "CfgClient:relay,0x%x,retransmit,0x%x", param->status_cb.relay_status.relay, param->status_cb.relay_status.retransmit); + break; + case ESP_BLE_MESH_MODEL_OP_MODEL_PUB_GET: + if (param->status_cb.model_pub_status.status == ESP_OK) { + ESP_LOGI(TAG, "CfgClient:PublishGet,OK,0x%x", param->status_cb.model_pub_status.publish_addr); + } else { + ESP_LOGI(TAG, "CfgClient:PublishGet,Fail"); + } + + break; + case ESP_BLE_MESH_MODEL_OP_FRIEND_GET: + ESP_LOGI(TAG, "CfgClient:friend,0x%x", param->status_cb.friend_status.friend_state); + break; + case ESP_BLE_MESH_MODEL_OP_HEARTBEAT_PUB_GET: + if (param->status_cb.heartbeat_pub_status.status == ESP_OK) { + ESP_LOGI(TAG, "CfgClient:HeartBeatPubGet,OK,destination:0x%x,countlog:0x%x,periodlog:0x%x,ttl:0x%x,features:0x%x,net_idx:0x%x", + param->status_cb.heartbeat_pub_status.dst, param->status_cb.heartbeat_pub_status.count, param->status_cb.heartbeat_pub_status.period, + param->status_cb.heartbeat_pub_status.ttl, param->status_cb.heartbeat_pub_status.features, param->status_cb.heartbeat_pub_status.net_idx); + } else { + ESP_LOGI(TAG, "CfgClient:HeartBeatGet,Fail,%d", param->status_cb.heartbeat_pub_status.status); + } + break; + case ESP_BLE_MESH_MODEL_OP_HEARTBEAT_SUB_GET: + if (param->status_cb.heartbeat_sub_status.status == ESP_OK) { + ESP_LOGI(TAG, "CfgClient:HeartBeatSubGet,OK,source:0x%x,destination:0x%x, periodlog:0x%x,countlog:0x%x,minhops:0x%x,maxhops:0x%x", + param->status_cb.heartbeat_sub_status.src, param->status_cb.heartbeat_sub_status.dst, param->status_cb.heartbeat_sub_status.period, + param->status_cb.heartbeat_sub_status.count, param->status_cb.heartbeat_sub_status.min_hops, param->status_cb.heartbeat_sub_status.max_hops); + } else { + ESP_LOGI(TAG, "CfgClient:HeartBeatSubGet,Fail,%d", param->status_cb.heartbeat_sub_status.status); + } + break; + default: + ESP_LOGI(TAG, "Not supported config client get message opcode"); + break; + } + break; + case ESP_BLE_MESH_CFG_CLIENT_SET_STATE_EVT: + switch (opcode) { + case ESP_BLE_MESH_MODEL_OP_BEACON_SET: + ESP_LOGI(TAG, "CfgClient:beacon,0x%x", param->status_cb.beacon_status.beacon); + break; + case ESP_BLE_MESH_MODEL_OP_DEFAULT_TTL_SET: + ESP_LOGI(TAG, "CfgClient:ttl,0x%x", param->status_cb.default_ttl_status.default_ttl); + break; + case ESP_BLE_MESH_MODEL_OP_GATT_PROXY_SET: + ESP_LOGI(TAG, "CfgClient:proxy,0x%x", param->status_cb.gatt_proxy_status.gatt_proxy); + break; + case ESP_BLE_MESH_MODEL_OP_RELAY_SET: + ESP_LOGI(TAG, "CfgClient:relay,0x%x, retransmit: 0x%x", param->status_cb.relay_status.relay, param->status_cb.relay_status.retransmit); + break; + case ESP_BLE_MESH_MODEL_OP_MODEL_PUB_SET: + if (param->status_cb.model_pub_status.status == ESP_OK) { + ESP_LOGI(TAG, "CfgClient:PublishSet,OK,0x%x", param->status_cb.model_pub_status.publish_addr); + } else { + ESP_LOGI(TAG, "CfgClient:PublishSet,Fail"); + } + break; + case ESP_BLE_MESH_MODEL_OP_MODEL_SUB_ADD: + if (param->status_cb.model_sub_status.status == ESP_OK) { + ESP_LOGI(TAG, "CnfClient:SubAdd,OK,%x,%x", param->status_cb.model_sub_status.element_addr, param->status_cb.model_sub_status.sub_addr); + } else { + ESP_LOGI(TAG, "CnfClient:SubAdd,Fail,%x", param->status_cb.model_sub_status.status); + } + break; + case ESP_BLE_MESH_MODEL_OP_MODEL_SUB_DELETE: + if (param->status_cb.model_sub_status.status == ESP_OK) { + ESP_LOGI(TAG, "CnfClient:SubDel,OK,%x,%x", param->status_cb.model_sub_status.element_addr, param->status_cb.model_sub_status.sub_addr); + } else { + ESP_LOGI(TAG, "CnfClient:SubDel,Fail,%x", param->status_cb.model_sub_status.status); + } + break; + case ESP_BLE_MESH_MODEL_OP_MODEL_SUB_OVERWRITE: + break; + case ESP_BLE_MESH_MODEL_OP_MODEL_SUB_VIRTUAL_ADDR_ADD: + break; + case ESP_BLE_MESH_MODEL_OP_MODEL_SUB_VIRTUAL_ADDR_DELETE: + break; + case ESP_BLE_MESH_MODEL_OP_MODEL_SUB_VIRTUAL_ADDR_OVERWRITE: + break; + case ESP_BLE_MESH_MODEL_OP_NET_KEY_ADD: + if (param->status_cb.netkey_status.status == ESP_OK) { + ESP_LOGI(TAG, "CfgClient:NetKeyAdd,OK"); + } else { + ESP_LOGI(TAG, "CfgClient:NetKeyAdd,Fail,%d", param->status_cb.netkey_status.status); + } + break; + case ESP_BLE_MESH_MODEL_OP_APP_KEY_ADD: + if (param->status_cb.appkey_status.status == ESP_OK) { + ESP_LOGI(TAG, "CnfClient:AddAppkey,OK,%x,%x,%x", param->status_cb.appkey_status.net_idx, param->status_cb.appkey_status.app_idx, param->params->ctx.addr); + } else { + ESP_LOGI(TAG, "CnfClient:AddAppkey,Fail,%x", param->status_cb.appkey_status.status); + } + break; + case ESP_BLE_MESH_MODEL_OP_MODEL_APP_BIND: + if (param->status_cb.model_app_status.status == ESP_OK) { + ESP_LOGI(TAG, "CnfClient:AppkeyBind,OK,%x,%x,%x", param->status_cb.model_app_status.app_idx, param->status_cb.model_app_status.model_id, param->params->ctx.addr); + } else { + ESP_LOGI(TAG, "CnfClient:AppkeyBind,Fail,%x", param->status_cb.model_app_status.status); + } + break; + case ESP_BLE_MESH_MODEL_OP_FRIEND_SET: + ESP_LOGI(TAG, "CfgClient:friend: 0x%x", param->status_cb.friend_status.friend_state); + break; + case ESP_BLE_MESH_MODEL_OP_HEARTBEAT_PUB_SET: + if (param->status_cb.heartbeat_pub_status.status == ESP_OK) { + ESP_LOGI(TAG, "CfgClient:HeartBeatPubSet,OK,destination:0x%x,countlog:0x%x, periodlog:0x%x,ttl:0x%x,features:0x%x,net_idx: 0x%x", + param->status_cb.heartbeat_pub_status.dst, param->status_cb.heartbeat_pub_status.count, param->status_cb.heartbeat_pub_status.period, + param->status_cb.heartbeat_pub_status.ttl, param->status_cb.heartbeat_pub_status.features, param->status_cb.heartbeat_pub_status.net_idx); + } else { + ESP_LOGI(TAG, "CfgClient:HeartBeatSet,Fail,%d", param->status_cb.heartbeat_pub_status.status); + } + break; + case ESP_BLE_MESH_MODEL_OP_HEARTBEAT_SUB_SET: + if (param->status_cb.heartbeat_sub_status.status == ESP_OK) { + ESP_LOGI(TAG, "CfgClient:HeartBeatSubSet,OK,source:0x%x,destination:0x%x, periodlog:0x%x,countlog:0x%x,minhops:0x%x,maxhops:0x%x", + param->status_cb.heartbeat_sub_status.src, param->status_cb.heartbeat_sub_status.dst, param->status_cb.heartbeat_sub_status.period, + param->status_cb.heartbeat_sub_status.count, param->status_cb.heartbeat_sub_status.min_hops, param->status_cb.heartbeat_sub_status.max_hops); + } else { + ESP_LOGI(TAG, "CfgClient:HeartBeatSubSet,Fail,%d", param->status_cb.heartbeat_sub_status.status); + } + break; + default: + ESP_LOGI(TAG, "Not supported config client set message opcode"); + break; + } + break; + case ESP_BLE_MESH_CFG_CLIENT_PUBLISH_EVT: + ESP_LOGI(TAG, "CnfClient:Publish,OK"); + break; + case ESP_BLE_MESH_CFG_CLIENT_EVT_MAX: + ESP_LOGI(TAG, "CnfClient:MaxEvt"); + break; + case ESP_BLE_MESH_CFG_CLIENT_TIMEOUT_EVT: + ESP_LOGI(TAG, "CfgClient:TimeOut"); + break; + default: + ESP_LOGI(TAG, "CfgClient:InvalidEvent"); + break; + } + } else { + ESP_LOGI(TAG, "CnfClient:Fail,%d", param->error_code); + } + ESP_LOGD(TAG, "exit %s \n", __func__); +} + +int ble_mesh_configuration_client_model_operation(int argc, char **argv) +{ + int err = ESP_OK; + const uint8_t *app_key = NULL; + esp_ble_mesh_cfg_default_ttl_set_t ttl_set; + esp_ble_mesh_cfg_gatt_proxy_set_t proxy_set; + esp_ble_mesh_cfg_app_key_add_t app_key_add; + esp_ble_mesh_cfg_model_pub_set_t mod_pub_set = { + .company_id = 0xFFFF, + .cred_flag = false, + .publish_period = 0, + .publish_retransmit = 0, + }; + esp_ble_mesh_cfg_model_sub_add_t mod_sub_add = { + .company_id = 0xFFFF, + }; + esp_ble_mesh_cfg_model_sub_delete_t mod_sub_del = { + .company_id = 0xFFFF, + }; + esp_ble_mesh_cfg_relay_set_t relay_set; + esp_ble_mesh_client_common_param_t client_common = { + .msg_role = ROLE_PROVISIONER, + .msg_timeout = 0, + .ctx.send_ttl = 7, + }; + esp_ble_mesh_cfg_client_get_state_t get_state = { + .comp_data_get.page = 0, + .model_pub_get.company_id = 0xFFFF, + }; + esp_ble_mesh_cfg_model_app_bind_t mod_app_bind = { + .company_id = 0xFFFF, + }; + + client_common.model = ble_mesh_get_model(ESP_BLE_MESH_MODEL_ID_CONFIG_CLI); + + ESP_LOGD(TAG, "enter %s \n", __func__); + + int nerrors = arg_parse(argc, argv, (void **) &configuration_client_model_operation); + if (nerrors != 0) { + arg_print_errors(stderr, configuration_client_model_operation.end, argv[0]); + return 1; + } + + if (configuration_client_model_operation.opcode->count != 0) { + client_common.opcode = configuration_client_model_operation.opcode->ival[0]; + } + + if (configuration_client_model_operation.net_idx->count != 0) { + client_common.ctx.net_idx = configuration_client_model_operation.net_idx->ival[0]; + app_key_add.net_idx = configuration_client_model_operation.net_idx->ival[0]; + } + + if (configuration_client_model_operation.unicast_address->count != 0) { + client_common.ctx.addr = configuration_client_model_operation.unicast_address->ival[0]; + get_state.model_pub_get.element_addr = configuration_client_model_operation.unicast_address->ival[0]; + mod_app_bind.element_addr = configuration_client_model_operation.unicast_address->ival[0]; + mod_sub_add.element_addr = configuration_client_model_operation.unicast_address->ival[0]; + mod_sub_del.element_addr = configuration_client_model_operation.unicast_address->ival[0]; + mod_pub_set.element_addr = configuration_client_model_operation.unicast_address->ival[0]; + } + + if (configuration_client_model_operation.appkey_index->count != 0) { + client_common.ctx.app_idx = configuration_client_model_operation.appkey_index->ival[0]; + mod_app_bind.model_app_idx = configuration_client_model_operation.appkey_index->ival[0]; + app_key_add.app_idx = configuration_client_model_operation.appkey_index->ival[0]; + mod_pub_set.publish_app_idx = configuration_client_model_operation.appkey_index->ival[0]; + } + + if (configuration_client_model_operation.value->count != 0) { + ttl_set.ttl = configuration_client_model_operation.value->ival[0]; + proxy_set.gatt_proxy = configuration_client_model_operation.value->ival[0]; + mod_pub_set.publish_ttl = configuration_client_model_operation.value->ival[0]; + } + + if (configuration_client_model_operation.addr->count != 0) { + mod_sub_del.sub_addr = configuration_client_model_operation.addr->ival[0]; + mod_sub_add.sub_addr = configuration_client_model_operation.addr->ival[0]; + mod_pub_set.publish_addr = configuration_client_model_operation.addr->ival[0]; + } + + if (configuration_client_model_operation.mod_id->count != 0) { + mod_app_bind.model_id = configuration_client_model_operation.mod_id->ival[0]; + mod_sub_add.model_id = configuration_client_model_operation.mod_id->ival[0]; + mod_sub_del.model_id = configuration_client_model_operation.mod_id->ival[0]; + get_state.model_pub_get.model_id = configuration_client_model_operation.mod_id->ival[0];; + mod_pub_set.model_id = configuration_client_model_operation.mod_id->ival[0]; + } + + if (configuration_client_model_operation.relay_statue->count != 0) { + relay_set.relay = configuration_client_model_operation.relay_statue->ival[0]; + mod_pub_set.publish_period = configuration_client_model_operation.relay_statue->ival[0]; + } + + if (configuration_client_model_operation.relay_transmit->count != 0) { + relay_set.relay_retransmit = configuration_client_model_operation.relay_transmit->ival[0]; + mod_pub_set.publish_retransmit = configuration_client_model_operation.relay_transmit->ival[0]; + } + + if (configuration_client_model_operation.cid->count != 0) { + mod_app_bind.company_id = configuration_client_model_operation.cid->ival[0]; + mod_sub_del.company_id = configuration_client_model_operation.cid->ival[0]; + mod_sub_add.company_id = configuration_client_model_operation.cid->ival[0]; + mod_pub_set.company_id = configuration_client_model_operation.cid->ival[0]; + } + + if (configuration_client_model_operation.action_type->count != 0) { + if (strcmp(configuration_client_model_operation.action_type->sval[0], "get") == 0) { + err = esp_ble_mesh_config_client_get_state(&client_common, &get_state); + } else if (strcmp(configuration_client_model_operation.action_type->sval[0], "set") == 0) { + if (configuration_client_model_operation.set_state->count != 0) { + if (strcmp(configuration_client_model_operation.set_state->sval[0], "appkey") == 0) { + app_key = esp_ble_mesh_provisioner_get_local_app_key(app_key_add.net_idx, app_key_add.app_idx); + if (app_key == NULL) { + ESP_LOGE(TAG, "CnfClient:AddAppkey,Fail,app key or network key NULL"); + return ESP_FAIL; + } else { + memcpy(app_key_add.app_key, app_key, 16); + } + err = esp_ble_mesh_config_client_set_state(&client_common, (esp_ble_mesh_cfg_client_set_state_t *)&app_key_add); + } else if (strcmp(configuration_client_model_operation.set_state->sval[0], "appbind") == 0) { + err = esp_ble_mesh_config_client_set_state(&client_common, (esp_ble_mesh_cfg_client_set_state_t *)&mod_app_bind); + } else if (strcmp(configuration_client_model_operation.set_state->sval[0], "ttl") == 0) { + err = esp_ble_mesh_config_client_set_state(&client_common, (esp_ble_mesh_cfg_client_set_state_t *)&ttl_set); + } else if (strcmp(configuration_client_model_operation.set_state->sval[0], "proxy") == 0) { + err = esp_ble_mesh_config_client_set_state(&client_common, (esp_ble_mesh_cfg_client_set_state_t *)&proxy_set); + } else if (strcmp(configuration_client_model_operation.set_state->sval[0], "subadd") == 0) { + err = esp_ble_mesh_config_client_set_state(&client_common, (esp_ble_mesh_cfg_client_set_state_t *)&mod_sub_add); + } else if (strcmp(configuration_client_model_operation.set_state->sval[0], "subdel") == 0) { + err = esp_ble_mesh_config_client_set_state(&client_common, (esp_ble_mesh_cfg_client_set_state_t *)&mod_sub_del); + } else if (strcmp(configuration_client_model_operation.set_state->sval[0], "relay") == 0) { + err = esp_ble_mesh_config_client_set_state(&client_common, (esp_ble_mesh_cfg_client_set_state_t *)&relay_set); + } else if (strcmp(configuration_client_model_operation.set_state->sval[0], "pubset") == 0) { + err = esp_ble_mesh_config_client_set_state(&client_common, (esp_ble_mesh_cfg_client_set_state_t *)&mod_pub_set); + } + } + } else if (strcmp(configuration_client_model_operation.action_type->sval[0], "reg") == 0) { + err = esp_ble_mesh_register_config_client_callback(ble_mesh_configuration_client_model_cb); + } + } + + if (err == ESP_OK) { + ESP_LOGI(TAG, "ConfigClient:OK"); + } else { + ESP_LOGI(TAG, "ConfigClient:Fail"); + } + + ESP_LOGD(TAG, "exit %s %d\n", __func__, err); + return err; +} + + +void ble_mesh_register_configuration_client_model_command() +{ + configuration_client_model_operation.action_type = arg_str1("z", NULL, "", "action type"); + configuration_client_model_operation.set_state = arg_str0("x", NULL, "", "set state"); + configuration_client_model_operation.opcode = arg_int0("o", NULL, "", "message opcode"); + configuration_client_model_operation.unicast_address = arg_int0("u", NULL, "

", "unicast address"); + configuration_client_model_operation.net_idx = arg_int0("n", NULL, "", "net work index"); + configuration_client_model_operation.appkey_index = arg_int0("i", NULL, "", "appkey index"); + configuration_client_model_operation.relay_statue = arg_int0("r", NULL, "", "relay statue"); + configuration_client_model_operation.relay_transmit = arg_int0("t", NULL, "", "relay transmit"); + configuration_client_model_operation.cid = arg_int0("c", NULL, "", "company id"); + configuration_client_model_operation.value = arg_int0("v", NULL, "", "value"); + configuration_client_model_operation.addr = arg_int0("a", NULL, "
", "address"); + configuration_client_model_operation.mod_id = arg_int0("m", NULL, "", "model id"); + configuration_client_model_operation.end = arg_end(1); + + const esp_console_cmd_t client_stconfiguration_client_model_operationate_cmd = { + .command = "bmccm", + .help = "ble mesh configuration client model", + .hint = NULL, + .func = &ble_mesh_configuration_client_model_operation, + .argtable = &configuration_client_model_operation, + }; + ESP_ERROR_CHECK(esp_console_cmd_register(&client_stconfiguration_client_model_operationate_cmd)); +} +#endif diff --git a/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_reg_gen_onoff_client_cmd.c b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_reg_gen_onoff_client_cmd.c new file mode 100644 index 0000000000..70a28162f7 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_reg_gen_onoff_client_cmd.c @@ -0,0 +1,180 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 "esp_timer.h" +#include "ble_mesh_adapter.h" + +#if (CONFIG_BLE_MESH_GENERIC_ONOFF_CLI) +typedef struct { + struct arg_str *action_type; + struct arg_int *op_en; + struct arg_int *unicast_address; + struct arg_int *onoff_state; + struct arg_int *trans_id; + struct arg_int *trans_time; + struct arg_int *delay; + struct arg_int *opcode; + struct arg_int *appkey_idx; + struct arg_int *role; + struct arg_int *net_idx; + struct arg_end *end; +} ble_mesh_gen_onoff_state_t; +ble_mesh_gen_onoff_state_t gen_onoff_state; + +void ble_mesh_register_gen_onoff_client_command(); +void ble_mesh_generic_onoff_client_model_cb(esp_ble_mesh_generic_client_cb_event_t event, + esp_ble_mesh_generic_client_cb_param_t *param); + +void ble_mesh_register_gen_onoff_client() +{ + ble_mesh_register_gen_onoff_client_command(); +} + +void ble_mesh_generic_onoff_client_model_cb(esp_ble_mesh_generic_client_cb_event_t event, + esp_ble_mesh_generic_client_cb_param_t *param) +{ + uint32_t opcode = param->params->opcode; + + ESP_LOGD(TAG, "enter %s: event is %d, error code is %d, opcode is 0x%x\n", + __func__, event, param->error_code, opcode); + + switch (event) { + case ESP_BLE_MESH_GENERIC_CLIENT_GET_STATE_EVT: { + switch (opcode) { + case ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_GET: + if (param->error_code == ESP_OK) { + ESP_LOGI(TAG, "GenOnOffClient:GetStatus,OK,%d", param->status_cb.onoff_status.present_onoff); + } else { + ESP_LOGE(TAG, "GenOnOffClient:GetStatus,Fail,%d", param->error_code); + } + break; + default: + break; + } + break; + } + case ESP_BLE_MESH_GENERIC_CLIENT_SET_STATE_EVT: { + switch (opcode) { + case ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET: + if (param->error_code == ESP_OK) { + ESP_LOGI(TAG, "GenOnOffClient:SetStatus,OK,%d", param->status_cb.onoff_status.present_onoff); + } else { + ESP_LOGE(TAG, "GenOnOffClient:SetStatus,Fail,%d", param->error_code); + } + break; + case ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK: + if (param->error_code == ESP_OK) { + ESP_LOGI(TAG, "GenOnOffClient:SetUNACK,OK"); + } else { + ESP_LOGE(TAG, "GenOnOffClient:SetUNACK,Fail,%d", param->error_code); + } + break; + default: + break; + } + break; + } + case ESP_BLE_MESH_GENERIC_CLIENT_PUBLISH_EVT: { + if (param->error_code == ESP_OK) { + ESP_LOGI(TAG, "GenOnOffClient:Publish,OK"); + } else { + ESP_LOGE(TAG, "GenOnOffClient:Publish,Fail,%d", param->error_code); + } + break; + } + case ESP_BLE_MESH_GENERIC_CLIENT_TIMEOUT_EVT: + ESP_LOGE(TAG, "GenOnOffClient:TimeOut,%d", param->error_code); + break; + case ESP_BLE_MESH_GENERIC_CLIENT_EVT_MAX: + ESP_LOGE(TAG, "GenONOFFClient:InvalidEvt,%d", param->error_code); + break; + default: + break; + } + ESP_LOGD(TAG, "exit %s \n", __func__); +} + +int ble_mesh_generic_onoff_client_model(int argc, char **argv) +{ + int err = ESP_OK; + esp_ble_mesh_generic_client_set_state_t gen_client_set; + esp_ble_mesh_generic_client_get_state_t gen_client_get; + esp_ble_mesh_client_common_param_t onoff_common = { + .msg_timeout = 0, + .ctx.send_ttl = 7, + }; + + ESP_LOGD(TAG, "enter %s\n", __func__); + + int nerrors = arg_parse(argc, argv, (void **) &gen_onoff_state); + if (nerrors != 0) { + arg_print_errors(stderr, gen_onoff_state.end, argv[0]); + return 1; + } + + onoff_common.model = ble_mesh_get_model(ESP_BLE_MESH_MODEL_ID_GEN_ONOFF_CLI); + + arg_int_to_value(gen_onoff_state.appkey_idx, onoff_common.ctx.app_idx, "appkey_index"); + arg_int_to_value(gen_onoff_state.opcode, onoff_common.opcode, "opcode"); + arg_int_to_value(gen_onoff_state.role, onoff_common.msg_role, "role"); + arg_int_to_value(gen_onoff_state.unicast_address, onoff_common.ctx.addr, "address"); + arg_int_to_value(gen_onoff_state.net_idx, onoff_common.ctx.net_idx, "network key index"); + arg_int_to_value(gen_onoff_state.op_en, gen_client_set.onoff_set.op_en, "op_en"); + arg_int_to_value(gen_onoff_state.onoff_state, gen_client_set.onoff_set.onoff, "onoff"); + arg_int_to_value(gen_onoff_state.trans_id, gen_client_set.onoff_set.tid, "tid"); + arg_int_to_value(gen_onoff_state.trans_time, gen_client_set.onoff_set.trans_time, "trans_time"); + arg_int_to_value(gen_onoff_state.delay, gen_client_set.onoff_set.delay, "delay"); + + if (gen_onoff_state.action_type->count != 0) { + if (strcmp(gen_onoff_state.action_type->sval[0], "get") == 0) { + err = esp_ble_mesh_generic_client_get_state(&onoff_common, &gen_client_get); + } else if (strcmp(gen_onoff_state.action_type->sval[0], "set") == 0) { + err = esp_ble_mesh_generic_client_set_state(&onoff_common, &gen_client_set); + } else if (strcmp(gen_onoff_state.action_type->sval[0], "reg") == 0) { + err = esp_ble_mesh_register_generic_client_callback(ble_mesh_generic_onoff_client_model_cb); + if (err == ESP_OK) { + ESP_LOGI(TAG, "GenONOFFClient:Reg,OK"); + } + } + } + ESP_LOGD(TAG, "exit %s\n", __func__); + return err; +} + +void ble_mesh_register_gen_onoff_client_command() +{ + gen_onoff_state.action_type = arg_str1("z", NULL, "", "action type"); + gen_onoff_state.opcode = arg_int0("o", NULL, "", "message opcode"); + gen_onoff_state.appkey_idx = arg_int0("a", NULL, "", "appkey index"); + gen_onoff_state.role = arg_int0("r", NULL, "", "role"); + gen_onoff_state.unicast_address = arg_int0("u", NULL, "
", "unicast address"); + gen_onoff_state.net_idx = arg_int0("n", NULL, "", "network key index"); + gen_onoff_state.op_en = arg_int0("e", NULL, "", "whether optional parameters included"); + gen_onoff_state.onoff_state = arg_int0("s", NULL, "", "present onoff state"); + gen_onoff_state.trans_id = arg_int0("i", NULL, "", "transaction identifier"); + gen_onoff_state.trans_time = arg_int0("t", NULL, "
", "unicast address"); + test_perf_client_model.ttl = arg_int0("t", NULL, "", "ttl"); + test_perf_client_model.app_idx = arg_int0("a", NULL, "", "appkey index"); + test_perf_client_model.net_idx = arg_int0("i", NULL, "", "network key index"); + test_perf_client_model.dev_role = arg_int0("d", NULL, "", "device role"); + test_perf_client_model.dev_role->ival[0] = ROLE_PROVISIONER; + test_perf_client_model.end = arg_end(1); + + const esp_console_cmd_t test_perf_client_model_cmd = { + .command = "bmtpcvm", + .help = "ble mesh test performance client vendor model", + .hint = NULL, + .func = &ble_mesh_test_performance_client_model, + .argtable = &test_perf_client_model, + }; + ESP_ERROR_CHECK(esp_console_cmd_register(&test_perf_client_model_cmd)); + + test_perf_client_model_statistics.action_type = arg_str1("z", NULL, "", "action type"); + test_perf_client_model_statistics.test_size = arg_int0("s", NULL, "", "test size"); + test_perf_client_model_statistics.node_num = arg_int0("n", NULL, "", "node number"); + test_perf_client_model_statistics.ttl = arg_int0("l", NULL, "", "ttl"); + test_perf_client_model_statistics.end = arg_end(1); + + const esp_console_cmd_t test_perf_client_model_performance_cmd = { + .command = "bmcperf", + .help = "ble mesh client: test performance", + .hint = NULL, + .func = &ble_mesh_test_performance_client_model_performance, + .argtable = &test_perf_client_model_statistics, + }; + ESP_ERROR_CHECK(esp_console_cmd_register(&test_perf_client_model_performance_cmd)); +} diff --git a/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_register_node_cmd.c b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_register_node_cmd.c new file mode 100644 index 0000000000..57e89605c9 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_register_node_cmd.c @@ -0,0 +1,476 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 "soc/soc.h" +#include "esp_bt.h" +#include "esp_bt_device.h" + +#include "esp_ble_mesh_defs.h" +#include "esp_ble_mesh_common_api.h" +#include "esp_ble_mesh_provisioning_api.h" +#include "esp_ble_mesh_networking_api.h" +#include "esp_ble_mesh_config_model_api.h" + +#include "ble_mesh_adapter.h" + +typedef struct { + struct arg_str *static_val; + struct arg_int *static_val_len; + struct arg_int *output_size; + struct arg_int *output_actions; + struct arg_int *input_size; + struct arg_int *input_actions; + struct arg_int *prov_start_address; + struct arg_end *end; +} ble_mesh_prov_t; +static ble_mesh_prov_t oob; + +typedef struct { + struct arg_int *model_type; + struct arg_int *config_index; + struct arg_int *pub_config; + struct arg_end *end; +} ble_mesh_comp_t; +static ble_mesh_comp_t component; + +typedef struct { + struct arg_int *bearer; + struct arg_int *enable; + struct arg_end *end; +} ble_mesh_bearer_t; +static ble_mesh_bearer_t bearer; + +typedef struct { + struct arg_str *action_type; + struct arg_int *tx_sense_power; + struct arg_end *end; +} ble_mesh_tx_sense_power; +static ble_mesh_tx_sense_power power_set; + +ble_mesh_node_status node_status = { + .previous = 0x0, + .current = 0x0, +}; + +SemaphoreHandle_t ble_mesh_node_sema; + +void ble_mesh_register_node_cmd(); +// Register callback function +void ble_mesh_prov_cb(esp_ble_mesh_prov_cb_event_t event, esp_ble_mesh_prov_cb_param_t *param); +void ble_mesh_model_cb(esp_ble_mesh_model_cb_event_t event, esp_ble_mesh_model_cb_param_t *param); + + +void ble_mesh_register_mesh_node() +{ + ble_mesh_register_node_cmd(); +} + +int ble_mesh_register_node_cb() +{ + ESP_LOGD(TAG, "enter %s\n", __func__); + ble_mesh_node_init(); + esp_ble_mesh_register_prov_callback(ble_mesh_prov_cb); + esp_ble_mesh_register_custom_model_callback(ble_mesh_model_cb); + ESP_LOGI(TAG, "Node:Reg,OK"); + ESP_LOGD(TAG, "exit %s\n", __func__); + return 0; +} + +void ble_mesh_prov_cb(esp_ble_mesh_prov_cb_event_t event, esp_ble_mesh_prov_cb_param_t *param) +{ + ESP_LOGD(TAG, "enter %s, event = %d", __func__, event); + switch (event) { + case ESP_BLE_MESH_PROV_REGISTER_COMP_EVT: + ble_mesh_callback_check_err_code(param->prov_register_comp.err_code, "Provisioning:Register"); + break; + case ESP_BLE_MESH_NODE_PROV_ENABLE_COMP_EVT: + ble_mesh_callback_check_err_code(param->node_prov_enable_comp.err_code, "Node:EnBearer"); + break; + case ESP_BLE_MESH_NODE_PROV_DISABLE_COMP_EVT: + ble_mesh_callback_check_err_code(param->node_prov_disable_comp.err_code, "Node:DisBearer"); + break; + case ESP_BLE_MESH_NODE_PROV_LINK_OPEN_EVT: + ESP_LOGI(TAG, "Node:LinkOpen,OK,%d", param->node_prov_link_open.bearer); + break; + case ESP_BLE_MESH_NODE_PROV_LINK_CLOSE_EVT: + ESP_LOGI(TAG, "Node:LinkClose,OK,%d", param->node_prov_link_close.bearer); + break; + case ESP_BLE_MESH_NODE_PROV_OUTPUT_NUMBER_EVT: + ESP_LOGI(TAG, "Node:OutPut,%d,%d", param->node_prov_output_num.action, param->node_prov_output_num.number); + break; + case ESP_BLE_MESH_NODE_PROV_OUTPUT_STRING_EVT: + ESP_LOGI(TAG, "Node:OutPutStr,%s", param->node_prov_output_str.string); + break; + case ESP_BLE_MESH_NODE_PROV_INPUT_EVT: + ESP_LOGI(TAG, "Node:InPut,%d,%d", param->node_prov_input.action, param->node_prov_input.size); + break; + case ESP_BLE_MESH_NODE_PROV_COMPLETE_EVT: + ESP_LOGI(TAG, "Node:OK,%d,%d", param->node_prov_complete.net_idx, param->node_prov_complete.addr); + ble_mesh_set_node_prestore_params(param->node_prov_complete.net_idx, param->node_prov_complete.addr); + break; + case ESP_BLE_MESH_NODE_PROV_RESET_EVT: + ESP_LOGI(TAG, "Node:Reset"); + break; + case ESP_BLE_MESH_NODE_PROV_INPUT_NUMBER_COMP_EVT: + ble_mesh_callback_check_err_code(param->node_prov_input_num_comp.err_code, "Node:InputNum"); + break; + case ESP_BLE_MESH_NODE_PROV_INPUT_STRING_COMP_EVT: + ble_mesh_callback_check_err_code(param->node_prov_input_str_comp.err_code, "Node:InputStr"); + break; + case ESP_BLE_MESH_NODE_SET_UNPROV_DEV_NAME_COMP_EVT: + ble_mesh_callback_check_err_code(param->node_set_unprov_dev_name_comp.err_code, "Node:SetName"); + break; + case ESP_BLE_MESH_NODE_PROXY_IDENTITY_ENABLE_COMP_EVT: + ble_mesh_callback_check_err_code(param->node_proxy_identity_enable_comp.err_code, "Node:ProxyIndentity"); + break; + case ESP_BLE_MESH_NODE_PROXY_GATT_ENABLE_COMP_EVT: + ble_mesh_callback_check_err_code(param->node_proxy_gatt_enable_comp.err_code, "Node:EnProxyGatt"); + break; + case ESP_BLE_MESH_NODE_PROXY_GATT_DISABLE_COMP_EVT: + ble_mesh_callback_check_err_code(param->node_proxy_gatt_disable_comp.err_code, "Node:DisProxyGatt"); + break; +#if (CONFIG_BLE_MESH_PROVISIONER) + case ESP_BLE_MESH_PROVISIONER_RECV_UNPROV_ADV_PKT_EVT: + ESP_LOGI(TAG, "Provisioner recv unprovisioned device beacon:"); + ESP_LOG_BUFFER_HEX("Device UUID %s", param->provisioner_recv_unprov_adv_pkt.dev_uuid, 16); + ESP_LOG_BUFFER_HEX("Address %s", param->provisioner_recv_unprov_adv_pkt.addr, 6); + ESP_LOGI(TAG, "Address type 0x%x, oob_info 0x%04x, adv_type 0x%x, bearer 0x%x", + param->provisioner_recv_unprov_adv_pkt.addr_type, param->provisioner_recv_unprov_adv_pkt.oob_info, + param->provisioner_recv_unprov_adv_pkt.adv_type, param->provisioner_recv_unprov_adv_pkt.bearer); + break; + case ESP_BLE_MESH_PROVISIONER_PROV_LINK_OPEN_EVT: + ESP_LOGI(TAG, "Provisioner:LinkOpen,OK,%d", param->provisioner_prov_link_open.bearer); + break; + case ESP_BLE_MESH_PROVISIONER_PROV_LINK_CLOSE_EVT: + ESP_LOGI(TAG, "Provisioner:LinkClose,OK,%d,%d", + param->provisioner_prov_link_close.bearer, param->provisioner_prov_link_close.reason); + break; + case ESP_BLE_MESH_PROVISIONER_ADD_UNPROV_DEV_COMP_EVT: + ble_mesh_callback_check_err_code(param->provisioner_add_unprov_dev_comp.err_code, "Provisioner:DevAdd"); + break; + case ESP_BLE_MESH_PROVISIONER_DELETE_DEV_COMP_EVT: + ble_mesh_callback_check_err_code(param->provisioner_delete_dev_comp.err_code, "Provisioner:DevDel"); + break; + case ESP_BLE_MESH_PROVISIONER_PROV_COMPLETE_EVT: + ESP_LOGI(TAG, "Provisioner:OK,%d,%d", param->provisioner_prov_complete.netkey_idx, param->provisioner_prov_complete.unicast_addr); + break; + case ESP_BLE_MESH_PROVISIONER_PROV_ENABLE_COMP_EVT: + ble_mesh_callback_check_err_code(param->provisioner_prov_enable_comp.err_code, "Provisioner:EnBearer"); + break; + case ESP_BLE_MESH_PROVISIONER_PROV_DISABLE_COMP_EVT: + ble_mesh_callback_check_err_code(param->provisioner_prov_disable_comp.err_code, "Provisioner:DisBearer"); + break; + case ESP_BLE_MESH_PROVISIONER_SET_DEV_UUID_MATCH_COMP_EVT: + ble_mesh_callback_check_err_code(param->provisioner_set_dev_uuid_match_comp.err_code, "Provisioner:UuidMatch"); + break; + case ESP_BLE_MESH_PROVISIONER_SET_PROV_DATA_INFO_COMP_EVT: + ble_mesh_callback_check_err_code(param->provisioner_set_prov_data_info_comp.err_code, "Provisioner:DataInfo"); + break; + case ESP_BLE_MESH_PROVISIONER_SET_NODE_NAME_COMP_EVT: + ble_mesh_callback_check_err_code(param->provisioner_set_node_name_comp.err_code, "Provisioner:NodeName"); + break; + case ESP_BLE_MESH_PROVISIONER_ADD_LOCAL_APP_KEY_COMP_EVT: + ble_mesh_callback_check_err_code(param->provisioner_add_app_key_comp.err_code, "Provisioner:AppKeyAdd"); + break; + case ESP_BLE_MESH_PROVISIONER_BIND_APP_KEY_TO_MODEL_COMP_EVT: + ble_mesh_callback_check_err_code(param->provisioner_bind_app_key_to_model_comp.err_code, "Provisioner:AppKeyBind"); + break; + case ESP_BLE_MESH_PROVISIONER_ADD_LOCAL_NET_KEY_COMP_EVT: + ble_mesh_callback_check_err_code(param->provisioner_add_net_key_comp.err_code, "Provisioner:NetKeyAdd"); + break; +#endif + default: + break; + } + ESP_LOGD(TAG, "exit %s\n", __func__); +} + +void ble_mesh_model_cb(esp_ble_mesh_model_cb_event_t event, esp_ble_mesh_model_cb_param_t *param) +{ + esp_err_t result = ESP_OK; + uint8_t status; + + ESP_LOGD(TAG, "enter %s, event=%x\n", __func__, event); + + switch (event) { + case ESP_BLE_MESH_MODEL_OPERATION_EVT: + if (param->model_operation.model != NULL && param->model_operation.model->op != NULL) { + if (param->model_operation.opcode == ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_GET) { + ESP_LOGI(TAG, "Node:GetStatus,OK"); + ble_mesh_node_get_state(status); + result = esp_ble_mesh_server_model_send_msg(param->model_operation.model, param->model_operation.ctx, ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_STATUS, + sizeof(status), &status); + } else if (param->model_operation.opcode == ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET) { + ble_mesh_node_set_state(param->model_operation.msg[0]); + ESP_LOGI(TAG, "Node:SetAck,OK,%d,%d", param->model_operation.msg[0], param->model_operation.ctx->recv_ttl); + result = esp_ble_mesh_server_model_send_msg(param->model_operation.model, param->model_operation.ctx, ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_STATUS, + sizeof(status), param->model_operation.msg); + } else if (param->model_operation.opcode == ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK) { + ble_mesh_node_set_state(param->model_operation.msg[0]); + ESP_LOGI(TAG, "Node:SetUnAck,OK,%d,%d", param->model_operation.msg[0], param->model_operation.ctx->recv_ttl); + } else if (param->model_operation.opcode == ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_STATUS) { + ESP_LOGI(TAG, "Node:Status,Success,%d", param->model_operation.length); + } else if (param->model_operation.opcode == ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_SET) { + ESP_LOGI(TAG, "VendorModel:SetAck,OK,%d", param->model_operation.ctx->recv_ttl); + } else if (param->model_operation.opcode == ESP_BLE_MESH_VND_MODEL_OP_TEST_PERF_STATUS) { + uint64_t current_time = esp_timer_get_time(); + result = ble_mesh_test_performance_client_model_accumulate_time(((uint32_t)(current_time - start_time) / 1000), param->model_operation.msg, param->model_operation.ctx->recv_ttl, param->model_operation.length); + ESP_LOGI(TAG, "VendorModel:Status,OK,%d", param->model_operation.ctx->recv_ttl); + if (ble_mesh_test_perf_send_sema != NULL && result == ESP_OK) { + xSemaphoreGive(ble_mesh_test_perf_send_sema); + } + } + } + break; + case ESP_BLE_MESH_MODEL_SEND_COMP_EVT: + if (param->model_send_comp.err_code == ESP_OK) { + ESP_LOGI(TAG, "Node:ModelSend,OK"); + } else { + ESP_LOGE(TAG, "Node:ModelSend,Fail,%d,0x%X,0x%04X", param->model_send_comp.err_code, param->model_send_comp.model->model_id, param->model_send_comp.model->op->opcode); + } + break; + case ESP_BLE_MESH_MODEL_PUBLISH_COMP_EVT: + ESP_LOGI(TAG, "Node:PublishSend,OK,0x%X,%d", param->model_publish_comp.model->model_id, param->model_publish_comp.model->pub->msg->len); + break; + case ESP_BLE_MESH_CLIENT_MODEL_RECV_PUBLISH_MSG_EVT: + ESP_LOGI(TAG, "Node:PublishReceive,OK,0x%04X,%d,%d", param->client_recv_publish_msg.opcode, param->client_recv_publish_msg.length, param->client_recv_publish_msg.msg[1]); + uint64_t current_time = esp_timer_get_time(); + result = ble_mesh_test_performance_client_model_accumulate_time(((uint32_t)(current_time - start_time) / 2000), param->client_recv_publish_msg.msg, param->client_recv_publish_msg.ctx->recv_ttl, param->client_recv_publish_msg.length); + if (ble_mesh_test_perf_send_sema != NULL && param->client_recv_publish_msg.msg[2] == VENDOR_MODEL_PERF_OPERATION_TYPE_SET_UNACK && result == ESP_OK) { + xSemaphoreGive(ble_mesh_test_perf_send_sema); + } + break; + case ESP_BLE_MESH_MODEL_PUBLISH_UPDATE_EVT: + ESP_LOGI(TAG, "Node:PublishUpdate,OK"); + break; + case ESP_BLE_MESH_CLIENT_MODEL_SEND_TIMEOUT_EVT: + ESP_LOGI(TAG, "Node:TimeOut, 0x%04X", param->client_send_timeout.opcode); + if (ble_mesh_test_perf_send_sema != NULL) { + xSemaphoreGive(ble_mesh_test_perf_send_sema); + } + break; + case ESP_BLE_MESH_MODEL_EVT_MAX: + ESP_LOGI(TAG, "Node:MaxEvt"); + break; + default: + break; + } + + ESP_LOGD(TAG, "exit %s\n", __func__); +} + +int ble_mesh_power_set(int argc, char **argv) +{ + esp_err_t result = ESP_OK; + int nerrors = arg_parse(argc, argv, (void **) &power_set); + + ESP_LOGD(TAG, "enter %s\n", __func__); + + if (nerrors != 0) { + arg_print_errors(stderr, power_set.end, argv[0]); + return 1; + } + + if (strcmp(power_set.action_type->sval[0], "tx") == 0) { + result = esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_ADV, power_set.tx_sense_power->ival[0]); + } else if (strcmp(power_set.action_type->sval[0], "sense") == 0) { + uint32_t *reg = (uint32_t *)(0x6001c07c); + int reg_addr = 0x6001c07c; + uint32_t flag = 0x00FF0000; + uint32_t sense_new = power_set.tx_sense_power->ival[0]; + uint32_t reg_to_write = ((*reg) &= ~flag) | ((256 - sense_new) << 16); + REG_WRITE(reg_addr, reg_to_write); + + } + + if (result == ESP_OK) { + ESP_LOGI(TAG, "Node:SetPower,OK\n"); + } + + ESP_LOGD(TAG, "exit %s\n", __func__); + return result; +} + +static int ble_mesh_load_oob(int argc, char **argv) +{ + uint8_t *static_val; + + ESP_LOGD(TAG, "enter %s \n", __func__); + + int nerrors = arg_parse(argc, argv, (void **) &oob); + if (nerrors != 0) { + arg_print_errors(stderr, oob.end, argv[0]); + return 1; + } + + //parsing prov +#if CONFIG_BLE_MESH_NODE + prov.uuid = dev_uuid; + memcpy(dev_uuid, esp_bt_dev_get_address(), 6); + if (oob.static_val->count != 0) { + static_val = malloc(oob.static_val_len->ival[0] + 1); + if (static_val == NULL) { + ESP_LOGE(TAG, "malloc fail,%s,%d\n", __func__, __LINE__); + } + get_value_string((char *)oob.static_val->sval[0], (char *)static_val); + prov.static_val = static_val; + } + + arg_int_to_value(oob.static_val_len, prov.static_val_len, "static value length"); + arg_int_to_value(oob.output_size, prov.output_size, "output size"); + arg_int_to_value(oob.output_actions, prov.output_actions, "output actions"); + arg_int_to_value(oob.input_size, prov.input_size, "input size"); + arg_int_to_value(oob.input_actions, prov.input_actions, "input actions"); +#endif + +#if CONFIG_BLE_MESH_PROVISIONER + if (oob.static_val->count != 0) { + static_val = malloc(oob.static_val_len->ival[0] + 1); + if (static_val == NULL) { + ESP_LOGE(TAG, "malloc fail,%s,%d\n", __func__, __LINE__); + } + get_value_string((char *)oob.static_val->sval[0], (char *)static_val); + prov.prov_static_oob_val = static_val; + } + arg_int_to_value(oob.prov_start_address, prov.prov_start_address, "provisioner start address"); + arg_int_to_value(oob.static_val_len, prov.prov_static_oob_len, "provisioner static value length"); +#endif + + ESP_LOGI(TAG, "OOB:Load,OK\n"); + + ESP_LOGD(TAG, "exit %s\n", __func__); + return 0; +} + + +int ble_mesh_init(int argc, char **argv) +{ + int err; + esp_ble_mesh_comp_t *local_component = NULL; + + int nerrors = arg_parse(argc, argv, (void **) &component); + if (nerrors != 0) { + arg_print_errors(stderr, component.end, argv[0]); + return 1; + } + + ESP_LOGD(TAG, "enter %s, module %x\n", __func__, component.model_type->ival[0]); + local_component = ble_mesh_get_component(component.model_type->ival[0]); + + + err = esp_ble_mesh_init(&prov, local_component); + if (err) { + ESP_LOGE(TAG, "Initializing mesh failed (err %d)\n", err); + return err; + } + + ESP_LOGD(TAG, "exit %s\n", __func__); + return err; +} + +int ble_mesh_provisioner_enable_bearer(int argc, char **argv) +{ + esp_err_t err = 0; + + ESP_LOGD(TAG, "enter %s \n", __func__); + + int nerrors = arg_parse(argc, argv, (void **) &bearer); + if (nerrors != 0) { + arg_print_errors(stderr, bearer.end, argv[0]); + return 1; + } + + if (bearer.enable->count != 0) { + if (bearer.enable->ival[0]) { + err = esp_ble_mesh_provisioner_prov_enable(bearer.bearer->ival[0]); + } else { + err = esp_ble_mesh_provisioner_prov_disable(bearer.bearer->ival[0]); + } + } else { + return 1; + } + + ESP_LOGD(TAG, "exit %s\n", __func__); + return err; +} + +void ble_mesh_register_node_cmd() +{ + const esp_console_cmd_t register_cmd = { + .command = "bmreg", + .help = "ble mesh: provisioner/node register callback", + .hint = NULL, + .func = &ble_mesh_register_node_cb, + }; + ESP_ERROR_CHECK(esp_console_cmd_register(®ister_cmd)); + oob.static_val = arg_str0("s", NULL, "", "Static OOB value"); + oob.static_val_len = arg_int0("l", NULL, "", "Static OOB value length"); + oob.output_size = arg_int0("x", NULL, "", "Maximum size of Output OOB"); + oob.output_actions = arg_int0("o", NULL, "", "Supported Output OOB Actions"); + oob.input_size = arg_int0("y", NULL, "", "Maximum size of Input OOB"); + oob.input_actions = arg_int0("i", NULL, "", "Supported Input OOB Actions"); + oob.prov_start_address = arg_int0("p", NULL, "
", "start address assigned by provisioner"); + oob.prov_start_address->ival[0] = 0x0005; + oob.end = arg_end(1); + + const esp_console_cmd_t oob_cmd = { + .command = "bmoob", + .help = "ble mesh: provisioner/node config OOB parameters", + .hint = NULL, + .func = &ble_mesh_load_oob, + .argtable = &oob, + }; + ESP_ERROR_CHECK( esp_console_cmd_register(&oob_cmd) ); + + component.model_type = arg_int0("m", NULL, "", "mesh model"); + component.config_index = arg_int0("c", NULL, "", "mesh model op"); + component.config_index->ival[0] = 0; // set default value + component.pub_config = arg_int0("p", NULL, "", "publish message buffer"); + component.end = arg_end(1); + + const esp_console_cmd_t model_cmd = { + .command = "bminit", + .help = "ble mesh: provisioner/node init", + .hint = NULL, + .func = &ble_mesh_init, + .argtable = &component, + }; + ESP_ERROR_CHECK( esp_console_cmd_register(&model_cmd) ); + + bearer.bearer = arg_int0("b", NULL, "", "supported bearer"); + bearer.enable = arg_int0("e", NULL, "", "bearers node supported"); + bearer.end = arg_end(1); + + const esp_console_cmd_t bearer_cmd = { + .command = "bmpbearer", + .help = "ble mesh provisioner: enable/disable different bearers", + .hint = NULL, + .func = &ble_mesh_provisioner_enable_bearer, + .argtable = &bearer, + }; + ESP_ERROR_CHECK(esp_console_cmd_register(&bearer_cmd)); + + power_set.tx_sense_power = arg_int0("t", NULL, "", "tx power or sense"); + power_set.action_type = arg_str1("z", NULL, "", "action type"); + power_set.end = arg_end(1); + + const esp_console_cmd_t power_set_cmd = { + .command = "bmtxpower", + .help = "ble mesh: set tx power or sense", + .hint = NULL, + .func = &ble_mesh_power_set, + .argtable = &power_set, + }; + ESP_ERROR_CHECK(esp_console_cmd_register(&power_set_cmd)); +} diff --git a/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_register_provisioner_cmd.c b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_register_provisioner_cmd.c new file mode 100644 index 0000000000..c5a2d3ad1b --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/ble_mesh_register_provisioner_cmd.c @@ -0,0 +1,424 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 "esp_bt_defs.h" + +#include "provisioner_prov.h" +#include "esp_ble_mesh_defs.h" +#include "esp_ble_mesh_networking_api.h" +#include "esp_ble_mesh_provisioning_api.h" +#include "esp_ble_mesh_config_model_api.h" + +#include "ble_mesh_adapter.h" +#include "ble_mesh_console_decl.h" + +#if CONFIG_BLE_MESH_PROVISIONER + +typedef struct { + struct arg_int *bearer; + struct arg_int *enable; + struct arg_end *end; +} ble_mesh_provisioner_bearer_t; +ble_mesh_provisioner_bearer_t provisioner_bearer; + +typedef struct { + struct arg_str *add_del; + struct arg_str *device_addr; + struct arg_str *device_uuid; + struct arg_int *addr_type; + struct arg_int *bearer; + struct arg_int *oob_info; + struct arg_int *flag; + struct arg_end *end; +} ble_mesh_provisioner_addr_t; +ble_mesh_provisioner_addr_t provisioner_addr; + +typedef struct { + struct arg_int *unicast_addr; + struct arg_end *end; +} ble_mesh_provisioner_get_node_t; +ble_mesh_provisioner_get_node_t provisioner_get_node; + +typedef struct { + struct arg_int *oob_info; + struct arg_int *unicast_addr; + struct arg_int *element_num; + struct arg_int *net_idx; + struct arg_str *dev_key; + struct arg_str *uuid; + struct arg_end *end; +} ble_mesh_provisioner_add_node_t; +ble_mesh_provisioner_add_node_t provisioner_add_node; + +typedef struct { + struct arg_int *appkey_index; + struct arg_int *element_address; + struct arg_int *network_index; + struct arg_int *mod_id; + struct arg_int *cid; + struct arg_end *end; +} ble_mesh_provisioner_bind_model_t; +ble_mesh_provisioner_bind_model_t provisioner_local_bind; + +typedef struct { + struct arg_str *action_type; + struct arg_int *net_idx; + struct arg_int *app_idx; + struct arg_str *key; + struct arg_end *end; +} ble_mesh_provisioner_add_key_t; +ble_mesh_provisioner_add_key_t provisioner_add_key; + +void ble_mesh_regist_provisioner_cmd(); + +void ble_mesh_prov_adv_cb(const esp_bd_addr_t addr, const esp_ble_addr_type_t addr_type, const uint8_t adv_type, + const uint8_t *dev_uuid, uint16_t oob_info, esp_ble_mesh_prov_bearer_t bearer); + +void ble_mesh_register_mesh_provisioner() +{ + ble_mesh_regist_provisioner_cmd(); +} + +void ble_mesh_prov_adv_cb(const esp_bd_addr_t addr, const esp_ble_addr_type_t addr_type, const uint8_t adv_type, + const uint8_t *dev_uuid, uint16_t oob_info, esp_ble_mesh_prov_bearer_t bearer) +{ + ESP_LOGD(TAG, "enter %s\n", __func__); + ESP_LOGI(TAG, "scan device address:"); + esp_log_buffer_hex(TAG, addr, sizeof(esp_bd_addr_t)); + ESP_LOGI(TAG, "scan device uuid:"); + esp_log_buffer_hex(TAG, dev_uuid, 16); + ESP_LOGD(TAG, "exit %s\n", __func__); +} + +int ble_mesh_provisioner_register() +{ + ESP_LOGD(TAG, "enter %s \n", __func__); + // esp_ble_mesh_register_unprov_adv_pkt_callback(ble_mesh_prov_adv_cb); + ESP_LOGI(TAG, "Provisioner:Reg,OK"); + ESP_LOGD(TAG, "exit %s \n", __func__); + return 0; +} + +int ble_mesh_provision_address(int argc, char **argv) +{ + esp_err_t err = ESP_OK; + esp_ble_mesh_unprov_dev_add_t device_addr = {0}; + uint8_t preset_addr_uuid[16] = {0x01, 0x02}; + esp_ble_mesh_device_delete_t del_dev = { + .flag = BIT(0), + }; + + ESP_LOGD(TAG, "enter %s \n", __func__); + + int nerrors = arg_parse(argc, argv, (void **) &provisioner_addr); + if (nerrors != 0) { + arg_print_errors(stderr, provisioner_addr.end, argv[0]); + return 1; + } + + if (provisioner_addr.device_addr->count != 0) { + if (provisioner_addr.device_uuid->count != 0) { + del_dev.flag = BIT(0) | BIT(1); + str_2_mac((uint8_t *)provisioner_addr.device_addr->sval[0], device_addr.uuid); + str_2_mac((uint8_t *)provisioner_addr.device_addr->sval[0], del_dev.uuid); + } else { + del_dev.flag = BIT(0); + memcpy(device_addr.uuid, preset_addr_uuid, 16); + memcpy(del_dev.uuid, preset_addr_uuid, 16); + } + str_2_mac((uint8_t *)provisioner_addr.device_addr->sval[0], device_addr.addr); + str_2_mac((uint8_t *)provisioner_addr.device_addr->sval[0], del_dev.addr); + arg_int_to_value(provisioner_addr.addr_type, device_addr.addr_type, "address type"); + arg_int_to_value(provisioner_addr.addr_type, del_dev.addr_type, "address type"); + } else if (provisioner_addr.device_uuid->count != 0) { + del_dev.flag = BIT(1); + memcpy(device_addr.addr, preset_addr_uuid, 6); + memcpy(del_dev.addr, preset_addr_uuid, 6); + str_2_mac((uint8_t *)provisioner_addr.device_addr->sval[0], device_addr.uuid); + str_2_mac((uint8_t *)provisioner_addr.device_addr->sval[0], del_dev.uuid); + } + + if (strcmp(provisioner_addr.add_del->sval[0], "add") == 0) { + arg_int_to_value(provisioner_addr.bearer, device_addr.bearer, "bearer"); + arg_int_to_value(provisioner_addr.oob_info, device_addr.oob_info, "oob information"); + err = esp_ble_mesh_provisioner_add_unprov_dev(&device_addr, provisioner_addr.flag->ival[0]); + } else if (strcmp(provisioner_addr.add_del->sval[0], "del") == 0) { + err = esp_ble_mesh_provisioner_delete_dev(&del_dev); + } + + ESP_LOGD(TAG, "exit %s \n", __func__); + return err; +} + +int ble_mesh_provisioner_bearer(int argc, char **argv) +{ + esp_err_t err; + + ESP_LOGD(TAG, "enter %s \n", __func__); + + int nerrors = arg_parse(argc, argv, (void **) &provisioner_bearer); + if (nerrors != 0) { + arg_print_errors(stderr, provisioner_bearer.end, argv[0]); + return 1; + } + + if (provisioner_bearer.enable->count != 0) { + if (provisioner_bearer.enable->ival[0]) { + err = esp_ble_mesh_provisioner_prov_enable(provisioner_bearer.bearer->ival[0]); + } else { + err = esp_ble_mesh_provisioner_prov_disable(provisioner_bearer.bearer->ival[0]); + } + } else { + return 1; + } + + ESP_LOGD(TAG, "exit %s \n", __func__); + return err; +} + +int ble_mesh_provisioner_get_node(int argc, char **argv) +{ + uint16_t unicast_addr = 0; + uint16_t i = 0; + struct bt_mesh_node_t *node_info; + + ESP_LOGD(TAG, "enter %s\n", __func__); + int nerrors = arg_parse(argc, argv, (void **) &provisioner_get_node); + if (nerrors != 0) { + arg_print_errors(stderr, provisioner_get_node.end, argv[0]); + return 1; + } + + arg_int_to_value(provisioner_get_node.unicast_addr, unicast_addr, "unicast address"); + node_info = bt_mesh_provisioner_get_node_info(unicast_addr); + + if (node_info == NULL) { + return ESP_FAIL; + } else { + printf("OobInfo:0x%x,Address:0x%x,EleNum:0x%x,NetIdx:0x%x,DevKey:", + node_info->oob_info, node_info->unicast_addr, node_info->element_num, node_info->net_idx); + for (i = 0; i < 16; i++) { + printf("%02x", node_info->dev_key[i]); + } + printf(",DevUuid:"); + for (i = 0; i < 16; i++) { + printf("%02x", node_info->dev_uuid[i]); + } + printf("\n"); + } + + ESP_LOGD(TAG, "exit %s\n", __func__); + return ESP_OK; +} + +int ble_mesh_provisioner_add_node(int argc, char **argv) +{ + struct bt_mesh_node_t node_info; + esp_err_t result; + ESP_LOGD(TAG, " enter %s\n", __func__); + + int nerrors = arg_parse(argc, argv, (void **) &provisioner_add_node); + if (nerrors != 0) { + arg_print_errors(stderr, provisioner_add_node.end, argv[0]); + return 1; + } + + arg_int_to_value(provisioner_add_node.oob_info, node_info.oob_info, "oob information"); + arg_int_to_value(provisioner_add_node.unicast_addr, node_info.unicast_addr, "unicast address"); + arg_int_to_value(provisioner_add_node.element_num, node_info.element_num, "element number"); + arg_int_to_value(provisioner_add_node.net_idx, node_info.net_idx, "network index"); + if (provisioner_add_node.dev_key->count != 0) { + get_value_string((char *)provisioner_add_node.dev_key->sval[0], (char *)node_info.dev_key); + } + if (provisioner_add_node.uuid->count != 0) { + get_value_string((char *)provisioner_add_node.uuid->sval[0], (char *)node_info.dev_uuid); + get_value_string((char *)provisioner_add_node.uuid->sval[0], (char *)node_info.dev_uuid); + } + + result = bt_mesh_provisioner_store_node_info(&node_info); + if (result == ESP_OK) { + ESP_LOGI(TAG, "Provisioner:AddNodeInfo,OK\n"); + } + + ESP_LOGD(TAG, "exit %s\n", __func__); + return result; +} + +int ble_mesh_provisioner_add_key(int argc, char **argv) +{ + esp_err_t err = ESP_OK; + uint8_t key[16] = {0}; + esp_ble_mesh_prov_data_info_t info = { + .net_idx = 1, + .flag = NET_IDX_FLAG, + }; + ESP_LOGD(TAG, " enter %s\n", __func__); + + int nerrors = arg_parse(argc, argv, (void **) &provisioner_add_key); + if (nerrors != 0) { + arg_print_errors(stderr, provisioner_add_key.end, argv[0]); + return 1; + } + + err = get_value_string((char *)provisioner_add_key.key->sval[0], (char *) key); + if (strcmp(provisioner_add_key.action_type->sval[0], "appkey") == 0) { + err = esp_ble_mesh_provisioner_add_local_app_key(key, provisioner_add_key.net_idx->ival[0], provisioner_add_key.app_idx->ival[0]); + } else if (strcmp(provisioner_add_key.action_type->sval[0], "netkey") == 0) { + // choose network key + info.net_idx = provisioner_add_key.net_idx->ival[0]; + err = esp_ble_mesh_provisioner_add_local_net_key(key, provisioner_add_key.net_idx->ival[0]); + err = err | esp_ble_mesh_provisioner_set_prov_data_info(&info); + } + + if (err != ESP_OK) { + ESP_LOGI(TAG, "Provisioner:KeyAction,Fail"); + } else { + ESP_LOGI(TAG, "Provisioner:KeyAction,OK"); + } + + ESP_LOGD(TAG, "exit %s\n", __func__); + return err; +} + +int ble_mesh_provision_bind_local_model(int argc, char **argv) +{ + esp_err_t err; + uint16_t element_addr = 0; + uint16_t app_idx = 0; + uint16_t model_id = 0; + uint16_t company_id = 0xFFFF; + + ESP_LOGD(TAG, " enter %s\n", __func__); + + int nerrors = arg_parse(argc, argv, (void **) &provisioner_local_bind); + if (nerrors != 0) { + arg_print_errors(stderr, provisioner_local_bind.end, argv[0]); + return 1; + } + + arg_int_to_value(provisioner_local_bind.element_address, element_addr, "element address"); + arg_int_to_value(provisioner_local_bind.appkey_index, app_idx, "appkey index"); + arg_int_to_value(provisioner_local_bind.mod_id, model_id, "model id"); + arg_int_to_value(provisioner_local_bind.cid, company_id, "company id"); + err = esp_ble_mesh_provisioner_bind_app_key_to_local_model(element_addr, app_idx, model_id, company_id); + + if (err != ESP_OK) { + ESP_LOGE(TAG, "Provisioner:BindModel,Fail,%x\n", err); + } else { + ESP_LOGI(TAG, "Provisioner:BindModel,OK\n"); + } + ESP_LOGD(TAG, "exit %s\n", __func__); + return err; +} + +void ble_mesh_regist_provisioner_cmd() +{ + const esp_console_cmd_t prov_register = { + .command = "bmpreg", + .help = "ble mesh provisioner: register callback", + .hint = NULL, + .func = &ble_mesh_provisioner_register, + }; + ESP_ERROR_CHECK(esp_console_cmd_register(&prov_register)); + + provisioner_addr.add_del = arg_str1("z", NULL, "", "action type"); + provisioner_addr.device_addr = arg_str0("d", NULL, "
", "device address"); + provisioner_addr.device_uuid = arg_str0("u", NULL, "", "device uuid"); + provisioner_addr.addr_type = arg_int0("a", NULL, "", "address type"); + provisioner_addr.flag = arg_int0("f", NULL, "", "address flag"); + provisioner_addr.flag->ival[0] = ADD_DEV_RM_AFTER_PROV_FLAG | ADD_DEV_FLUSHABLE_DEV_FLAG; + provisioner_addr.bearer = arg_int0("b", NULL, "", "used bearer"); + provisioner_addr.oob_info = arg_int0("o", NULL, "", "oob information"); + provisioner_addr.end = arg_end(1); + + const esp_console_cmd_t provisioner_addr_cmd = { + .command = "bmpdev", + .help = "ble mesh provisioner: add/delete unprovisioned device", + .hint = NULL, + .func = &ble_mesh_provision_address, + .argtable = &provisioner_addr, + }; + ESP_ERROR_CHECK(esp_console_cmd_register(&provisioner_addr_cmd)); + + provisioner_bearer.bearer = arg_int0("b", NULL, "", "bearer supported provisioner"); + provisioner_bearer.enable = arg_int0("e", NULL, "", "enable or disable bearer"); + provisioner_bearer.end = arg_end(1); + + const esp_console_cmd_t provisioner_bearer_cmd = { + .command = "bmpbearer", + .help = "ble mesh provisioner: enable/disable provisioner different bearer", + .hint = NULL, + .func = &ble_mesh_provisioner_bearer, + .argtable = &provisioner_bearer, + }; + ESP_ERROR_CHECK(esp_console_cmd_register(&provisioner_bearer_cmd)); + + provisioner_get_node.unicast_addr = arg_int1("u", NULL, "
", "get node by unicast address"); + provisioner_get_node.end = arg_end(1); + + const esp_console_cmd_t provisioner_get_node_cmd = { + .command = "bmpgetn", + .help = "ble mesh provisioner: get node", + .func = &ble_mesh_provisioner_get_node, + .argtable = &provisioner_get_node, + }; + ESP_ERROR_CHECK(esp_console_cmd_register(&provisioner_get_node_cmd)); + + provisioner_add_node.oob_info = arg_int0("o", NULL, "", "oob information"); + provisioner_add_node.unicast_addr = arg_int0("a", NULL, "", "unicast address"); + provisioner_add_node.element_num = arg_int0("e", NULL, "", "element num"); + provisioner_add_node.net_idx = arg_int0("n", NULL, "", "net index"); + provisioner_add_node.dev_key = arg_str0("d", NULL, "", "device key"); + provisioner_add_node.uuid = arg_str0("u", NULL, "", "device uuid"); + provisioner_add_node.end = arg_end(1); + + const esp_console_cmd_t provisioner_add_node_cmd = { + .command = "bmpaddn", + .help = "ble mesh provisioner: add node", + .func = &ble_mesh_provisioner_add_node, + .argtable = &provisioner_add_node, + }; + ESP_ERROR_CHECK(esp_console_cmd_register(&provisioner_add_node_cmd)); + + provisioner_local_bind.appkey_index = arg_int1("a", NULL, "", "appkey index"); + provisioner_local_bind.element_address = arg_int1("e", NULL, "", "element address"); + provisioner_local_bind.network_index = arg_int1("n", NULL, "", "network index"); + provisioner_local_bind.mod_id = arg_int1("m", NULL, "", "model id"); + provisioner_local_bind.cid = arg_int0("c", NULL, "", "company id"); + provisioner_local_bind.end = arg_end(1); + + const esp_console_cmd_t provisioner_local_bind_cmd = { + .command = "bmpbind", + .help = "ble mesh provisioner: bind local model", + .func = &ble_mesh_provision_bind_local_model, + .argtable = &provisioner_local_bind, + }; + ESP_ERROR_CHECK(esp_console_cmd_register(&provisioner_local_bind_cmd)); + + provisioner_add_key.action_type = arg_str1("z", NULL, "", "add appkey or network key"); + provisioner_add_key.net_idx = arg_int1("n", NULL, "", "network key index"); + provisioner_add_key.key = arg_str1("k", NULL, "", "appkey or network"); + provisioner_add_key.app_idx = arg_int0("a", NULL, "", "appkey index"); + provisioner_add_key.end = arg_end(1); + + const esp_console_cmd_t provisioner_add_key_cmd = { + .command = "bmpkey", + .help = "ble mesh provisioner: key", + .func = &ble_mesh_provisioner_add_key, + .argtable = &provisioner_add_key, + }; + ESP_ERROR_CHECK(esp_console_cmd_register(&provisioner_add_key_cmd)); +} +#endif + diff --git a/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/component.mk b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/component.mk new file mode 100644 index 0000000000..0b9d7585e7 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/component.mk @@ -0,0 +1,5 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) + diff --git a/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/register_bluetooth.c b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/register_bluetooth.c new file mode 100644 index 0000000000..bcb548520f --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/main/register_bluetooth.c @@ -0,0 +1,45 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 "esp_bt_device.h" +#include "esp_console.h" + +#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] +#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" + +void register_ble_address(); + +void register_bluetooth() +{ + register_ble_address(); +} + +int bt_mac(int argc, char** argv) +{ + const uint8_t *mac = esp_bt_dev_get_address(); + printf("+BTMAC:"MACSTR"\n", MAC2STR(mac)); + return 0; +} + +void register_ble_address() +{ + const esp_console_cmd_t cmd = { + .command = "btmac", + .help = "BLE address", + .hint = NULL, + .func = (esp_console_cmd_func_t)&bt_mac, + }; + ESP_ERROR_CHECK(esp_console_cmd_register(&cmd)); +} + diff --git a/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/sdkconfig.defaults b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/sdkconfig.defaults new file mode 100644 index 0000000000..9c0ad1b87c --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_console/ble_mesh_provisioner/sdkconfig.defaults @@ -0,0 +1,40 @@ +# Override some defaults so BT stack is enabled +# by default in this example +CONFIG_BT_ENABLED=y +CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY=y +CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY= +CONFIG_BTDM_CONTROLLER_MODE_BTDM= +CONFIG_BTDM_CONTROLLER_MODEM_SLEEP=n +CONFIG_BLE_SCAN_DUPLICATE=y +CONFIG_SCAN_DUPLICATE_TYPE=2 +CONFIG_DUPLICATE_SCAN_CACHE_SIZE=200 +CONFIG_BLE_MESH_SCAN_DUPLICATE_EN=y +CONFIG_MESH_DUPLICATE_SCAN_CACHE_SIZE=200 +CONFIG_BTDM_CONTROLLER_FULL_SCAN_SUPPORTED=y +CONFIG_BLE_MESH=y +CONFIG_BLE_MESH_HCI_5_0=y +CONFIG_BLE_MESH_USE_DUPLICATE_SCAN=y +CONFIG_BLE_MESH_PROV=y +CONFIG_BLE_MESH_PROVISIONER=y +CONFIG_BLE_MESH_WAIT_FOR_PROV_MAX_DEV_NUM=20 +CONFIG_BLE_MESH_MAX_PROV_NODES=20 +CONFIG_BLE_MESH_PBA_SAME_TIME=10 +CONFIG_BLE_MESH_PBG_SAME_TIME=4 +CONFIG_BLE_MESH_PROVISIONER_SUBNET_COUNT=3 +CONFIG_BLE_MESH_PROVISIONER_APP_KEY_COUNT=9 +CONFIG_BLE_MESH_PB_ADV=y +CONFIG_BLE_MESH_NET_BUF_POOL_USAGE=y +CONFIG_BLE_MESH_PB_GATT=y +CONFIG_BLE_MESH_GATT_PROXY=y +CONFIG_BLE_MESH_RELAY=y +CONFIG_BLE_MESH_LOW_POWER= +CONFIG_BLE_MESH_FRIEND= +CONFIG_BLE_MESH_MSG_CACHE_SIZE=10 +CONFIG_BLE_MESH_ADV_BUF_COUNT=60 +CONFIG_BLE_MESH_TX_SEG_MSG_COUNT=6 +CONFIG_BLE_MESH_RX_SEG_MSG_COUNT=1 +CONFIG_BLE_MESH_RX_SDU_MAX=384 +CONFIG_BLE_MESH_TX_SEG_MAX=32 +CONFIG_BTU_TASK_STACK_SIZE=4512 +CONFIG_BLE_MESH_CFG_CLI=y +CONFIG_BLE_MESH_GENERIC_ONOFF_CLI=y \ No newline at end of file diff --git a/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_client/CMakeLists.txt b/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_client/CMakeLists.txt new file mode 100644 index 0000000000..df96eec280 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_client/CMakeLists.txt @@ -0,0 +1,8 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/bluetooth/ble_mesh/ble_mesh_vendor_models/fast_prov_vendor_model/components) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(ble_mesh_fast_prov_client) diff --git a/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_client/Makefile b/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_client/Makefile new file mode 100644 index 0000000000..06bba88878 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_client/Makefile @@ -0,0 +1,12 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := ble_mesh_fast_prov_client + +COMPONENT_ADD_INCLUDEDIRS := components/include + +EXTRA_COMPONENT_DIRS := $(IDF_PATH)/examples/bluetooth/ble_mesh/ble_mesh_vendor_models/fast_prov_vendor_model/components + +include $(IDF_PATH)/make/project.mk diff --git a/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_client/README.md b/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_client/README.md new file mode 100644 index 0000000000..9e45f4f31b --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_client/README.md @@ -0,0 +1,2 @@ +ESP BLE Mesh Fast Provisioning Client Demo +======================== \ No newline at end of file diff --git a/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_client/main/CMakeLists.txt b/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_client/main/CMakeLists.txt new file mode 100644 index 0000000000..3d3bc6f9a5 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_client/main/CMakeLists.txt @@ -0,0 +1,5 @@ +set(COMPONENT_SRCS "ble_mesh_demo_main.c") + +set(COMPONENT_ADD_INCLUDEDIRS ".") + +register_component() diff --git a/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_client/main/ble_mesh_demo_main.c b/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_client/main/ble_mesh_demo_main.c new file mode 100644 index 0000000000..3337277340 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_client/main/ble_mesh_demo_main.c @@ -0,0 +1,638 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 + +#include "esp_system.h" +#include "esp_log.h" +#include "nvs_flash.h" + +#include "esp_bt.h" +#include "esp_bt_main.h" +#include "esp_bt_device.h" +#include "esp_gap_ble_api.h" +#include "esp_ble_mesh_defs.h" +#include "esp_ble_mesh_common_api.h" +#include "esp_ble_mesh_provisioning_api.h" +#include "esp_ble_mesh_networking_api.h" +#include "esp_ble_mesh_config_model_api.h" +#include "esp_ble_mesh_generic_model_api.h" + +#include "esp_fast_prov_common.h" +#include "esp_fast_prov_operation.h" +#include "esp_fast_prov_client_model.h" + +#define TAG "FAST_PROV_CLIENT_DEMO" + +#define PROV_OWN_ADDR 0x0001 +#define APP_KEY_OCTET 0x12 +#define GROUP_ADDRESS 0xC000 + +static uint8_t dev_uuid[16] = { 0xdd, 0xdd }; +static uint8_t match[] = { 0xdd, 0xdd }; + +static const esp_ble_mesh_client_op_pair_t fast_prov_cli_op_pair[] = { + { ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_SET, ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_STATUS }, + { ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NET_KEY_ADD, ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NET_KEY_STATUS }, + { ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_GET, ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_STATUS }, +}; + +static esp_ble_mesh_cfg_srv_t config_server = { + .relay = ESP_BLE_MESH_RELAY_DISABLED, + .beacon = ESP_BLE_MESH_BEACON_ENABLED, +#if defined(CONFIG_BLE_MESH_FRIEND) + .friend_state = ESP_BLE_MESH_FRIEND_ENABLED, +#else + .friend_state = ESP_BLE_MESH_FRIEND_NOT_SUPPORTED, +#endif +#if defined(CONFIG_BLE_MESH_GATT_PROXY) + .gatt_proxy = ESP_BLE_MESH_GATT_PROXY_ENABLED, +#else + .gatt_proxy = ESP_BLE_MESH_GATT_PROXY_NOT_SUPPORTED, +#endif + .default_ttl = 7, + /* 3 transmissions with a 20ms interval */ + .net_transmit = ESP_BLE_MESH_TRANSMIT(2, 20), + .relay_retransmit = ESP_BLE_MESH_TRANSMIT(2, 20), +}; +esp_ble_mesh_client_t config_client; +esp_ble_mesh_client_t gen_onoff_client; +esp_ble_mesh_client_t fast_prov_client = { + .op_pair_size = ARRAY_SIZE(fast_prov_cli_op_pair), + .op_pair = fast_prov_cli_op_pair, +}; + +static esp_ble_mesh_model_op_t fast_prov_cli_op[] = { + ESP_BLE_MESH_MODEL_OP(ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_STATUS, 1), + ESP_BLE_MESH_MODEL_OP(ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NET_KEY_STATUS, 2), + ESP_BLE_MESH_MODEL_OP(ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_STATUS, 2), + ESP_BLE_MESH_MODEL_OP_END, +}; + +static esp_ble_mesh_model_t root_models[] = { + ESP_BLE_MESH_MODEL_CFG_SRV(&config_server), + ESP_BLE_MESH_MODEL_CFG_CLI(&config_client), + ESP_BLE_MESH_MODEL_GEN_ONOFF_CLI(NULL, &gen_onoff_client), +}; + +static esp_ble_mesh_model_t vnd_models[] = { + ESP_BLE_MESH_VENDOR_MODEL(CID_ESP, ESP_BLE_MESH_VND_MODEL_ID_FAST_PROV_CLI, + fast_prov_cli_op, NULL, &fast_prov_client), +}; + +static esp_ble_mesh_elem_t elements[] = { + ESP_BLE_MESH_ELEMENT(0, root_models, vnd_models), +}; + +static esp_ble_mesh_comp_t comp = { + .cid = CID_ESP, + .elements = elements, + .element_count = ARRAY_SIZE(elements), +}; + +static esp_ble_mesh_prov_t prov = { + .prov_uuid = dev_uuid, + .prov_unicast_addr = PROV_OWN_ADDR, + .prov_start_address = 0x0005, + .prov_attention = 0x00, + .prov_algorithm = 0x00, + .prov_pub_key_oob = 0x00, + .prov_static_oob_val = NULL, + .prov_static_oob_len = 0x00, + .flags = 0x00, + .iv_index = 0x00, +}; + +example_prov_info_t prov_info = { + .net_idx = ESP_BLE_MESH_KEY_PRIMARY, + .app_idx = ESP_BLE_MESH_KEY_PRIMARY, + .node_addr_cnt = 100, + .unicast_max = 0x7FFF, + .group_addr = GROUP_ADDRESS, + .max_node_num = 0x01, +}; + +static void provisioner_prov_link_open(esp_ble_mesh_prov_bearer_t bearer) +{ + ESP_LOGI(TAG, "%s link open", bearer == ESP_BLE_MESH_PROV_ADV ? "PB-ADV" : "PB-GATT"); +} + +static void provisioner_prov_link_close(esp_ble_mesh_prov_bearer_t bearer, uint8_t reason) +{ + ESP_LOGI(TAG, "%s link close, reason 0x%02x", + bearer == ESP_BLE_MESH_PROV_ADV ? "PB-ADV" : "PB-GATT", reason); + + if (bearer == ESP_BLE_MESH_PROV_ADV && reason != 0x00) { + prov_info.max_node_num++; + } +} + +static void provisioner_prov_complete(int node_index, const uint8_t uuid[16], uint16_t unicast_addr, + uint8_t elem_num, uint16_t net_idx) +{ + example_node_info_t *node = NULL; + char name[10]; + esp_err_t err; + + ESP_LOGI(TAG, "Node index: 0x%x, unicast address: 0x%02x, element num: %d, netkey index: 0x%02x", + node_index, unicast_addr, elem_num, net_idx); + ESP_LOGI(TAG, "Node uuid: %s", bt_hex(uuid, 16)); + + sprintf(name, "%s%d", "NODE-", node_index); + if (esp_ble_mesh_provisioner_set_node_name(node_index, name)) { + ESP_LOGE(TAG, "%s: Failed to set node name", __func__); + return; + } + + /* Sets node info */ + err = example_store_node_info(uuid, unicast_addr, elem_num, prov_info.net_idx, + prov_info.app_idx, LED_OFF); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Failed to set node info", __func__); + return; + } + + /* Gets node info */ + node = example_get_node_info(unicast_addr); + if (!node) { + ESP_LOGE(TAG, "%s: Failed to get node info", __func__); + return; + } + + /* The Provisioner will send Config AppKey Add to the node. */ + example_msg_common_info_t info = { + .net_idx = node->net_idx, + .app_idx = node->app_idx, + .dst = node->unicast_addr, + .timeout = 0, + .role = ROLE_PROVISIONER, + }; + esp_ble_mesh_cfg_app_key_add_t add_key = { + .net_idx = prov_info.net_idx, + .app_idx = prov_info.app_idx, + }; + memcpy(add_key.app_key, prov_info.app_key, 16); + err = example_send_config_appkey_add(config_client.model, &info, &add_key); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Failed to send Config AppKey Add message", __func__); + return; + } +} + +static void example_recv_unprov_adv_pkt(uint8_t dev_uuid[16], uint8_t addr[ESP_BD_ADDR_LEN], + esp_ble_addr_type_t addr_type, uint16_t oob_info, + uint8_t adv_type, esp_ble_mesh_prov_bearer_t bearer) +{ + esp_ble_mesh_unprov_dev_add_t add_dev = {0}; + esp_ble_mesh_dev_add_flag_t flag; + esp_err_t err; + bool reprov; + + if (bearer & ESP_BLE_MESH_PROV_ADV) { + /* Checks if the device has been provisioned previously. If the device + * is a re-provisioned one, we will ignore the 'max_node_num' count and + * start to provision it directly. + */ + reprov = example_is_node_exist(dev_uuid); + if (reprov) { + goto add; + } + + if (prov_info.max_node_num == 0) { + return; + } + + ESP_LOGI(TAG, "address: %s, address type: %d, adv type: %d", bt_hex(addr, 6), addr_type, adv_type); + ESP_LOGI(TAG, "dev uuid: %s", bt_hex(dev_uuid, 16)); + ESP_LOGI(TAG, "oob info: %d, bearer: %s", oob_info, (bearer & ESP_BLE_MESH_PROV_ADV) ? "PB-ADV" : "PB-GATT"); + +add: + memcpy(add_dev.addr, addr, 6); + add_dev.addr_type = (uint8_t)addr_type; + memcpy(add_dev.uuid, dev_uuid, 16); + add_dev.oob_info = oob_info; + add_dev.bearer = (uint8_t)bearer; + flag = ADD_DEV_RM_AFTER_PROV_FLAG | ADD_DEV_START_PROV_NOW_FLAG | ADD_DEV_FLUSHABLE_DEV_FLAG; + err = esp_ble_mesh_provisioner_add_unprov_dev(&add_dev, flag); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Failed to start provisioning a device", __func__); + return; + } + + if (!reprov) { + if (prov_info.max_node_num) { + prov_info.max_node_num--; + } + } + } +} + +static void example_provisioning_callback(esp_ble_mesh_prov_cb_event_t event, + esp_ble_mesh_prov_cb_param_t *param) +{ + switch (event) { + case ESP_BLE_MESH_PROV_REGISTER_COMP_EVT: + ESP_LOGI(TAG, "ESP_BLE_MESH_PROV_REGISTER_COMP_EVT, err_code: %d", + param->prov_register_comp.err_code); + break; + case ESP_BLE_MESH_PROVISIONER_PROV_ENABLE_COMP_EVT: + ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_PROV_ENABLE_COMP_EVT"); + break; + case ESP_BLE_MESH_PROVISIONER_RECV_UNPROV_ADV_PKT_EVT: + example_recv_unprov_adv_pkt(param->provisioner_recv_unprov_adv_pkt.dev_uuid, param->provisioner_recv_unprov_adv_pkt.addr, + param->provisioner_recv_unprov_adv_pkt.addr_type, param->provisioner_recv_unprov_adv_pkt.oob_info, + param->provisioner_recv_unprov_adv_pkt.adv_type, param->provisioner_recv_unprov_adv_pkt.bearer); + break; + case ESP_BLE_MESH_PROVISIONER_PROV_LINK_OPEN_EVT: + ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_PROV_LINK_OPEN_EVT, bearer %s", + param->provisioner_prov_link_open.bearer == ESP_BLE_MESH_PROV_ADV ? "PB-ADV" : "PB-GATT"); + provisioner_prov_link_open(param->provisioner_prov_link_open.bearer); + break; + case ESP_BLE_MESH_PROVISIONER_PROV_LINK_CLOSE_EVT: + ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_PROV_LINK_CLOSE_EVT, bearer %s reason 0x%02x", + param->provisioner_prov_link_close.bearer == ESP_BLE_MESH_PROV_ADV ? "PB-ADV" : "PB-GATT", + param->provisioner_prov_link_close.reason); + provisioner_prov_link_close(param->provisioner_prov_link_close.bearer, + param->provisioner_prov_link_close.reason); + break; + case ESP_BLE_MESH_PROVISIONER_PROV_COMPLETE_EVT: + ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_PROV_COMPLETE_EVT"); + provisioner_prov_complete(param->provisioner_prov_complete.node_idx, + param->provisioner_prov_complete.device_uuid, + param->provisioner_prov_complete.unicast_addr, + param->provisioner_prov_complete.element_num, + param->provisioner_prov_complete.netkey_idx); + break; + case ESP_BLE_MESH_PROVISIONER_ADD_UNPROV_DEV_COMP_EVT: + ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_ADD_UNPROV_DEV_COMP_EVT, err_code: %d", + param->provisioner_add_unprov_dev_comp.err_code); + break; + case ESP_BLE_MESH_PROVISIONER_SET_DEV_UUID_MATCH_COMP_EVT: + ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_SET_DEV_UUID_MATCH_COMP_EVT, err_code: %d", + param->provisioner_set_dev_uuid_match_comp.err_code); + break; + case ESP_BLE_MESH_PROVISIONER_SET_NODE_NAME_COMP_EVT: + ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_SET_NODE_NAME_COMP_EVT, err_code: %d", + param->provisioner_set_node_name_comp.err_code); + break; + case ESP_BLE_MESH_PROVISIONER_ADD_LOCAL_APP_KEY_COMP_EVT: { + ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_ADD_LOCAL_APP_KEY_COMP_EVT, err_code %d", param->provisioner_add_app_key_comp.err_code); + if (param->provisioner_add_app_key_comp.err_code == ESP_OK) { + esp_err_t err; + prov_info.app_idx = param->provisioner_add_app_key_comp.app_idx; + err = esp_ble_mesh_provisioner_bind_app_key_to_local_model(PROV_OWN_ADDR, prov_info.app_idx, + ESP_BLE_MESH_MODEL_ID_GEN_ONOFF_CLI, CID_NVAL); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Failed to bind AppKey with OnOff Client Model", __func__); + return; + } + err = esp_ble_mesh_provisioner_bind_app_key_to_local_model(PROV_OWN_ADDR, prov_info.app_idx, + ESP_BLE_MESH_VND_MODEL_ID_FAST_PROV_CLI, CID_ESP); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Failed to bind AppKey with Fast Prov Client Model", __func__); + return; + } + } + break; + } + case ESP_BLE_MESH_PROVISIONER_BIND_APP_KEY_TO_MODEL_COMP_EVT: + ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_BIND_APP_KEY_TO_MODEL_COMP_EVT, err_code %d", param->provisioner_bind_app_key_to_model_comp.err_code); + break; + default: + break; + } + return; +} + +static void example_custom_model_callback(esp_ble_mesh_model_cb_event_t event, + esp_ble_mesh_model_cb_param_t *param) +{ + uint32_t opcode; + esp_err_t err; + + switch (event) { + case ESP_BLE_MESH_MODEL_OPERATION_EVT: { + if (!param->model_operation.model || !param->model_operation.model->op || + !param->model_operation.ctx) { + ESP_LOGE(TAG, "%s: model_operation parameter is NULL", __func__); + return; + } + opcode = param->model_operation.opcode; + switch (opcode) { + case ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_STATUS: + case ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NET_KEY_STATUS: + case ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_STATUS: { + ESP_LOGI(TAG, "%s: Fast Prov Client Model receives status, opcode 0x%04x", __func__, opcode); + err = example_fast_prov_client_recv_status(param->model_operation.model, + param->model_operation.ctx, + param->model_operation.length, + param->model_operation.msg); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Failed to handle fast prov status message", __func__); + return; + } + break; + } + default: + ESP_LOGI(TAG, "%s: opcode 0x%04x", __func__, param->model_operation.opcode); + break; + } + break; + } + case ESP_BLE_MESH_MODEL_SEND_COMP_EVT: + ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_SEND_COMP_EVT, err_code %d", + param->model_send_comp.err_code); + break; + case ESP_BLE_MESH_MODEL_PUBLISH_COMP_EVT: + ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_PUBLISH_COMP_EVT, err_code %d", + param->model_publish_comp.err_code); + break; + case ESP_BLE_MESH_CLIENT_MODEL_RECV_PUBLISH_MSG_EVT: + ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_CLIENT_RECV_PUBLISH_MSG_EVT, opcode 0x%04x", + param->client_recv_publish_msg.opcode); + break; + case ESP_BLE_MESH_CLIENT_MODEL_SEND_TIMEOUT_EVT: + ESP_LOGI(TAG, "ESP_BLE_MESH_CLIENT_MODEL_SEND_TIMEOUT_EVT, opcode 0x%04x, dst 0x%04x", + param->client_send_timeout.opcode, param->client_send_timeout.ctx->addr); + err = example_fast_prov_client_recv_timeout(param->client_send_timeout.opcode, + param->client_send_timeout.model, + param->client_send_timeout.ctx); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Failed to resend fast prov client message", __func__); + return; + } + break; + default: + break; + } +} + +static void example_config_client_callback(esp_ble_mesh_cfg_client_cb_event_t event, + esp_ble_mesh_cfg_client_cb_param_t *param) +{ + example_node_info_t *node = NULL; + uint32_t opcode; + uint16_t address; + esp_err_t err; + + ESP_LOGI(TAG, "%s, error_code = 0x%02x, event = 0x%02x, addr: 0x%04x", + __func__, param->error_code, event, param->params->ctx.addr); + + opcode = param->params->opcode; + address = param->params->ctx.addr; + + node = example_get_node_info(address); + if (!node) { + ESP_LOGE(TAG, "%s: Failed to get node info", __func__); + return; + } + + if (param->error_code) { + ESP_LOGE(TAG, "Failed to send config client message, opcode: 0x%04x", opcode); + return; + } + + switch (event) { + case ESP_BLE_MESH_CFG_CLIENT_GET_STATE_EVT: + break; + case ESP_BLE_MESH_CFG_CLIENT_SET_STATE_EVT: + switch (opcode) { + case ESP_BLE_MESH_MODEL_OP_APP_KEY_ADD: { + example_fast_prov_info_set_t set = {0}; + if (!node->reprov || !ESP_BLE_MESH_ADDR_IS_UNICAST(node->unicast_min)) { + /* If the node is a new one or the node is re-provisioned but the information of the node + * has not been set before, here we will set the Fast Prov Info Set info to the node. + */ + node->node_addr_cnt = prov_info.node_addr_cnt; + node->unicast_min = prov_info.unicast_min; + node->unicast_max = prov_info.unicast_max; + node->flags = prov.flags; + node->iv_index = prov.iv_index; + node->fp_net_idx = prov_info.net_idx; + node->group_addr = prov_info.group_addr; + node->match_len = prov_info.match_len; + memcpy(node->match_val, prov_info.match_val, prov_info.match_len); + node->action = 0x81; + } + set.ctx_flags = 0x037F; + memcpy(&set.node_addr_cnt, &node->node_addr_cnt, + sizeof(example_node_info_t) - offsetof(example_node_info_t, node_addr_cnt)); + example_msg_common_info_t info = { + .net_idx = node->net_idx, + .app_idx = node->app_idx, + .dst = node->unicast_addr, + .timeout = 0, + .role = ROLE_PROVISIONER, + }; + err = example_send_fast_prov_info_set(fast_prov_client.model, &info, &set); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Failed to set Fast Prov Info Set message", __func__); + return; + } + break; + } + default: + break; + } + break; + case ESP_BLE_MESH_CFG_CLIENT_PUBLISH_EVT: + break; + case ESP_BLE_MESH_CFG_CLIENT_TIMEOUT_EVT: + switch (opcode) { + case ESP_BLE_MESH_MODEL_OP_APP_KEY_ADD: { + example_msg_common_info_t info = { + .net_idx = node->net_idx, + .app_idx = node->app_idx, + .dst = node->unicast_addr, + .timeout = 0, + .role = ROLE_PROVISIONER, + }; + esp_ble_mesh_cfg_app_key_add_t add_key = { + .net_idx = prov_info.net_idx, + .app_idx = prov_info.app_idx, + }; + memcpy(add_key.app_key, prov_info.app_key, 16); + err = example_send_config_appkey_add(config_client.model, &info, &add_key); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Failed to send Config AppKey Add message", __func__); + return; + } + break; + } + default: + break; + } + break; + default: + ESP_LOGE(TAG, "Not a config client status message event"); + break; + } +} + +static void example_generic_client_callback(esp_ble_mesh_generic_client_cb_event_t event, + esp_ble_mesh_generic_client_cb_param_t *param) +{ + example_node_info_t *node = NULL; + uint32_t opcode; + uint16_t address; + + ESP_LOGI(TAG, "%s, error_code = 0x%02x, event = 0x%02x, addr: 0x%04x", + __func__, param->error_code, event, param->params->ctx.addr); + + opcode = param->params->opcode; + address = param->params->ctx.addr; + + node = example_get_node_info(address); + if (!node) { + ESP_LOGE(TAG, "%s: Failed to get node info", __func__); + return; + } + + if (param->error_code) { + ESP_LOGE(TAG, "Failed to send generic client message, opcode: 0x%04x", opcode); + return; + } + + switch (event) { + case ESP_BLE_MESH_GENERIC_CLIENT_GET_STATE_EVT: + break; + case ESP_BLE_MESH_GENERIC_CLIENT_SET_STATE_EVT: + switch (opcode) { + case ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET: + node->onoff = param->status_cb.onoff_status.present_onoff; + ESP_LOGI(TAG, "node->onoff: 0x%02x", node->onoff); + break; + default: + break; + } + break; + case ESP_BLE_MESH_GENERIC_CLIENT_PUBLISH_EVT: + break; + case ESP_BLE_MESH_GENERIC_CLIENT_TIMEOUT_EVT: + break; + default: + ESP_LOGE(TAG, "Not a generic client status message event"); + break; + } +} + +static esp_err_t ble_mesh_init(void) +{ + esp_err_t err; + + memcpy(dev_uuid, esp_bt_dev_get_address(), 6); + + prov_info.unicast_min = prov.prov_start_address + prov_info.max_node_num; + prov_info.match_len = sizeof(match); + memcpy(prov_info.match_val, match, sizeof(match)); + memset(prov_info.app_key, APP_KEY_OCTET, sizeof(prov_info.app_key)); + + esp_ble_mesh_register_prov_callback(example_provisioning_callback); + esp_ble_mesh_register_custom_model_callback(example_custom_model_callback); + esp_ble_mesh_register_config_client_callback(example_config_client_callback); + esp_ble_mesh_register_generic_client_callback(example_generic_client_callback); + + err = esp_ble_mesh_provisioner_set_dev_uuid_match(match, 0x02, 0x00, false); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Failed to set matching device UUID", __func__); + return ESP_FAIL; + } + + err = esp_ble_mesh_init(&prov, &comp); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Failed to initialize BLE Mesh", __func__); + return ESP_FAIL; + } + + err = esp_ble_mesh_client_model_init(&vnd_models[0]); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Failed to initialize fast prov client model", __func__); + return ESP_FAIL; + } + + err = esp_ble_mesh_provisioner_prov_enable(ESP_BLE_MESH_PROV_ADV | ESP_BLE_MESH_PROV_GATT); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Failed to enable provisioning", __func__); + return ESP_FAIL; + } + + err = esp_ble_mesh_provisioner_add_local_app_key(prov_info.app_key, prov_info.net_idx, prov_info.app_idx); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Failed to add local application key", __func__); + return ESP_FAIL; + } + + ESP_LOGI(TAG, "BLE Mesh Provisioner initialized"); + + return err; +} + +esp_err_t bluetooth_init(void) +{ + esp_err_t ret; + + ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK(ret); + + ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT)); + + esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + ret = esp_bt_controller_init(&bt_cfg); + if (ret) { + ESP_LOGE(TAG, "%s initialize controller failed", __func__); + return ret; + } + + ret = esp_bt_controller_enable(ESP_BT_MODE_BLE); + if (ret) { + ESP_LOGE(TAG, "%s enable controller failed", __func__); + return ret; + } + ret = esp_bluedroid_init(); + if (ret) { + ESP_LOGE(TAG, "%s init bluetooth failed", __func__); + return ret; + } + ret = esp_bluedroid_enable(); + if (ret) { + ESP_LOGE(TAG, "%s enable bluetooth failed", __func__); + return ret; + } + + return ret; +} + +void app_main(void) +{ + int err; + + ESP_LOGI(TAG, "Initializing..."); + + err = bluetooth_init(); + if (err) { + ESP_LOGE(TAG, "esp32_bluetooth_init failed (err %d)", err); + return; + } + + /* Initialize the Bluetooth Mesh Subsystem */ + err = ble_mesh_init(); + if (err) { + ESP_LOGE(TAG, "Failed to initialize BLE Mesh (err %d)", err); + } +} diff --git a/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_client/main/component.mk b/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_client/main/component.mk new file mode 100644 index 0000000000..a98f634eae --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_client/main/component.mk @@ -0,0 +1,4 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_client/sdkconfig.defaults b/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_client/sdkconfig.defaults new file mode 100644 index 0000000000..a5c4750931 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_client/sdkconfig.defaults @@ -0,0 +1,43 @@ +# Override some defaults so BT stack is enabled +# by default in this example +CONFIG_BT_ENABLED=y +CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY=y +CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY= +CONFIG_BTDM_CONTROLLER_MODE_BTDM= +CONFIG_BTDM_CONTROLLER_MODEM_SLEEP=n +CONFIG_BLE_SCAN_DUPLICATE=y +CONFIG_SCAN_DUPLICATE_TYPE=2 +CONFIG_DUPLICATE_SCAN_CACHE_SIZE=200 +CONFIG_BLE_MESH_SCAN_DUPLICATE_EN=y +CONFIG_MESH_DUPLICATE_SCAN_CACHE_SIZE=200 +CONFIG_BTDM_CONTROLLER_FULL_SCAN_SUPPORTED=y +CONFIG_BLE_MESH=y +CONFIG_BLE_MESH_HCI_5_0=y +CONFIG_BLE_MESH_USE_DUPLICATE_SCAN=y +CONFIG_BLE_MESH_PROV=y +CONFIG_BLE_MESH_PROVISIONER=y +CONFIG_BLE_MESH_WAIT_FOR_PROV_MAX_DEV_NUM=10 +CONFIG_BLE_MESH_MAX_PROV_NODES=10 +CONFIG_BLE_MESH_MAX_STORED_NODES=10 +CONFIG_BLE_MESH_PBA_SAME_TIME=1 +CONFIG_BLE_MESH_PBG_SAME_TIME=1 +CONFIG_BLE_MESH_PROVISIONER_SUBNET_COUNT=3 +CONFIG_BLE_MESH_PROVISIONER_APP_KEY_COUNT=3 +CONFIG_BLE_MESH_PB_ADV=y +CONFIG_BLE_MESH_NET_BUF_POOL_USAGE=y +CONFIG_BLE_MESH_PB_GATT=y +CONFIG_BLE_MESH_GATT_PROXY=y +CONFIG_BLE_MESH_RELAY=y +CONFIG_BLE_MESH_LOW_POWER= +CONFIG_BLE_MESH_FRIEND= +CONFIG_BTU_TASK_STACK_SIZE=4512 +CONFIG_BLE_MESH_CFG_CLI=y +CONFIG_BLE_MESH_GENERIC_ONOFF_CLI=y +CONFIG_BLE_MESH_ADV_BUF_COUNT=100 +CONFIG_BLE_MESH_CRPL=10 +CONFIG_BLE_MESH_TX_SEG_MSG_COUNT=10 +CONFIG_BLE_MESH_RX_SEG_MSG_COUNT=10 +CONFIG_BLE_MESH_RX_SDU_MAX=384 +CONFIG_BLE_MESH_TX_SEG_MAX=32 +CONFIG_BLE_MESH_NO_LOG=n +CONFIG_BLE_MESH_STACK_TRACE_LEVEL=1 \ No newline at end of file diff --git a/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_client/tutorial/ble_mesh_fast_provision_client.md b/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_client/tutorial/ble_mesh_fast_provision_client.md new file mode 100644 index 0000000000..60d27304ee --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_client/tutorial/ble_mesh_fast_provision_client.md @@ -0,0 +1,218 @@ +# 1. Introduction +## 1.1 Demo Function + +This demo completes the following functions: + +1. Provisioning an unprovisioned device and change it to a node. +2. Binding the provisioner's Appkey to its own models. +3. Sending messages to the node about the Appkey and the fast provisioning information. +4. Getting the addresses of all the nodes in the fast provisioning network. +5. Controlling the nodes by their group address. + +**Note: The demo's functionality is similar to that of the EspBleMesh app.** + +## 1.2 Node Composition + +This demo has only one element, in which the following four models are implemented: + +- The **Configuration Server** model is used to represent a mesh network configuration of a device. +- The **Configuration Client** model is used to represent an element that can control and monitor the configuration of a node. +- The **Generic OnOff Client** model controls a Generic OnOff Server via messages defined by the **Generic OnOff** model (turning on and off the lights in this demo). +- The **Vendor Client** model is used to control the `fast_prov_server` state, which defines the fast provisioning behavior of a node. + +**Note: For detailed information about these models, please refer to other BLE Mesh demos.** + +## 2. Code Analysis + +Code initialization part reference [Initializing the Bluetooth and Initializing the BLE Mesh](../../../ble_mesh_wifi_coexist/tutorial%20%20%20%20%20%20/ble_mesh_wifi_coexist.md) + +### 2.1 Data Structure + +`example_prov_info_t` is used to define the keys, the address range can be assigned by a node, and the maximum number of nodes supported by the mesh network. + +| Name |Description | +| ----------------------|------------------------- | +| `net_idx` | Netkey index value | +| `app_idx` | AppKey index value | +| `app_key[16]` | Appkey, which is used throughout the network | +| `node_addr_cnt`| The maximum number of nodes supported in the mesh network,which serves the same purpose of the `Fast provisioning count` parameter in the EspBleMesh app| +| `unicast_min` | Minimum unicast address to be assigned to the nodes in the mesh network | +| `unicast_max` | Maximum unicast address to be assigned to the nodes in the mesh network | +| `group_addr`| The group address, which is used to control the on/off state of all nodes in the mesh network, that is said, turning on and off the lights in this demo| +| `match_val[16]`| The value used by the Fast Provisioning Provisioner to filter the devices to be provisioned | +| `match_len` | The maximum length of `match_val[16]` | +| `max_node_num` | The maximum number of nodes can be provisioned by the client | + +### 2.2 Code Flow + +The events and APIs in this section are presented in the same order with code execution. + +### 2.2.1 Initialization + +#### 2.2.1.1 Set the UUID Filter + +The `esp_ble_mesh_provisioner_set_dev_uuid_match` API is called by the provisioner to set the part of the device UUID to be compared before starting to provision. + +``` +/** + * @brief This function is called by Provisioner to set the part of the device UUID + * to be compared before starting to provision. + * + * @param[in] match_val: Value to be compared with the part of the device UUID. + * @param[in] match_len: Length of the compared match value. + * @param[in] offset: Offset of the device UUID to be compared (based on zero). + * @param[in] prov_after_match: Flag used to indicate whether provisioner should start to provision + * the device immediately if the part of the UUID matches. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_provisioner_set_dev_uuid_match(const uint8_t *match_val, uint8_t match_len, + uint8_t offset, bool prov_after_match); +``` + +```c +err = esp_ble_mesh_provisioner_set_dev_uuid_match(match, 0x02, 0x00, false); +if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Failed to set matching device UUID", __func__); + return ESP_FAIL; +} +``` + + + +#### 2.2.1.2 Add local Appkey + +The provisioner has no Appkey right after it has been initialized. Therefore, you have to add a local Appkey for the provisioner by calling the `esp_ble_mesh_provisioner_add_local_app_key`. + +```c +err = esp_ble_mesh_provisioner_add_local_app_key(prov_info.app_key, prov_info.net_idx, prov_info.app_idx); +if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Failed to add local application key", __func__); + return ESP_FAIL; +} +``` +Please check the return value of the API calling and the return value of `ESP_BLE_MESH_PROVISIONER_ADD_LOCAL_APP_KEY_COMP_EVT`, and make sure that the Appkey has been added to this provisioner. + +#### 2.2.1.3 Bind Appkey to local model + +To control the server model, the client model uses messages to control the server model and these message must be encrypted by the Appkey. To that end, users must bind the Appkey of the provisioner to its local models, which are the **Generic OnOff Client** model and the **Vendor Client** model, by calling the `esp_ble_mesh_provisioner_add_local_app_key` api. + +```c +prov_info.app_idx = param->provisioner_add_app_key_comp.app_idx; +err = esp_ble_mesh_provisioner_bind_app_key_to_local_model(PROV_OWN_ADDR, prov_info.app_idx, + ESP_BLE_MESH_MODEL_ID_GEN_ONOFF_CLI, CID_NVAL); +if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Failed to bind AppKey with OnOff Client Model", __func__); + return; +} +err = esp_ble_mesh_provisioner_bind_app_key_to_local_model(PROV_OWN_ADDR, prov_info.app_idx, + ESP_BLE_MESH_VND_MODEL_ID_FAST_PROV_CLI, CID_ESP); +if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Failed to bind AppKey with Fast Prov Client Model", __func__); + return; +} +``` +Please check the return value of the API calling and the return value of the `ESP_BLE_MESH_PROVISIONER_ADD_LOCAL_APP_KEY_COMP_EVT` event, and make sure that the Appkey has been binded to the local models. + + +### 2.2.2 Provisioning a device + +The unprovisioned devices continuously send the **Unprovisioned Device** beacon, which contains the value of its UUID. + +* If the UUID matched, a `ESP_BLE_MESH_PROVISIONER_RECV_UNPROV_ADV_PKT_EVT` event will be triggered, which will add the unprovisioned device information to the queue of to-be-provisioned devices. + + ```c + err = esp_ble_mesh_provisioner_add_unprov_dev(&add_dev, flag); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Failed to start provisioning a device", __func__); + return; + } + + if (!reprov) { + if (prov_info.max_node_num) { + prov_info.max_node_num--; + } + } + ``` +* If not, this device will be ignored. + +After that, all the devices in the queue will be provisioned automatically. + +### 2.2.3 Sending cache data + +Appkey is among the cache required for this node to become a provisioner. + +When the provisioning completes, an `ESP_BLE_MESH_PROVISIONER_PROV_COMPLETE_EVT` event will be triggered, which will add the Appkey to the node's **Config Server** model by calling the `esp_ble_mesh_config_client_set_state` API: + +```c +common.opcode = ESP_BLE_MESH_MODEL_OP_APP_KEY_ADD; +common.model = model; +common.ctx.net_idx = info->net_idx; +common.ctx.app_idx = 0x0000; /* not used for config messages */ +common.ctx.addr = info->dst; +common.ctx.send_rel = false; +common.ctx.send_ttl = 0; +common.msg_timeout = info->timeout; +common.msg_role = info->role; + +return esp_ble_mesh_config_client_set_state(&common, &set); +``` + +* If API calling succeeds, an `ESP_BLE_MESH_CFG_CLIENT_SET_STATE_EVT` event will be triggered, which sends other cache information (`example_fast_prov_info_set_t`) to the node's **Vendor Server** model by calling the `example_send_fast_prov_info_set` function; + * If API calling (`example_send_fast_prov_info_set`) succeeded, a message with an opcode of `ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_SET` will be sent, whose acknowledgement (with an opcode of `ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_STATUS`) will further trigger an `ESP_BLE_MESH_MODEL_OPERATION_EVT` event + ```c + err = example_send_fast_prov_info_set(fast_prov_client.model, &info, &set); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Failed to set Fast Prov Info Set message", __func__); + return; + } + ``` + * If API calling (`example_send_fast_prov_info_set`) times out, an `ESP_BLE_MESH_CLIENT_MODEL_SEND_TIMEOUT_EVT` event will be triggered. +* If API calling times out, an `ESP_BLE_MESH_CFG_CLIENT_TIMEOUT_EVT` event is triggered. + +After that, this node has the ability to provisioning other nodes as a provisioner, and further controls other nodes. + +**Note: The message with an opcode of `ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_SET` contains the group address of all the nodes. When a node receives this message, it will automatically subscribe the Onoff Server model of this address.** + +### 2.2.4 Controlling the node + +When the `ESP_BLE_MESH_MODEL_OPERATION_EVT` event is triggered, the provisioner starts a timer. + +```c + ESP_LOG_BUFFER_HEX("fast prov info status", data, len); +#if !defined(CONFIG_BLE_MESH_FAST_PROV) + prim_prov_addr = ctx->addr; + k_delayed_work_init(&get_all_node_addr_timer, example_get_all_node_addr); + k_delayed_work_submit(&get_all_node_addr_timer, GET_ALL_NODE_ADDR_TIMEOUT); +#endif + break; +``` +After the timers times out, the provisioner starts to get the addresses of all nodes in the mesh network by calling the `example_send_fast_prov_all_node_addr_get` function, which sends a message with an opcode of `ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_GET`. + +```c +err = example_send_fast_prov_all_node_addr_get(model, &info); +if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Failed to send Fast Prov Node Address Get message", __func__); + return; +} +``` + +After that, the provisioner will receive an acknowledgement, which is a message with an opcode of `ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_STATUS`, which triggers the `ESP_BLE_MESH_MODEL_OPERATION_EVT` event. + +Then, the provisioner is able to turn on all the nodes (which are lights in this demo) by calling the `example_send_generic_onoff_set` function using the group address. + +```c +example_msg_common_info_t info = { + .net_idx = node->net_idx, + .app_idx = node->app_idx, + .dst = node->group_addr, + .timeout = 0, + .role = ROLE_PROVISIONER, +}; +err = example_send_generic_onoff_set(cli_model, &info, LED_ON, 0x00, false); +if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Failed to send Generic OnOff Set Unack message", __func__); + return ESP_FAIL; +} +``` diff --git a/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/CMakeLists.txt b/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/CMakeLists.txt new file mode 100644 index 0000000000..594effdbb8 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/CMakeLists.txt @@ -0,0 +1,8 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/bluetooth/ble_mesh/ble_mesh_vendor_models/fast_prov_vendor_model/components) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(ble_mesh_fast_prov_server) diff --git a/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/Makefile b/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/Makefile new file mode 100644 index 0000000000..f6ece484ac --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/Makefile @@ -0,0 +1,12 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := ble_mesh_fast_prov_server + +COMPONENT_ADD_INCLUDEDIRS := components/include + +EXTRA_COMPONENT_DIRS := $(IDF_PATH)/examples/bluetooth/ble_mesh/ble_mesh_vendor_models/fast_prov_vendor_model/components + +include $(IDF_PATH)/make/project.mk diff --git a/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/README.md b/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/README.md new file mode 100644 index 0000000000..2746ed629a --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/README.md @@ -0,0 +1,2 @@ +ESP BLE Mesh Fast Provisioning Server Demo +======================== \ No newline at end of file diff --git a/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/main/CMakeLists.txt b/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/main/CMakeLists.txt new file mode 100644 index 0000000000..1eb2d87ed2 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/main/CMakeLists.txt @@ -0,0 +1,6 @@ +set(COMPONENT_SRCS "ble_mesh_demo_main.c" + "board.c") + +set(COMPONENT_ADD_INCLUDEDIRS ".") + +register_component() diff --git a/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/main/Kconfig.projbuild b/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/main/Kconfig.projbuild new file mode 100644 index 0000000000..e48a853e0b --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/main/Kconfig.projbuild @@ -0,0 +1,16 @@ +menu "Example Configuration" + + choice BLE_MESH_EXAMPLE_BOARD + prompt "Board selection for BLE Mesh" + default BLE_MESH_ESP_WROOM_32 + help + Select this option to choose the board for BLE Mesh. The default is ESP32-WROOM-32 + + config BLE_MESH_ESP_WROOM_32 + bool "ESP32-WROOM-32" + + config BLE_MESH_ESP_WROVER + bool "ESP32-WROVER" + endchoice + +endmenu diff --git a/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/main/ble_mesh_demo_main.c b/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/main/ble_mesh_demo_main.c new file mode 100644 index 0000000000..87bc00e1d0 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/main/ble_mesh_demo_main.c @@ -0,0 +1,842 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 + +#include "esp_log.h" +#include "nvs_flash.h" + +#include "esp_bt.h" +#include "esp_bt_main.h" +#include "esp_bt_device.h" + +#include "esp_ble_mesh_defs.h" +#include "esp_ble_mesh_common_api.h" +#include "esp_ble_mesh_networking_api.h" +#include "esp_ble_mesh_provisioning_api.h" +#include "esp_ble_mesh_config_model_api.h" +#include "esp_ble_mesh_generic_model_api.h" + +#include "board.h" +#include "esp_fast_prov_operation.h" +#include "esp_fast_prov_client_model.h" +#include "esp_fast_prov_server_model.h" + +#define TAG "FAST_PROV_SERVER_DEMO" + +extern struct _led_state led_state[3]; +extern struct k_delayed_work send_self_prov_node_addr_timer; +extern bt_mesh_atomic_t fast_prov_cli_flags; + +static uint8_t dev_uuid[16] = { 0xdd, 0xdd }; +static uint8_t prov_start_num = 0; +static bool prov_start = false; + +static const esp_ble_mesh_client_op_pair_t fast_prov_cli_op_pair[] = { + { ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_SET, ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_STATUS }, + { ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NET_KEY_ADD, ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NET_KEY_STATUS }, + { ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR, ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_ACK }, + { ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_GET, ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_STATUS }, +}; + +/* Configuration Client Model user_data */ +esp_ble_mesh_client_t config_client; + +/* Configuration Server Model user_data */ +esp_ble_mesh_cfg_srv_t config_server = { + .relay = ESP_BLE_MESH_RELAY_ENABLED, + .beacon = ESP_BLE_MESH_BEACON_DISABLED, +#if defined(CONFIG_BLE_MESH_FRIEND) + .friend_state = ESP_BLE_MESH_FRIEND_ENABLED, +#else + .friend_state = ESP_BLE_MESH_FRIEND_NOT_SUPPORTED, +#endif +#if defined(CONFIG_BLE_MESH_GATT_PROXY) + .gatt_proxy = ESP_BLE_MESH_GATT_PROXY_ENABLED, +#else + .gatt_proxy = ESP_BLE_MESH_GATT_PROXY_NOT_SUPPORTED, +#endif + .default_ttl = 7, + /* 3 transmissions with 20ms interval */ + .net_transmit = ESP_BLE_MESH_TRANSMIT(2, 20), + .relay_retransmit = ESP_BLE_MESH_TRANSMIT(2, 20), +}; + +/* Fast Prov Client Model user_data */ +esp_ble_mesh_client_t fast_prov_client = { + .op_pair_size = ARRAY_SIZE(fast_prov_cli_op_pair), + .op_pair = fast_prov_cli_op_pair, +}; + +/* Fast Prov Server Model user_data */ +example_fast_prov_server_t fast_prov_server = { + .primary_role = false, + .max_node_num = 6, + .prov_node_cnt = 0x0, + .unicast_min = ESP_BLE_MESH_ADDR_UNASSIGNED, + .unicast_max = ESP_BLE_MESH_ADDR_UNASSIGNED, + .unicast_cur = ESP_BLE_MESH_ADDR_UNASSIGNED, + .unicast_step = 0x0, + .flags = 0x0, + .iv_index = 0x0, + .net_idx = ESP_BLE_MESH_KEY_UNUSED, + .app_idx = ESP_BLE_MESH_KEY_UNUSED, + .group_addr = ESP_BLE_MESH_ADDR_UNASSIGNED, + .prim_prov_addr = ESP_BLE_MESH_ADDR_UNASSIGNED, + .match_len = 0x0, + .pend_act = FAST_PROV_ACT_NONE, + .state = STATE_IDLE, +}; + +static esp_ble_mesh_model_op_t onoff_op[] = { + ESP_BLE_MESH_MODEL_OP(ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_GET, 0), + ESP_BLE_MESH_MODEL_OP(ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET, 2), + ESP_BLE_MESH_MODEL_OP(ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK, 2), + ESP_BLE_MESH_MODEL_OP_END, +}; + +ESP_BLE_MESH_MODEL_PUB_DEFINE(onoff_pub, 2 + 1, ROLE_FAST_PROV); + +static esp_ble_mesh_model_op_t fast_prov_srv_op[] = { + ESP_BLE_MESH_MODEL_OP(ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_SET, 3), + ESP_BLE_MESH_MODEL_OP(ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NET_KEY_ADD, 16), + ESP_BLE_MESH_MODEL_OP(ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR, 2), + ESP_BLE_MESH_MODEL_OP(ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_GET, 0), + ESP_BLE_MESH_MODEL_OP(ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_GROUP_ADD, 2), + ESP_BLE_MESH_MODEL_OP(ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_GROUP_DELETE, 2), + ESP_BLE_MESH_MODEL_OP_END, +}; + +static esp_ble_mesh_model_op_t fast_prov_cli_op[] = { + ESP_BLE_MESH_MODEL_OP(ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_STATUS, 1), + ESP_BLE_MESH_MODEL_OP(ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NET_KEY_STATUS, 2), + ESP_BLE_MESH_MODEL_OP(ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_ACK, 0), + ESP_BLE_MESH_MODEL_OP_END, +}; + +static esp_ble_mesh_model_t root_models[] = { + ESP_BLE_MESH_MODEL_CFG_SRV(&config_server), + ESP_BLE_MESH_MODEL_CFG_CLI(&config_client), + ESP_BLE_MESH_SIG_MODEL(ESP_BLE_MESH_MODEL_ID_GEN_ONOFF_SRV, onoff_op, + &onoff_pub, &led_state[1]), +}; + +static esp_ble_mesh_model_t vnd_models[] = { + ESP_BLE_MESH_VENDOR_MODEL(CID_ESP, ESP_BLE_MESH_VND_MODEL_ID_FAST_PROV_SRV, + fast_prov_srv_op, NULL, &fast_prov_server), + ESP_BLE_MESH_VENDOR_MODEL(CID_ESP, ESP_BLE_MESH_VND_MODEL_ID_FAST_PROV_CLI, + fast_prov_cli_op, NULL, &fast_prov_client), +}; + +static esp_ble_mesh_elem_t elements[] = { + ESP_BLE_MESH_ELEMENT(0, root_models, vnd_models), +}; + +static esp_ble_mesh_comp_t comp = { + .cid = CID_ESP, + .elements = elements, + .element_count = ARRAY_SIZE(elements), +}; + +static esp_ble_mesh_prov_t prov = { + .uuid = dev_uuid, + .output_size = 0, + .output_actions = 0, + .prov_attention = 0x00, + .prov_algorithm = 0x00, + .prov_pub_key_oob = 0x00, + .prov_static_oob_val = NULL, + .prov_static_oob_len = 0x00, + .flags = 0x00, + .iv_index = 0x00, +}; + +static void gen_onoff_get_handler(esp_ble_mesh_model_t *model, + esp_ble_mesh_msg_ctx_t *ctx, + uint16_t len, uint8_t *data) +{ + struct _led_state *led = NULL; + uint8_t send_data; + esp_err_t err; + + led = (struct _led_state *)model->user_data; + if (!led) { + ESP_LOGE(TAG, "%s: Failed to get generic onoff server model user_data", __func__); + return; + } + + send_data = led->current; + + /* Sends Generic OnOff Status as a reponse to Generic OnOff Get */ + err = esp_ble_mesh_server_model_send_msg(model, ctx, ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_STATUS, + sizeof(send_data), &send_data); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Failed to send Generic OnOff Status message", __func__); + return; + } + + /* When the node receives the first Generic OnOff Get/Set/Set Unack message, it will + * start the timer used to disable fast provisioning functionality. + */ + if (!bt_mesh_atomic_test_and_set_bit(fast_prov_server.srv_flags, DISABLE_FAST_PROV_START)) { + k_delayed_work_submit(&fast_prov_server.disable_fast_prov_timer, DISABLE_FAST_PROV_TIMEOUT); + } +} + +static void gen_onoff_set_unack_handler(esp_ble_mesh_model_t *model, + esp_ble_mesh_msg_ctx_t *ctx, + uint16_t len, uint8_t *data) +{ + struct _led_state *led = NULL; + + led = (struct _led_state *)model->user_data; + if (!led) { + ESP_LOGE(TAG, "%s: Failed to get generic onoff server model user_data", __func__); + return; + } + + led->current = data[0]; + gpio_set_level(led->pin, led->current); + + /* When the node receives the first Generic OnOff Get/Set/Set Unack message, it will + * start the timer used to disable fast provisioning functionality. + */ + if (!bt_mesh_atomic_test_and_set_bit(fast_prov_server.srv_flags, DISABLE_FAST_PROV_START)) { + k_delayed_work_submit(&fast_prov_server.disable_fast_prov_timer, DISABLE_FAST_PROV_TIMEOUT); + } +} + +static void gen_onoff_set_handler(esp_ble_mesh_model_t *model, + esp_ble_mesh_msg_ctx_t *ctx, + uint16_t len, uint8_t *data) +{ + gen_onoff_set_unack_handler(model, ctx, len, data); + gen_onoff_get_handler(model, ctx, len, data); +} + +static void node_prov_complete(uint16_t net_idx, uint16_t addr, uint8_t flags, uint32_t iv_index) +{ + ESP_LOGI(TAG, "net_idx: 0x%04x, unicast_addr: 0x%04x", net_idx, addr); + ESP_LOGI(TAG, "flags: 0x%02x, iv_index: 0x%08x", flags, iv_index); + board_prov_complete(); + /* Updates the net_idx used by Fast Prov Server model, and it can also + * be updated if the Fast Prov Info Set message contains a valid one. + */ + fast_prov_server.net_idx = net_idx; +} + +static void provisioner_prov_link_open(esp_ble_mesh_prov_bearer_t bearer) +{ + ESP_LOGI(TAG, "%s: bearer %s", __func__, bearer == ESP_BLE_MESH_PROV_ADV ? "PB-ADV" : "PB-GATT"); +} + +static void provisioner_prov_link_close(esp_ble_mesh_prov_bearer_t bearer, uint8_t reason) +{ + ESP_LOGI(TAG, "%s: bearer %s, reason 0x%02x", __func__, + bearer == ESP_BLE_MESH_PROV_ADV ? "PB-ADV" : "PB-GATT", reason); + if (prov_start_num) { + prov_start_num--; + } +} + +static void provisioner_prov_complete(int node_idx, const uint8_t uuid[16], uint16_t unicast_addr, + uint8_t element_num, uint16_t net_idx) +{ + example_node_info_t *node = NULL; + esp_err_t err; + + if (example_is_node_exist(uuid) == false) { + fast_prov_server.prov_node_cnt++; + } + + ESP_LOG_BUFFER_HEX("Device uuid", uuid + 2, 6); + ESP_LOGI(TAG, "Unicast address 0x%04x", unicast_addr); + + /* Sets node info */ + err = example_store_node_info(uuid, unicast_addr, element_num, net_idx, + fast_prov_server.app_idx, LED_OFF); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Failed to set node info", __func__); + return; + } + + /* Gets node info */ + node = example_get_node_info(unicast_addr); + if (!node) { + ESP_LOGE(TAG, "%s: Failed to get node info", __func__); + return; + } + + if (fast_prov_server.primary_role == true) { + /* If the Provisioner is the primary one (i.e. provisioned by the phone), it shall + * store self-provisioned node addresses; + * If the node_addr_cnt configured by the phone is small than or equal to the + * maximum number of nodes it can provision, it shall reset the timer which is used + * to send all node addresses to the phone. + */ + err = example_store_remote_node_address(unicast_addr); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Failed to store node address 0x%04x", __func__, unicast_addr); + return; + } + if (fast_prov_server.node_addr_cnt != FAST_PROV_NODE_COUNT_MIN && + fast_prov_server.node_addr_cnt <= fast_prov_server.max_node_num) { + if (bt_mesh_atomic_test_and_clear_bit(fast_prov_server.srv_flags, GATT_PROXY_ENABLE_START)) { + k_delayed_work_cancel(&fast_prov_server.gatt_proxy_enable_timer); + } + if (!bt_mesh_atomic_test_and_set_bit(fast_prov_server.srv_flags, GATT_PROXY_ENABLE_START)) { + k_delayed_work_submit(&fast_prov_server.gatt_proxy_enable_timer, GATT_PROXY_ENABLE_TIMEOUT); + } + } + } else { + /* When a device is provisioned, the non-primary Provisioner shall reset the timer + * which is used to send node addresses to the primary Provisioner. + */ + if (bt_mesh_atomic_test_and_clear_bit(&fast_prov_cli_flags, SEND_SELF_PROV_NODE_ADDR_START)) { + k_delayed_work_cancel(&send_self_prov_node_addr_timer); + } + if (!bt_mesh_atomic_test_and_set_bit(&fast_prov_cli_flags, SEND_SELF_PROV_NODE_ADDR_START)) { + k_delayed_work_submit(&send_self_prov_node_addr_timer, SEND_SELF_PROV_NODE_ADDR_TIMEOUT); + } + } + + if (bt_mesh_atomic_test_bit(fast_prov_server.srv_flags, DISABLE_FAST_PROV_START)) { + /* When a device is provisioned, and the stop_prov flag of the Provisioner has been + * set, the Provisioner shall reset the timer which is used to stop the provisioner + * functionality. + */ + k_delayed_work_cancel(&fast_prov_server.disable_fast_prov_timer); + k_delayed_work_submit(&fast_prov_server.disable_fast_prov_timer, DISABLE_FAST_PROV_TIMEOUT); + } + + /* The Provisioner will send Config AppKey Add to the node. */ + example_msg_common_info_t info = { + .net_idx = node->net_idx, + .app_idx = node->app_idx, + .dst = node->unicast_addr, + .timeout = 0, + .role = ROLE_FAST_PROV, + }; + err = example_send_config_appkey_add(config_client.model, &info, NULL); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Failed to send Config AppKey Add message", __func__); + return; + } +} + +static void example_recv_unprov_adv_pkt(uint8_t dev_uuid[16], uint8_t addr[ESP_BD_ADDR_LEN], + esp_ble_addr_type_t addr_type, uint16_t oob_info, + uint8_t adv_type, esp_ble_mesh_prov_bearer_t bearer) +{ + esp_ble_mesh_unprov_dev_add_t add_dev = {0}; + esp_ble_mesh_dev_add_flag_t flag; + esp_err_t err; + + /* In Fast Provisioning, the Provisioner should only use PB-ADV to provision devices. */ + if (prov_start && (bearer & ESP_BLE_MESH_PROV_ADV)) { + /* Checks if the device is a reprovisioned one. */ + if (example_is_node_exist(dev_uuid) == false) { + if ((prov_start_num >= fast_prov_server.max_node_num) || + (fast_prov_server.prov_node_cnt >= fast_prov_server.max_node_num)) { + return; + } + } + + add_dev.addr_type = (uint8_t)addr_type; + add_dev.oob_info = oob_info; + add_dev.bearer = (uint8_t)bearer; + memcpy(add_dev.uuid, dev_uuid, 16); + memcpy(add_dev.addr, addr, ESP_BD_ADDR_LEN); + flag = ADD_DEV_RM_AFTER_PROV_FLAG | ADD_DEV_START_PROV_NOW_FLAG | ADD_DEV_FLUSHABLE_DEV_FLAG; + err = esp_ble_mesh_provisioner_add_unprov_dev(&add_dev, flag); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Failed to start provisioning device", __func__); + return; + } + + /* If adding unprovisioned device successfully, increase prov_start_num */ + prov_start_num++; + } + + return; +} + +static void example_ble_mesh_provisioning_cb(esp_ble_mesh_prov_cb_event_t event, + esp_ble_mesh_prov_cb_param_t *param) +{ + esp_err_t err; + + switch (event) { + case ESP_BLE_MESH_PROV_REGISTER_COMP_EVT: + ESP_LOGI(TAG, "ESP_BLE_MESH_PROV_REGISTER_COMP_EVT, err_code: %d", + param->prov_register_comp.err_code); + break; + case ESP_BLE_MESH_NODE_PROV_ENABLE_COMP_EVT: + ESP_LOGI(TAG, "ESP_BLE_MESH_NODE_PROV_ENABLE_COMP_EVT, err_code: %d", + param->node_prov_enable_comp.err_code); + break; + case ESP_BLE_MESH_NODE_PROV_LINK_OPEN_EVT: + ESP_LOGI(TAG, "ESP_BLE_MESH_NODE_PROV_LINK_OPEN_EVT, bearer: %s", + param->node_prov_link_open.bearer == ESP_BLE_MESH_PROV_ADV ? "PB-ADV" : "PB-GATT"); + break; + case ESP_BLE_MESH_NODE_PROV_LINK_CLOSE_EVT: + ESP_LOGI(TAG, "ESP_BLE_MESH_NODE_PROV_LINK_CLOSE_EVT, bearer: %s", + param->node_prov_link_close.bearer == ESP_BLE_MESH_PROV_ADV ? "PB-ADV" : "PB-GATT"); + break; + case ESP_BLE_MESH_NODE_PROV_COMPLETE_EVT: + ESP_LOGI(TAG, "ESP_BLE_MESH_NODE_PROV_COMPLETE_EVT"); + node_prov_complete(param->node_prov_complete.net_idx, param->node_prov_complete.addr, + param->node_prov_complete.flags, param->node_prov_complete.iv_index); + break; + case ESP_BLE_MESH_NODE_PROXY_GATT_DISABLE_COMP_EVT: + ESP_LOGI(TAG, "ESP_BLE_MESH_NODE_PROXY_GATT_DISABLE_COMP_EVT"); + if (fast_prov_server.primary_role == true) { + config_server.relay = ESP_BLE_MESH_RELAY_DISABLED; + } + prov_start = true; + break; + case ESP_BLE_MESH_PROVISIONER_RECV_UNPROV_ADV_PKT_EVT: + example_recv_unprov_adv_pkt(param->provisioner_recv_unprov_adv_pkt.dev_uuid, param->provisioner_recv_unprov_adv_pkt.addr, + param->provisioner_recv_unprov_adv_pkt.addr_type, param->provisioner_recv_unprov_adv_pkt.oob_info, + param->provisioner_recv_unprov_adv_pkt.adv_type, param->provisioner_recv_unprov_adv_pkt.bearer); + break; + case ESP_BLE_MESH_PROVISIONER_PROV_LINK_OPEN_EVT: + ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_PROV_LINK_OPEN_EVT"); + provisioner_prov_link_open(param->provisioner_prov_link_open.bearer); + break; + case ESP_BLE_MESH_PROVISIONER_PROV_LINK_CLOSE_EVT: + ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_PROV_LINK_CLOSE_EVT"); + provisioner_prov_link_close(param->provisioner_prov_link_close.bearer, + param->provisioner_prov_link_close.reason); + break; + case ESP_BLE_MESH_PROVISIONER_PROV_COMPLETE_EVT: + ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_PROV_COMPLETE_EVT"); + provisioner_prov_complete(param->provisioner_prov_complete.node_idx, + param->provisioner_prov_complete.device_uuid, + param->provisioner_prov_complete.unicast_addr, + param->provisioner_prov_complete.element_num, + param->provisioner_prov_complete.netkey_idx); + break; + case ESP_BLE_MESH_PROVISIONER_ADD_UNPROV_DEV_COMP_EVT: + ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_ADD_UNPROV_DEV_COMP_EVT, err_code: %d", + param->provisioner_add_unprov_dev_comp.err_code); + break; + case ESP_BLE_MESH_PROVISIONER_SET_DEV_UUID_MATCH_COMP_EVT: + ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_SET_DEV_UUID_MATCH_COMP_EVT, err_code: %d", + param->provisioner_set_dev_uuid_match_comp.err_code); + break; + case ESP_BLE_MESH_PROVISIONER_SET_NODE_NAME_COMP_EVT: + ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_SET_NODE_NAME_COMP_EVT, err_code: %d", + param->provisioner_set_node_name_comp.err_code); + break; + case ESP_BLE_MESH_SET_FAST_PROV_INFO_COMP_EVT: { + ESP_LOGI(TAG, "ESP_BLE_MESH_SET_FAST_PROV_INFO_COMP_EVT"); + ESP_LOGI(TAG, "status_unicast: 0x%02x, status_net_idx: 0x%02x, status_match 0x%02x", + param->set_fast_prov_info_comp.status_unicast, + param->set_fast_prov_info_comp.status_net_idx, + param->set_fast_prov_info_comp.status_match); + err = example_handle_fast_prov_info_set_comp_evt(fast_prov_server.model, + param->set_fast_prov_info_comp.status_unicast, + param->set_fast_prov_info_comp.status_net_idx, + param->set_fast_prov_info_comp.status_match); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Failed to handle Fast Prov Info Set complete event", __func__); + return; + } + break; + } + case ESP_BLE_MESH_SET_FAST_PROV_ACTION_COMP_EVT: + ESP_LOGI(TAG, "ESP_BLE_MESH_SET_FAST_PROV_ACTION_COMP_EVT, status_action 0x%02x", + param->set_fast_prov_action_comp.status_action); + err = example_handle_fast_prov_action_set_comp_evt(fast_prov_server.model, + param->set_fast_prov_action_comp.status_action); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Failed to handle Fast Prov Action Set complete event", __func__); + return; + } + break; + default: + break; + } + + return; +} + +static void example_ble_mesh_custom_model_cb(esp_ble_mesh_model_cb_event_t event, + esp_ble_mesh_model_cb_param_t *param) +{ + uint32_t opcode; + esp_err_t err; + + switch (event) { + case ESP_BLE_MESH_MODEL_OPERATION_EVT: { + if (!param->model_operation.model || !param->model_operation.model->op || + !param->model_operation.ctx) { + ESP_LOGE(TAG, "%s: model_operation parameter is NULL", __func__); + return; + } + opcode = param->model_operation.opcode; + switch (opcode) { + case ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET: + gen_onoff_set_handler(param->model_operation.model, param->model_operation.ctx, + param->model_operation.length, param->model_operation.msg); + break; + case ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK: + gen_onoff_set_unack_handler(param->model_operation.model, param->model_operation.ctx, + param->model_operation.length, param->model_operation.msg); + break; + case ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_SET: + case ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NET_KEY_ADD: + case ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR: + case ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_GET: + case ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_GROUP_ADD: + case ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_GROUP_DELETE: { + ESP_LOGI(TAG, "%s: Fast prov server receives msg, opcode 0x%04x", __func__, opcode); + struct net_buf_simple buf = { + .len = param->model_operation.length, + .data = param->model_operation.msg, + }; + err = example_fast_prov_server_recv_msg(param->model_operation.model, + param->model_operation.ctx, &buf); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Failed to handle fast prov client message", __func__); + return; + } + break; + } + case ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_STATUS: + case ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NET_KEY_STATUS: + case ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_ACK: { + ESP_LOGI(TAG, "%s: Fast prov client receives msg, opcode 0x%04x", __func__, opcode); + err = example_fast_prov_client_recv_status(param->model_operation.model, + param->model_operation.ctx, + param->model_operation.length, + param->model_operation.msg); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Failed to handle fast prov server message", __func__); + return; + } + break; + } + default: + ESP_LOGI(TAG, "%s: opcode 0x%04x", __func__, param->model_operation.opcode); + break; + } + break; + } + case ESP_BLE_MESH_MODEL_SEND_COMP_EVT: + ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_SEND_COMP_EVT, err_code %d", param->model_send_comp.err_code); + switch (param->model_send_comp.opcode) { + case ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_STATUS: + case ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NET_KEY_STATUS: + case ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_ACK: + case ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_STATUS: + err = example_handle_fast_prov_status_send_comp_evt(param->model_send_comp.err_code, + param->model_send_comp.opcode, + param->model_send_comp.model, + param->model_send_comp.ctx); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Failed to handle fast prov status send complete event", __func__); + return; + } + break; + default: + break; + } + break; + case ESP_BLE_MESH_MODEL_PUBLISH_COMP_EVT: + ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_PUBLISH_COMP_EVT, err_code %d", + param->model_publish_comp.err_code); + break; + case ESP_BLE_MESH_CLIENT_MODEL_RECV_PUBLISH_MSG_EVT: + ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_CLIENT_RECV_PUBLISH_MSG_EVT, opcode 0x%04x", + param->client_recv_publish_msg.opcode); + break; + case ESP_BLE_MESH_CLIENT_MODEL_SEND_TIMEOUT_EVT: + ESP_LOGI(TAG, "ESP_BLE_MESH_CLIENT_MODEL_SEND_TIMEOUT_EVT, opcode 0x%04x, dst 0x%04x", + param->client_send_timeout.opcode, param->client_send_timeout.ctx->addr); + err = example_fast_prov_client_recv_timeout(param->client_send_timeout.opcode, + param->client_send_timeout.model, + param->client_send_timeout.ctx); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Faield to resend fast prov client message", __func__); + return; + } + break; + default: + break; + } +} + +static void example_ble_mesh_config_client_cb(esp_ble_mesh_cfg_client_cb_event_t event, + esp_ble_mesh_cfg_client_cb_param_t *param) +{ + example_node_info_t *node = NULL; + uint32_t opcode; + uint16_t address; + esp_err_t err; + + ESP_LOGI(TAG, "%s, error_code = 0x%02x, event = 0x%02x, addr: 0x%04x", + __func__, param->error_code, event, param->params->ctx.addr); + + opcode = param->params->opcode; + address = param->params->ctx.addr; + + node = example_get_node_info(address); + if (!node) { + ESP_LOGE(TAG, "%s: Failed to get node info", __func__); + return; + } + + if (param->error_code) { + ESP_LOGE(TAG, "Failed to send config client message, opcode: 0x%04x", opcode); + return; + } + + switch (event) { + case ESP_BLE_MESH_CFG_CLIENT_GET_STATE_EVT: + break; + case ESP_BLE_MESH_CFG_CLIENT_SET_STATE_EVT: + switch (opcode) { + case ESP_BLE_MESH_MODEL_OP_APP_KEY_ADD: { + example_fast_prov_info_set_t set = {0}; + if (node->reprov == false) { + /* After sending Config AppKey Add successfully, start to send Fast Prov Info Set */ + if (fast_prov_server.unicast_cur >= fast_prov_server.unicast_max) { + /* TODO: + * 1. If unicast_cur is >= unicast_max, we can also send the message to enable + * the Provisioner functionality on the node, and need to add another vendor + * message used by the node to require a new unicast address range from primary + * Provisioner, and before get the correct response, the node should pend + * the fast provisioning functionality. + * 2. Currently if address is not enough, the Provisioner will only add the group + * address to the node. + */ + ESP_LOGW(TAG, "%s: Not enough address to be assigned", __func__); + node->lack_of_addr = true; + } else { + /* Send fast_prov_info_set message to node */ + node->lack_of_addr = false; + node->unicast_min = fast_prov_server.unicast_cur; + if (fast_prov_server.unicast_cur + fast_prov_server.unicast_step >= fast_prov_server.unicast_max) { + node->unicast_max = fast_prov_server.unicast_max; + } else { + node->unicast_max = fast_prov_server.unicast_cur + fast_prov_server.unicast_step; + } + node->flags = fast_prov_server.flags; + node->iv_index = fast_prov_server.iv_index; + node->fp_net_idx = fast_prov_server.net_idx; + node->group_addr = fast_prov_server.group_addr; + node->prov_addr = fast_prov_server.prim_prov_addr; + node->match_len = fast_prov_server.match_len; + memcpy(node->match_val, fast_prov_server.match_val, fast_prov_server.match_len); + node->action = FAST_PROV_ACT_ENTER; + fast_prov_server.unicast_cur = node->unicast_max + 1; + } + } + if (node->lack_of_addr == false) { + set.ctx_flags = 0x03FE; + memcpy(&set.unicast_min, &node->unicast_min, + sizeof(example_node_info_t) - offsetof(example_node_info_t, unicast_min)); + } else { + set.ctx_flags = BIT(6); + set.group_addr = fast_prov_server.group_addr; + } + example_msg_common_info_t info = { + .net_idx = node->net_idx, + .app_idx = node->app_idx, + .dst = node->unicast_addr, + .timeout = 0, + .role = ROLE_FAST_PROV, + }; + err = example_send_fast_prov_info_set(fast_prov_client.model, &info, &set); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Failed to send Fast Prov Info Set message", __func__); + return; + } + break; + } + default: + break; + } + break; + case ESP_BLE_MESH_CFG_CLIENT_PUBLISH_EVT: + break; + case ESP_BLE_MESH_CFG_CLIENT_TIMEOUT_EVT: + switch (opcode) { + case ESP_BLE_MESH_MODEL_OP_APP_KEY_ADD: { + example_msg_common_info_t info = { + .net_idx = node->net_idx, + .app_idx = node->app_idx, + .dst = node->unicast_addr, + .timeout = 0, + .role = ROLE_FAST_PROV, + }; + err = example_send_config_appkey_add(config_client.model, &info, NULL); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Failed to send Config AppKey Add message", __func__); + return; + } + break; + } + default: + break; + } + break; + default: + return; + } +} + +static void example_ble_mesh_config_server_cb(esp_ble_mesh_cfg_server_cb_event_t event, + esp_ble_mesh_cfg_server_cb_param_t *param) +{ + esp_err_t err; + + ESP_LOGI(TAG, "%s, event = 0x%02x, opcode = 0x%04x, addr: 0x%04x", + __func__, event, param->ctx.recv_op, param->ctx.addr); + + switch (event) { + case ESP_BLE_MESH_CFG_SERVER_RECV_MSG_EVT: + switch (param->ctx.recv_op) { + case ESP_BLE_MESH_MODEL_OP_APP_KEY_ADD: + ESP_LOGI(TAG, "Config Server get Config AppKey Add"); + err = example_handle_config_app_key_add_evt(param->status_cb.app_key_add.app_idx); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Failed to bind app_idx 0x%04x with non-config models", + __func__, param->status_cb.app_key_add.app_idx); + return; + } + break; + default: + break; + } + break; + default: + return; + } +} + +static esp_err_t ble_mesh_init(void) +{ + esp_err_t err; + + /* First two bytes of device uuid is compared with match value by Provisioner */ + memcpy(dev_uuid + 2, esp_bt_dev_get_address(), 6); + + esp_ble_mesh_register_prov_callback(example_ble_mesh_provisioning_cb); + esp_ble_mesh_register_custom_model_callback(example_ble_mesh_custom_model_cb); + esp_ble_mesh_register_config_client_callback(example_ble_mesh_config_client_cb); + esp_ble_mesh_register_config_server_callback(example_ble_mesh_config_server_cb); + + err = esp_ble_mesh_init(&prov, &comp); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Failed to initialize BLE Mesh", __func__); + return err; + } + + err = example_fast_prov_server_init(&vnd_models[0]); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Failed to initialize fast prov server model", __func__); + return err; + } + + err = esp_ble_mesh_client_model_init(&vnd_models[1]); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Failed to initialize fast prov client model", __func__); + return err; + } + + k_delayed_work_init(&send_self_prov_node_addr_timer, example_send_self_prov_node_addr); + + err = esp_ble_mesh_node_prov_enable(ESP_BLE_MESH_PROV_ADV | ESP_BLE_MESH_PROV_GATT); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Failed to enable node provisioning", __func__); + return err; + } + + ESP_LOGI(TAG, "BLE Mesh Fast Prov Node initialized"); + + board_led_operation(LED_B, LED_ON); + + return ESP_OK; +} + +static esp_err_t bluetooth_init(void) +{ + esp_err_t ret; + + ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK(ret); + + ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT)); + + esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + ret = esp_bt_controller_init(&bt_cfg); + if (ret) { + ESP_LOGE(TAG, "%s initialize controller failed", __func__); + return ret; + } + + ret = esp_bt_controller_enable(ESP_BT_MODE_BLE); + if (ret) { + ESP_LOGE(TAG, "%s enable controller failed", __func__); + return ret; + } + + ret = esp_bluedroid_init(); + if (ret) { + ESP_LOGE(TAG, "%s init bluetooth failed", __func__); + return ret; + } + + ret = esp_bluedroid_enable(); + if (ret) { + ESP_LOGE(TAG, "%s enable bluetooth failed", __func__); + return ret; + } + + return ret; +} + +void app_main(void) +{ + esp_err_t err; + + ESP_LOGI(TAG, "Initializing..."); + + err = board_init(); + if (err) { + ESP_LOGE(TAG, "board_init failed (err %d)", err); + return; + } + + err = bluetooth_init(); + if (err) { + ESP_LOGE(TAG, "esp32_bluetooth_init failed (err %d)", err); + return; + } + + /* Initialize the Bluetooth Mesh Subsystem */ + err = ble_mesh_init(); + if (err) { + ESP_LOGE(TAG, "Bluetooth mesh init failed (err %d)", err); + return; + } +} diff --git a/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/main/board.c b/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/main/board.c new file mode 100644 index 0000000000..4c51d04af7 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/main/board.c @@ -0,0 +1,72 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 "driver/gpio.h" +#include "board.h" +#include "esp_fast_prov_common.h" + +#define TAG "BOARD" + +struct _led_state led_state[3] = { + { LED_OFF, LED_OFF, LED_R, "red" }, + { LED_OFF, LED_OFF, LED_G, "green" }, + { LED_OFF, LED_OFF, LED_B, "blue" }, +}; + +void board_output_number(esp_ble_mesh_output_action_t action, uint32_t number) +{ + ESP_LOGI(TAG, "Board output number %d", number); +} + +void board_prov_complete(void) +{ + board_led_operation(LED_B, LED_OFF); +} + +void board_led_operation(uint8_t pin, uint8_t onoff) +{ + for (int i = 0; i < 3; i++) { + if (led_state[i].pin != pin) { + continue; + } + if (onoff == led_state[i].previous) { + ESP_LOGW(TAG, "led %s is already %s", + led_state[i].name, (onoff ? "on" : "off")); + return; + } + gpio_set_level(pin, onoff); + led_state[i].previous = onoff; + return; + } + + ESP_LOGE(TAG, "LED is not found!"); +} + +static void board_led_init(void) +{ + for (int i = 0; i < 3; i++) { + gpio_pad_select_gpio(led_state[i].pin); + gpio_set_direction(led_state[i].pin, GPIO_MODE_OUTPUT); + gpio_set_level(led_state[i].pin, LED_OFF); + led_state[i].previous = LED_OFF; + } +} + +esp_err_t board_init(void) +{ + board_led_init(); + return ESP_OK; +} diff --git a/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/main/board.h b/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/main/board.h new file mode 100644 index 0000000000..b00fc1e04b --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/main/board.h @@ -0,0 +1,47 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#ifndef _BOARD_H_ +#define _BOARD_H_ + +#include "sdkconfig.h" +#include "driver/gpio.h" +#include "esp_ble_mesh_defs.h" + +#ifdef CONFIG_BLE_MESH_ESP_WROOM_32 +#define LED_R GPIO_NUM_25 +#define LED_G GPIO_NUM_26 +#define LED_B GPIO_NUM_27 +#elif defined(CONFIG_BLE_MESH_ESP_WROVER) +#define LED_R GPIO_NUM_0 +#define LED_G GPIO_NUM_2 +#define LED_B GPIO_NUM_4 +#endif + +struct _led_state { + uint8_t current; + uint8_t previous; + uint8_t pin; + char *name; +}; + +void board_output_number(esp_ble_mesh_output_action_t action, uint32_t number); + +void board_prov_complete(void); + +void board_led_operation(uint8_t pin, uint8_t onoff); + +esp_err_t board_init(void); + +#endif diff --git a/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/main/component.mk b/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/main/component.mk new file mode 100644 index 0000000000..a98f634eae --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/main/component.mk @@ -0,0 +1,4 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/sdkconfig.defaults b/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/sdkconfig.defaults new file mode 100644 index 0000000000..4f6ca5787a --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/sdkconfig.defaults @@ -0,0 +1,51 @@ +# Override some defaults so BT stack is enabled +# by default in this example +CONFIG_BT_ENABLED=y +CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY=y +CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY= +CONFIG_BTDM_CONTROLLER_MODE_BTDM= +CONFIG_BTDM_CONTROLLER_MODEM_SLEEP=n +CONFIG_BLE_SCAN_DUPLICATE=y +CONFIG_SCAN_DUPLICATE_TYPE=2 +CONFIG_DUPLICATE_SCAN_CACHE_SIZE=200 +CONFIG_BLE_MESH_SCAN_DUPLICATE_EN=y +CONFIG_MESH_DUPLICATE_SCAN_CACHE_SIZE=200 +CONFIG_BTDM_CONTROLLER_FULL_SCAN_SUPPORTED=y +CONFIG_GATTS_ENABLE=y +CONFIG_GATTS_SEND_SERVICE_CHANGE_MANUAL=y +CONFIG_BLE_MESH=y +CONFIG_BLE_MESH_HCI_5_0=y +CONFIG_BLE_MESH_USE_DUPLICATE_SCAN=y +CONFIG_BLE_MESH_FAST_PROV=y +CONFIG_BLE_MESH_PROV=y +CONFIG_BLE_MESH_NODE=y +CONFIG_BLE_MESH_PROVISIONER=y +CONFIG_BLE_MESH_WAIT_FOR_PROV_MAX_DEV_NUM=20 +CONFIG_BLE_MESH_MAX_STORED_NODES=10 +CONFIG_BLE_MESH_MAX_PROV_NODES=6 +CONFIG_BLE_MESH_PBA_SAME_TIME=3 +CONFIG_BLE_MESH_PBG_SAME_TIME=3 +CONFIG_BLE_MESH_PROVISIONER_SUBNET_COUNT=3 +CONFIG_BLE_MESH_PROVISIONER_APP_KEY_COUNT=9 +CONFIG_BLE_MESH_PB_ADV=y +CONFIG_BLE_MESH_NET_BUF_POOL_USAGE=y +CONFIG_BLE_MESH_SUBNET_COUNT=2 +CONFIG_BLE_MESH_APP_KEY_COUNT=3 +CONFIG_BLE_MESH_MODEL_KEY_COUNT=3 +CONFIG_BLE_MESH_MODEL_GROUP_COUNT=3 +CONFIG_BLE_MESH_PB_GATT=y +CONFIG_BLE_MESH_GATT_PROXY=y +CONFIG_BLE_MESH_RELAY=y +CONFIG_BLE_MESH_LOW_POWER= +CONFIG_BLE_MESH_FRIEND= +CONFIG_BTU_TASK_STACK_SIZE=4512 +CONFIG_BLE_MESH_CFG_CLI=y +CONFIG_BLE_MESH_CRPL=60 +CONFIG_BLE_MESH_MSG_CACHE_SIZE=60 +CONFIG_BLE_MESH_ADV_BUF_COUNT=200 +CONFIG_BLE_MESH_TX_SEG_MSG_COUNT=10 +CONFIG_BLE_MESH_RX_SEG_MSG_COUNT=10 +CONFIG_BLE_MESH_RX_SDU_MAX=384 +CONFIG_BLE_MESH_TX_SEG_MAX=32 +CONFIG_BLE_MESH_NO_LOG=n +CONFIG_BLE_MESH_STACK_TRACE_LEVEL=0 \ No newline at end of file diff --git a/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/tutorial/EspBleMesh.md b/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/tutorial/EspBleMesh.md new file mode 100644 index 0000000000..274ecf581d --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/tutorial/EspBleMesh.md @@ -0,0 +1,93 @@ +# Demo Function + +This demo demonstrates the fast provisioning of ESP BLE Mesh network and how to use the EspBleMesh app to control an individual provisioned node or all the provisioned nodes. + +A video of this demo can be seen +[here](http://download.espressif.com/BLE_MESH/BLE_Mesh_Demo/V0.4_Demo_Fast_Provision/ESP32_BLE_Mesh_Fast_Provision.mp4) + +# What You Need + +* [EspBleMesh App for Android](http://download.espressif.com/BLE_MESH/BLE_Mesh_Tools/BLE_Mesh_App/EspBleMesh-0.9.4.apk) +* [ESP BLE Mesh SDK v0.6(Beta Version)](https://glab.espressif.cn/ble_mesh/esp-ble-mesh-v0.6) +* ESP32 Development Boards + +> Note: +> +> 1. Please flash the [`ble_mesh_fast_prov_server`](https://glab.espressif.cn/ble_mesh/esp-ble-mesh-v0.6/tree/ble_mesh_release/esp-ble-mesh-v0.6/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server) to your boards first; +> 2. To have a better understanding of the performance of the BLE Mesh network, we recommend that at least 3 devices should be added in your network. +> 3. We recommend that you solder LED indicators if your development board does not come with lights. +> 4. Please check the type of board and LED pin definition enabled in `Example BLE Mesh Config` by running `make menuconfig` + +![Board](images/device.png) + + +# Flash and Monitor + +1. Enter the directory: +examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server +2. Make sure that the `IDF_PATH` environment variable was set in accordance with your current IDF path +3. Check the version of your toolchain. Version 4.1 or newer should be used. + +![Checkenvironment](images/picture1.png) + +4. Run `make -j4 flash` to compile codes and flash the codes to the device. + +![compiledcode](images/picture2.png) + +> Note: +> +> Please click on the Exit button if you see the following windows. + + +5. Please establish a connection between your device and PC, using the correct serial number, if you want to monitor the operation of this device on PC. + +# How to Use the App + +Please launch the `EspBleMesh` app, and follow the steps described below to establish a BLE Mesh network and control any individual node or all the nodes. + +![App steps](images/app_ble.png) +1. Click on the upper left corner to see more options; +2. Click on **Provisioning** to scan nearby unprovisioned devices; +3. Choose any unprovisioned devices in the scanned list; +4. Enter the number of devices you want to add in your mesh network; +> Note: +> +> If you only want to use the normal provisioning feature,You don't check the option for fast provisioning. +5. Wait until all the devices are provisioned; +6. Click on the upper left corner to see more options; +7. Click on **Fast Provisioned** to see all the provisioned devices; +8. Control your devices. + +> Note: +> +> Please disable your Bluetooth function on your phone, enable it and try again, if you have encountered any connection issues. + + +# Procedure + +## Role + +* Phone - Top Provisioner +* The device that has been provisioned by Phone - Primary Provisioner +* Devices that have been provisioned and changed to the role of a provisioner - Temporary Provisioner +* Devices that have been provisioned but not changed to the role of a provisioner - Node + +## Interaction + +![Interaction](images/time.png) +1. The Top Provisioner configures the first device to access the network with the GATT bearer. +2. The Top Provisioner sends the `send_config_appkey_add` message to allocate the Appkey to this device. +3. The Top Provisioner sends the `send_fast_prov_info_set` message to provide the necessary information so the device can be changed to a Primary Provisioner. +4. The device calls the `esp_ble_mesh_set_fast_prov_action` API to change itself into the role of a Primary Provisioner and disconnects with the Top Provisioner. +5. The Primary Provisioner sends the `send_config_appkey_add` message to allocate the Appkey to an other device. +6. The Primary Provisioner sends the `send_fast_prov_info_set` message to provide the necessary information so the device can be changed to a Temporary Provisioner. +7. The device calls the `esp_ble_mesh_set_fast_prov_action` API to change itself into the role of a Temporary Provisioner and starts its address timer. +8. The Temporary Provisioner collects the addresses of nodes that it has provisioned and sends these addresses to the Primary Provisioner, when its address timer times out, which indicates the Temporary Provisioner hasn't provisioned any devices for 10s. +9. The Primary Provisioner reconnects to the Top Provisioner when its address timer times out, which indicates the Primary Provisioner hasn't received any messages from the Temporary Provisioners for 10s. +10. The Top Provisioner sends the `node_adress_Get` message automatically after reconnecting with the Primary Provisioner. +11. At this point, the Top Provisioner is able to control any nodes in the BLE Mesh Network. + +> Note: +> +> The nodes in the BLE Mesh Network only disable its provisioner functionality after it has been controlled by the Top Provisioner for at least one time. + diff --git a/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/tutorial/ble_mesh_fast_provision_server.md b/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/tutorial/ble_mesh_fast_provision_server.md new file mode 100644 index 0000000000..d1e597cda6 --- /dev/null +++ b/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/tutorial/ble_mesh_fast_provision_server.md @@ -0,0 +1,409 @@ +# 1. Introduction +## 1.1 Demo Function + +This demo is used for fast provisioning networks. It takes no more than 60 seconds to provisioning 100 devices in this demo. + +This demo must be used with the EspBleMesh app. For details about how to use the EspBleMesh app, please click [here](EspBleMesh.md). + +## 1.2 Node Composition + +This demo has only one element, where the following five Models are implemented: + +- The **Configuration Server** Model is used to represent a mesh network configuration of a device. +- The **Configuration Client** Model is used to represent an element that can control and monitor the configuration of a node. +- The **Generic OnOff Server** Model implements the node's Onoff state. +- The **Vendor Server** Model implements the node's `fast_prov_server` state. +- The **Vendor Client** Model is used to control the `fast_prov_server` state, which defines the fast provisioning behavior of a node. + + +## 2. Code Analysis + +Code initialization part reference [Initializing the Bluetooth and Initializing the BLE Mesh](../../../ble_mesh_wifi_coexist/tutorial%20%20%20%20%20%20/ble_mesh_wifi_coexist.md) + +### 2.1 Data Structure + +This section introduces the `example_fast_prov_server_t` strut for this demo, and its variables in groups. + +``` +typedef struct { + esp_ble_mesh_model_t *model; /* Fast Prov Server Model pointer */ + ATOMIC_DEFINE(srv_flags, SRV_MAX_FLAGS); + + bool primary_role; /* Indicate if the device is a Primary Provisioner */ + uint8_t max_node_num; /* The maximum number of devices can be provisioned by the Provisioner */ + uint8_t prov_node_cnt; /* Number of self-provisioned nodes */ + uint16_t app_idx; /* AppKey index of the application key added by other Provisioner */ + uint16_t top_address; /* Address of the device(e.g. phone) which triggers fast provisioning */ + + esp_ble_mesh_msg_ctx_t ctx; /* the context stored for sending fast prov status message */ + struct fast_prov_info_set *set_info; /* Used to store received fast prov info set context */ + + uint16_t node_addr_cnt; /* Number of node address shall be received */ + uint16_t unicast_min; /* Minimum unicast address can be send to other nodes */ + uint16_t unicast_max; /* Maximum unicast address can be send to other nodes */ + uint16_t unicast_cur; /* Current unicast address can be assigned */ + uint16_t unicast_step; /* Unicast address change step */ + uint8_t flags; /* Flags state */ + uint32_t iv_index; /* Iv_index state */ + uint16_t net_idx; /* Netkey index state */ + uint16_t group_addr; /* Subscribed group address */ + uint16_t prim_prov_addr; /* Unicast address of Primary Provisioner */ + uint8_t match_val[16]; /* Match value to be compared with unprovisioned device UUID */ + uint8_t match_len; /* Length of match value to be compared */ + + uint8_t pend_act; /* Pending action to be performed */ + uint8_t state; /* Fast prov state -> 0: idle, 1: pend, 2: active */ + + struct k_delayed_work disable_fast_prov_timer; /* Used to disable fast provisioning */ + struct k_delayed_work gatt_proxy_enable_timer; /* Used to Mesh GATT Proxy functionality */ +} __attribute__((packed)) example_fast_prov_server_t; +``` + + +#### 2.1.1 Provisioner Role and State + +Different provisioners have different behaviors and it’s helpful to understand the concepts of different roles so you can better understand the codes. + +In the struct, there are three variables that are related to roles and states, which are described in the following table: + +| Variable Name |Description | +| ---------------------|------------------------- | +| `primary_role` | Provisioner identity | +| `state` | Fast provisioner state (0: idle, 1: pend, 2: active) | +| `srv_flags` | Flags (`DISABLE_FAST_PROV_START`,`GATT_PROXY_ENABLE_START`,`RELAY_PROXY_DISABLED`,`SRV_MAX_FLAGS`) | + +Among which, there are four roles in this demo (`primary_role`): + +* Phone - Top Provisioner +* The device that has been provisioned by Phone - Primary Provisioner +* Devices that have been provisioned and changed to the role of a provisioner - Temporary Provisioner +* Devices that have been provisioned but not changed to the role of a provisioner - Node + + +#### 2.1.2 Provisioner Address Management + +The provisioner address management is used to assign a unicast address to each node, so as to prevent address conflicts by allocating address in an equally manner. Each provisioner has its own address range and a maximum number of the nodes it can provisioned. The provisioner will allocate a subset of its address range to the nodes it has provisioned. + +Example: A top provisioner's address range is 0 to 100 and the maximum number of nodes it can provisioned is 5. The provisioner address management will assign subsets of address range to these 5 nodes, which are 1 to 20, 21 to 40, 41 to 60, 61 to 80 and 81 to 100. + +The variables that are related to the address management are described in the following table: + +| Variable Name |Description | +| ----------------------|------------------------- | +| `unicast_min` | Minimum unicast address can be allocated to other nodes | +| `unicast_max` | Maximum unicast address can be allocated to other nodes | +| `unicast_cur` | Current unicast address | +| `unicast_step` | Unicast address change step Offset| + +#### 2.1.3 Provisioner Cache Data + +The cache data is required, so a node can change its role to become a provisioner. During this process, the `esp_ble_mesh_set_fast_prov_info` and `esp_ble_mesh_set_fast_prov_action` APIs are called. + +The node's cache data, which are described in the following table, is sent by the provisioner. + +| Variable Name |Description | +| ----------------------|------------------------- | +| `flags` |Flags state| +| `iv_index` |Iv_index state| +| `net_idx` |Netkey index state | +| `group_addr` |Subscribed group address | +| `prim_prov_addr` |Unicast address of Primary Provisioner | +| `match_val[16]` |Match value to be compared with unprovisioned device UUID | +| `match_len` | Length of match value to be compared | +| `max_node_num` | The maximum number of devices can be provisioned by the Provisioner | +| `prov_node_cnt` | Number of self-provisioned nodes | +| `app_idx` | AppKey index of the application key added by other Provisioner | +| `top_address` | Address of the device(e.g. phone) which triggers fast provisioning | + + +#### 2.1.4 Provisioner Timer + +There are two timers in this demo, which are: + +1. `gatt_proxy_enable_timer` is used to enable Mesh GATT Proxy functionality. + * The timer starts or resets and starts when a Temporary Provisioner provisions an unprovisioned device. + * The Temporary Provisioner will send a message (Address information) to the Primary Provisioner. +2. `disable_fast_prov_timer` is used to disable the provisioning capabilities. + * The node starts the timer when it receives a **Generic OnOff Get/Set/Set Unack** message sent by the EspBleMesh app. The group address should be used if you want to disable the provisioning capabilities of all nodes. + +The variables that are related to these two timers are described below: + +| Variable Name |Description | +| ----------------------|------------------------- | +| `disable_fast_prov_timer` |Used to disable fast provisioning| +| `gatt_proxy_enable_timer` |Used to enable Mesh GATT Proxy functionality| + +### 2.2 Model Definition + +#### 2.2.1 Vendor Server Model + +The **Vendor Server** Model implements the node's `fast_prov_server` state, which has been covered in the previous section. + +```c +example_fast_prov_server_t fast_prov_server = { + .primary_role = false, + .max_node_num = 6, + .prov_node_cnt = 0x0, + .unicast_min = ESP_BLE_MESH_ADDR_UNASSIGNED, + .unicast_max = ESP_BLE_MESH_ADDR_UNASSIGNED, + .unicast_cur = ESP_BLE_MESH_ADDR_UNASSIGNED, + .unicast_step = 0x0, + .flags = 0x0, + .iv_index = 0x0, + .net_idx = ESP_BLE_MESH_KEY_UNUSED, + .app_idx = ESP_BLE_MESH_KEY_UNUSED, + .group_addr = ESP_BLE_MESH_ADDR_UNASSIGNED, + .prim_prov_addr = ESP_BLE_MESH_ADDR_UNASSIGNED, + .match_len = 0x0, + .pend_act = FAST_PROV_ACT_NONE, + .state = STATE_IDLE, +}; +``` + +The `fast_prov_srv_op` is used to register the minimum length of messages. For example, the minimum length of the `ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_SET` message is registered as 3 octets. + +```c +static esp_ble_mesh_model_op_t fast_prov_srv_op[] = { + { ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_SET, 3, NULL }, + { ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NET_KEY_ADD, 16, NULL }, + { ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR, 2, NULL }, + { ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_GET, 0, NULL }, + ESP_BLE_MESH_MODEL_OP_END, +}; + +``` +The `example_fast_prov_server_init` function is used to register the callback function triggered when the timers timeout, and initializes the Model-related variables in the data struct. + +```c +err = example_fast_prov_server_init(&vnd_models[0]); +if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Failed to initialize fast prov server model", __func__); + return err; +} +``` + +The `fast_prov_server` struct represents the Vendor server's states. The `CID_ESP` and `ESP_BLE_MESH_VND_MODEL_ID_FAST_PROV_SRV` constants, which consist of the vendor server Model's Model id `ESP_BLE_MESH_VND_MODEL_ID_FAST_PROV_SRV`, are used to identity the Vendor server Model. + + +```c +static esp_ble_mesh_model_t vnd_models[] = { + ESP_BLE_MESH_VENDOR_MODEL(CID_ESP, ESP_BLE_MESH_VND_MODEL_ID_FAST_PROV_SRV, + fast_prov_srv_op, NULL, &fast_prov_server), +}; +static esp_ble_mesh_elem_t elements[] = { + ESP_BLE_MESH_ELEMENT(0, root_models, vnd_models), +}; +``` + + +#### 2.2.2 Vendor Client Model + +The **Vendor Client** Model is used to control the `fast_prov_server` state, which defines the fast provisioning behavior of a node. + +The `fast_prov_cli_op_pair` struct is used to register the corresponding message acknowledgements. + +```c +static const esp_ble_mesh_client_op_pair_t fast_prov_cli_op_pair[] = { + { ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_SET, ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_STATUS }, + { ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NET_KEY_ADD, ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NET_KEY_STATUS }, + { ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR, ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_ACK }, + { ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_GET, ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_STATUS }, +}; +``` + +Example: The **Vendor Client** Model sends message with an opcode of `ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_SET`, which requires the **Vendor Server** Model to respond with a message with an opcode of `ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_STATUS`. After that, the **Vendor Client** Model times out if it receives no corresponding acknowledgement. + +```c +static const esp_ble_mesh_client_op_pair_t fast_prov_cli_op_pair[] = { + { ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_SET, ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_STATUS }, +}; +``` +Note that you can also use the code below if you don't want the **Vendor Client** Model to wait for acknowledgement from the server Model, which means the client Model will never time out. + +```c +static const esp_ble_mesh_client_op_pair_t fast_prov_cli_op_pair[] = { + { ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_SET, NULL }, +}; +``` + +The `esp_ble_mesh_client_model_init` API is used to register the callback function triggered when the timers timeout, and initializes the Model-related variables in the data struct. + +```c +err = esp_ble_mesh_client_model_init(&vnd_models[1]); +if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: Failed to initialize fast prov client Model", __func__); + return err; +} +``` + +The `CID_ESP` and `ESP_BLE_MESH_VND_MODEL_ID_FAST_PROV_CLI` constants, which consist of the vendor client Model's Model id `ESP_BLE_MESH_VND_MODEL_ID_FAST_PROV_CLI`, are used to identity the Vendor client Model. + +```c + +esp_ble_mesh_client_t fast_prov_client = { + .op_pair_size = ARRAY_SIZE(fast_prov_cli_op_pair), + .op_pair = fast_prov_cli_op_pair, +}; + +static esp_ble_mesh_model_op_t fast_prov_cli_op[] = { + { ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_STATUS, 1, NULL }, + { ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NET_KEY_STATUS, 2, NULL }, + { ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_ACK, 0, NULL }, + ESP_BLE_MESH_MODEL_OP_END, +}; + +static esp_ble_mesh_model_t vnd_models[] = { + ESP_BLE_MESH_VENDOR_MODEL(CID_ESP, ESP_BLE_MESH_VND_MODEL_ID_FAST_PROV_CLI, + fast_prov_cli_op, NULL, &fast_prov_client), +}; +static esp_ble_mesh_elem_t elements[] = { + ESP_BLE_MESH_ELEMENT(0, root_models, vnd_models), +}; + +``` + +## 2.3 Message Opcode + +"Opcode-send" represents the message that the client sends to the server. + +"Opcode-ack" represents the message that the server sends to the client. + +* INFO_SET + +|Meaning | Opcode-send | Opcode-ack | +| -----| ------------- | -------------| +|Opcode| `ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_SET` | `ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_STATUS` | +|Function| This message contains all the information as a Provisioner |Checks each field of the Provisioner information and set the corresponding flag bit. The returned status is variable.| +|Parameter|structfast_prov_info_set|status_bit_mask, status_ctx_flag, status_unicast, status_net_idx, status_group, status_pri_prov, status_match, status_action| + + +* NODE_ADDR + +|Meaning | Opcode-send | Opcode-ack | +| -----| ------------- | -------------| +|Opcode| `ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR` | `ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_ACK` | +|Function| Temporary Provisioner reports the address of the node it has provisioned. |Used to check if the message was sent successfully. | +|Parameter| Address array |NA | + +* ADDR_GET + +|Meaning | Opcode-send | Opcode-ack | +| -----| ------------- | -------------| +|Opcode| `ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_GET` | `ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_STATUS` | +|Function|Top Provisioner gets the address of all nodes obtained from Primary Provisioner. | Returns the address of all nodes, but does not contain its own. | +|Parameter|NA |Address array | + +* NET_KEY_ADD + +|Meaning | Opcode-send | Opcode-ack | +| -----| ------------- | -------------| +|Opcode | `ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NET_KEY_ADD` | `ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NET_KEY_STATUS` | +|Function| Reserved for later use | Reserved for later use | +|Parameter| NA | NA | + + +### 2.4 Callback Function +#### 2.4.1 The Callback function for the Vendor Server Model + +```c + esp_ble_mesh_register_custom_model_callback(example_ble_mesh_custom_model_cb); + esp_ble_mesh_register_prov_callback(example_ble_mesh_provisioning_cb); +``` + +1. The callback function will be triggered when the **Vendor Server** Model: + * Receives a message that indicates the Onoff state of the client Model; or + * Calls any APIs that send messages. + +2. The events that this callback function handle: + +* Generic Onoff Server Model + +| Event Name | Opcode |Description | +| ------------- | ------------|------------------------------------------- | +| ESP_BLE_MESH_MODEL_OPERATION_EVT|ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET | This event is triggered when the **Generic Onoff Server** model receives the `ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET` message | +| ESP_BLE_MESH_MODEL_OPERATION_EVT|ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK| This event is triggered when the **Generic Onoff Server** model receives the `ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK` message. | + +* Vendor Server Model + +| Event Name | Opcode |Description | +| ------------- | ------------|------------------------------------------- | +| ESP_BLE_MESH_MODEL_OPERATION_EVT | ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_SET | This event is triggered when the **Vendor Server** model receives the `ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_SET` message.| +| ESP_BLE_MESH_MODEL_OPERATION_EVT | ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR | This event is triggered when the **Vendor Server** model receives the `ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR` message.| +| ESP_BLE_MESH_MODEL_OPERATION_EVT | ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_GET | This event is triggered when the **Vendor Server** model receives the `ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_GET` message.| + +* The **Configuration Client** Model + +| Event Name | Opcode |Description | +| ------------- | ------------|------------------------------------------- | +|ESP_BLE_MESH_SET_FAST_PROV_INFO_COMP_EVT| NA| This event is triggered when the `esp_ble_mesh_set_fast_prov_info` API is called. | +|ESP_BLE_MESH_SET_FAST_PROV_ACTION_COMP_EVT| NA| This event is triggered when the `esp_ble_mesh_set_fast_prov_action` API is called. | +|ESP_BLE_MESH_CFG_CLIENT_SET_STATE_EVT|ESP_BLE_MESH_MODEL_OP_APP_KEY_ADD|This event is triggered when the **Configuration Server** model receives and further triggers an API calling to send `ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_SET` message. | +|ESP_BLE_MESH_CFG_CLIENT_TIMEOUT_EVT|ESP_BLE_MESH_MODEL_OP_APP_KEY_ADD|This event is triggered when the API `example_send_config_appkey_add` times out.| + +#### 2.4.2 The Vendor Client Model + +```c + esp_ble_mesh_register_custom_model_callback(example_ble_mesh_custom_model_cb); +``` + +1. The callback function will be triggered when the **Vendor Client** model: + * Receives any message sent by the vendor server Model; or + * Calls any APIs that send messages. + +2. The events that this callback function handle: + +| Event Name | Opcode |Description | +| ------------- | ------------|------------------------------------------- | +| ESP_BLE_MESH_MODEL_OPERATION_EVT | ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_STATUS | This event is triggered when the **Vendor Client** model receives the `ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_STATUS` message.| +| ESP_BLE_MESH_MODEL_OPERATION_EVT | ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NET_KEY_STATUS | This event is triggered when the **Vendor Client** model receives the `ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NET_KEY_STATUS` message.| +| ESP_BLE_MESH_MODEL_OPERATION_EVT | ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_ACK | This event is triggered when the **Vendor Client** model receives the `ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_ACK` message | +| ESP_BLE_MESH_CLIENT_MODEL_SEND_TIMEOUT_EVT | client_send_timeout.opcode | This event is triggered when the API `esp_ble_mesh_client_model_send_msg` times out.| + +### 2.5 Message Sending +#### 2.5.1 The Vendor Client sends messages + +The Vendor Client Model calls the `esp_ble_mesh_client_model_send_msg` API to send messages to the Vendor Server Model. + +| Parameter Name |Description | +| ----------------------|------------------------- | +| `model` | The pointer to the client Model struct | +| `ctx.net_idx` | The NetKey Index of the subnet through which the message is sent | +| `ctx.app_idx` | The AppKey Index for the message encryption | +| `ctx.addr` | The address of the destination nodes | +| `ctx.send_ttl`| The TTL State, which determines how many times a message can be relayed| +| `ctx.send_rel`| This parameter determines whether the Model will wait for an acknowledgment after sending a message | +| `opcode` | The message opcode | +| `msg->len` | The length of the `msg->data`| +| `msg->data` | The pointer to sent data| +| `msg_timeout` | The maximum duration (4000 ms by default) that the Model waits for an acknowledgment. | +|`true` | True: an acknowledgement is required; False: no acknowledgement is required | +| `msg_role` | The role of a message (node/provisioner) | + +```c +esp_ble_mesh_msg_ctx_t ctx = { + .net_idx = info->net_idx, + .app_idx = info->app_idx, + .addr = info->dst, + .send_rel = false, + .send_ttl = 0, + }; + err = esp_ble_mesh_client_model_send_msg(model, &ctx, + ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_SET, + msg->len, msg->data, info->timeout, true, info->role); +``` + +#### 2.5.2 The Vendor Server sends messages + +The **Vendor Server** Model has to bind its Appkey before calling the `esp_ble_mesh_server_model_send_msg` API to send a message. + +```c +esp_ble_mesh_server_model_send_msg(model, ctx, ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_STATUS, + msg->len ,msg->data ); +``` +The **Vendor Server** Model calls the `esp_ble_mesh_model_publish` API to publish messages. Only the Models that have subscribed to this destination address receive the published messages. + +```c +esp_err_t esp_ble_mesh_model_publish(esp_ble_mesh_model_t *model, uint32_t opcode, + uint16_t length, uint8_t *data, + esp_ble_mesh_dev_role_t device_role); +``` diff --git a/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/tutorial/images/app_ble.png b/examples/bluetooth/ble_mesh/ble_mesh_fast_provision/ble_mesh_fast_prov_server/tutorial/images/app_ble.png new file mode 100644 index 0000000000000000000000000000000000000000..e62b7577899d5a1f3e205a3bce80af5e3f55617f GIT binary patch literal 182199 zcmeAS@N?(olHy`uVBq!ia0y~yV9sPNSs54@I14-?iy0XBj({-ZRBb+K28RDjJzX3_D&pSEWt}79e)-sI zmhy6rcVds;RVE*~dw)lLVfmwX-;UkAJ6A)XBk^Fs`nzL!rq1U=*EML@B zCr@VjTJL$&-Z19$gZC%={oLHX?X8(EcTQyQH4D|qsF3wqnpOFe=GmMo_WZ?OACwZN zc{5e@D2M#t(C)hFx4(J>E>x*j`*wY!LTc_SW7d_*=9S%_s|u%uM%?M%{LbdI*Qv^@ zUzSe2WxssA@8qSoL{&8-m!#=VTy?cOH>JX{uR-|%hlohynY-05{yb4xyP)D{ZC`_O zfDng;r#5HE=@n-;L>^HeZ zJ)@LeHu&&M`&6ziQb>sG+qg%fPO~}vyaHCM?*s;$p)6|q%|Ry3WIHf>7+S8x(r zI=7hFnhePxS9|`PFn>A83!$%6-Pg*vik_HX%<{>dAzL@SDyXZ*K7V7(-{&kpb5D9r zwEAj3cL{51uI)vSt99|t*8hsKeEcrHKNXaDKjWzL@thu;6X&)j9O~1U_w(?s&hjG@ zbuDLKc~&EOv5EJ&MSt!hg~JiC2XlFpw*sWWT_V)v5-&v9!8vmCPS zE!oqw=kbX-0#=vU{-66NS?V1%b6?o{JAwr)4hKA=j=p5yb}5HTR)@zZZS~2Ub97@k zu5T8d_hpO2l~tO7*1KG5D{DTVJJ>btvF6OR=IfT{sxIveofH00SCfV5rdLUtR~Jw0 zmY8)vlK1e$?TA^D$2{?sDR*GNoao{&<|iVTy}7zz*_*3t_Su*xFXV~dXU6Dp>(@q> zz@s}i@7pCE?(aKo>PyK@C9!R5+*psk5Z;FF(M(CBTHm%8Yo^vd@w+r@sha2KRhmYr zrL%O7^=|W7JL%}(zCC+*cR73zStx82_UXW}I{w498@c&k&oB0#^a;U6k znzwG++WTU9leO#4t7U%eYF?81^}UwW`L~xhwB5N?Y#g~o&v%`qz4yNOU3!dDzOC69 z?kajL_v+OjZ@!&P`Mk??>8_|v;ToR5Lp@(Vp8Wj8htQXk+MepW|JGqt+H%nQWZcDz zl}fIhkLG>NKJoT$w5RCW`#)|(#om~?DXF){Abn$vLH^7$?JSDPjyjU9a!fNfl{ddC z`kL08y3(uQ--?$;o}JTKUdQoxo{n3yYHfV%y@e}fZthuk$2_xdk41Xxt}9}@lIj&E z3SQV4t6V&nCxm~(vt1h|F8XU~y#JrLm|^F7ze$#vGe4c1qq<3LrB_*e(WGf7*56Jo zW;w;nboJUgUOlIee066o2L*Fqn^wBR`r4Fxz0$jOCm$5>zO%bXHB+mop!CeO^eH{5 z6OGq7U;A=@l>pnMYn%4jSS^?xb?Hw5ix(&3+ip+Kmy;$=(K;-qH&we{Ufo_Nz*|aX zZJQdu<`u!2!kcwZY2Lc{edSao{x5G8yr(RPwN9H=v#(<7xrw2<4k_0IE*(+drWom> z5qbQ6+1Ax3H~%%C+~%e8FK%1ZvUM@avRUERB`!)C72VX@S@TCma?!N?2MoEMESj0@ zcYnjBrX4=dcJ!y}K3ZYu#g`coJn#KwziF9k9#l-toqI3XUAJ!Ss-<3`s$TuRGqu0_ zN9%lU+PZ2&`&IF5l`}ufr(IyqQaQa;cGk&1TS|78K8l&SGwfo+VpbcoGU2tWGUnde zHc{rzo_RKAiPLKKRIH8q`)K#JryhG6w;J`AbJx_0*o zF{`#oKj#{E=j%BuoSv+*`ZUk=jioO>)TcU5I>-9#-0a?|(U-KcmVYd>WKwN$XioKB z`pc^H;>E{Tf3tfk|1ID1xc6<@#Q$&i<^RYEd~3C7SJu`$szsB+obp%mboHucTE6SQ zDQ5ipV$%{{R-XHFFWv6R;W^d_K z(G7mAb2;TZo9UB>c5hP;&MGZByF>ER)kUU}@0zpLp0Zl>MnPV6pGADo1G!xmi{2e; zwlbT%c}l<@v+Rj6CRMw1eAlY!u2MN|`d0d6qOR1*x&O~i{C_q^$9oI!)u}S30Uuw# z`utjCzw4>c6CE?>P1?G}Y4&T?bg$Z6d6~4%ti?Nit#z#T^%e9zu4H=o`bn+1%&T76 zehr$GGWXtQ2l1@1jF^OutIpdD+?Qy3dQWS<%JtLd{@Ev|e|kUdxvzfm(34e(`)+K{ zl-(JYnjc^q>L>AW{hwV)Yj3@5dLw3Zv8A++0SMLYJc)iwk^ZdnAq;v7p zb%$?Xs(jaO?n~McwO;+@U*4^MLRypNZOiObnk5+H?`Y+9X3i4P=NB{zB5(a)oss`C zZuP1wXDf9h1J~`8O)Rv&9o+KIDC?<}=jk(L>U}m(RMsBIKbT*_qGir>R!pC#<;|M& z_7nC^R%bEb+xI3dGGKdLOhVt!huW6G@1Ir_oo7yQX+LMQG15CScV zP3orfOgXB?d+=@G-L+@#eq15OJ7-Dg9Jz@BTAjpS{iDU&LI!t5ZYXglu0Gbwrv)J0^5(^0K__52vzPc2ASNnQ~<@<9{8v1AA)QmT@vAzxob6V(dNlMc**!$M=&(lw4 z*85F#)%+E`Xp?z(+z*HAVxQ)#l|H>=uXfU6Yu?=pi+gqAcl(L0kI}7_x;9mO`T6PM z={nQyN?o0{cx!KLc53cwgSwwum2t_;`vbRM-sLr8-mA-Qw(VRsWd7-8+CJGn5den-&&XN ze=AR?&YX1E__q1PW>dD06FoOSecYNIA{p+Jy)?`6?2^s9rhmHkKXz+aT)3rOIUoDyTHD%7DVDFEw!uL;`bT8$n>;Ky2Q6G+F*u+fpUOsutBY6>5&5L4@Q*OL| zuyp@3@hQ`meNS!KdE97Ep3f_V%+$I6w`yekFa|Znx;VB(8TkACewDoQ?^5-rOXq%@ zFqh3%;@lk$sYQD?J^uAO>h))v-RIvsFUi^_x=!Ti_flYErMTEnZRcm|e+_Z8-%uB7xg;;^+ssAt=lptKwQ`g9 z+LV>NhBv3|;{F{KV(t_C`s#VFEnz7tC+oaC&V@B?=oRXmCbh!MMR>&>gS=@qCgnSY z&iYt7ao*zfdhu!%YtDyBp|b>%=gKTNA9Hc#%#r}NZIySkKGn<4^4`B|Ys!&Jp`Q*v zntExco9WKg)zUX(Cg-WVE`Gau$q7F%8@`Nt9zjz!^o5z9V#{*0S{@sG)9++-hx=Ji ziz!zZEI9P)Y06(cSKX+SQje$4T6X@Wxarr(Nfil|tJZcq)MkZU{;}U`lk?+UM)|qB z)Wkg}#hN6ZE}c8eJ72Z0UO#@Z-rUto`X?QZ-o15NZdUlj)YV~{&VJKnUtO7?`#S9U zgoQkri>DpgE2ZIhX_I)i<<_YhU)RLB?h0QmB(%mPko&gnue$P!)pZxk>Q<|)uDq&M zl-Mq|Y5F8Lqsna;x8{D{`V)ecI1W#Y{sMh_gH8nTk@vW%5 zwh1{)=1W`|K~jzx^uvTFT*C#QDPP$Dg009{eRZDf*x0TuS+|Y;X7lrj7^9m^oFxnjb zxN6gCtq*f{e>}PW>z$>ZwwX(%cl_Vru~yS-nwQ#7*PaJAlZ9;yw?)mnp|fxCsSUGF z?OYu-ch|KQt5`HtcRbb&UgSJ`-RnC}QQ9y6?4KC?WMXFb6Y;G!+X9rDLUWsTWfdMpX5tp-z-yk^7dx;(@z_XpPt_7y`!!5Qu_+mteuml?p%G{Lwf4k>C*}q#NFN*7^_>obcqDb0zWbD-QPNC>;LS_ z5uLB1cBWhj`RQ~2?itb5OSYvI&so9gyWz^)OFR06u1kH|s%9>^w`Qx*jkGM+HLCuZ zM$@$a&D5NKHeOHr%Ip;Rx7;WG-M%#SlBaI1^(*6gy-3Ay4ad65UraN1*0jo8+_mvr zcS@!2ZOIRuhCX@Oo;{Xo+T9=PrLJCb?U@#t93#&8Xs-3^2>Ip7iF>|H{PMwCGF$X) z)yCnjU|4mVWS6R^27{^+i0R`@5v(9y;Xgt}5(1 z<-ng!37xyP=uVIio%lcWd&2Kf-SCL`x~EsN|Jb^mzqtBZ>QS|)(-qfQ#5KHZvtnN# z67+fZDbujYMLL1k4@*gjU)x`ot2(vR+2;yNs7AD=re)mSiQyWZI};DZL@xZEP&<1K zOJ>t9tG>W9$FvM*?ksWbxO~fL{{1MiM)) zs~4&og zWzv@`S1LbsM}3?1`ul_}mR@J{!j*Ogc=DvYS~86{b?Kyq;%T~5FV31;SQ;UI>cY3H z6Q5OWwy59C_)>zPnQ#@)o-Mon7gjvguKA-tT9Z zBHr(=`Vum8%Dgp^f2X8m{@xziBeiml@{|*%VdBSSuUz^zIqljX7vVEHs_#7a2KZe~ zpZ;ohrK6|xr}gJ`B4y?tJgs5Hbkn}goPjZ$ zFPWK@dm={2KGM%KVP44ISt?go&Mm)qb(QB;;Xd8YSK96~Ee$hgWjG1D@q`+cKK)=Q zHF;U5v($>Gi_+|VJa3ycEjn~^oXmoaA=ArqJkzJ&{y$T1zFw5(YSWJ{Cd@JC>Kg8_ zT59t)wcWY7OZBE~^s3onoSM&G@5@-J^*zl?uY5&#uxDg#@7z^-*L`Ow7hhX)vi5G& ziJHH6N^iJCCf?MP+;25)In%zv+vZg@Xl|8Ay@f8NZE zKJQ*$@Zbx%zR&vKT=nnTnTJfjG+q1pRLD2{|Ae&6nu=#ecb6$x`zni-O?@qJ^rL0( z+h&1%%MIhTzedfvw%g(TZ|~0g9$O!+4M^ISym!^7TVk;(ZU6QjF0Vg*E9>f1m8%nu z@7_KoHLG!V&Lp7~qPxB}?(+E-$lv$8YWvhGt8?FeRP8u(m8aT2vuM)VsUdfb6Vo;n zm<2MGEj`xwchR-Iwy&3*ik-D2W#Ob3|NG`?UY>I0?dq_D%~N&Cg2hkos(PQa_ItHz z#rkt`Qkw$qt@&8Ae0!co_^PFExwp>RyD>C;>kKVUX`3LmlnjTtQHQHSyeI9pHk&xj z%VPaZ3-?=Dmf>5nHS$+~eS0m_J9Si*imPot%JbOz&!4sGWjp;8r6z~@OxsdnqtCB?`q6nU$~9lOF=M|)#n>YFDJDE!osuLpXldrBSyLEEXuE*b&7O9!$C`bycJ=L6? z@u}&{`}!LzY!&O4=juJ)Zg6+Wq&wcX_;fOj3(8HrqZZzkRZe&rv*MWM%oSc=WmL7S zSGcBSZ(XHx#dT%9r(*s!qqV|Eb=&8NX7K4Qn?C7nU5vN5-?~uqV{fli8Heho8BeJ> z6Fl>O;Oa2xt#Y}`_3Hj>J)E^dFKYK{RZj2A-It%8_R3}c7#O=+-8;VbsZ#Lcd08{9 z0w;IXn5R#)S#i?HF2wxcwJ5jv-v3g|rtLbIrFwW$+wRBy5rR__CJGnLuDY6P-TPK* z)wH&S2PdU{zFg&f%VF!hz+_fcVk80q$9IFZH>C*roHX=DpjHKi~MQl!gnfepSfwKUGa)-xxMEa=Vu<8 zbxi8|w#88&k7ZQauQ-~z^qj5lm5Er~e}Zi%(&i~llh<=eeq<7@7fwwRQ@{8pWW!`J+cEE(tN%xv9U@nr!M*Q4#0Osq}| z4t`o%bZ+_kQy=(ZuN*DBZ8Kjn_jB5>#i8z-lSH4LxFA36_;)J<9?n;{mME9Ww1&QT zvvKP|7KypjxzzI~=x?{Y;KbB3Aw}Ev_3`g&*HWM6PIz}V?15P--^uK}Q^yJ>$N4DA zg{dz6_T>GOch}=5+`eBC$XET!GqU>Z+)dx!&R0!NRXla_>9wu?cDk$A?=*ZKQ~Y0f z(v?j|-O^vh?e^Zf+5i0KHAN5p&DIN0^zwLlOErGxs(DpGcfKv*m*42P`=CPdy+2M@ z-fhqO6xDjpG5c!Pw~vQHi;f?;xl42NiPpEXSCy`v7LqRA_0c`db2&1&(>Us^}N`+R{BI`mhI~> zWmlz|ES?&0CG7j*Ygvk3p+`%Dr`HA4To$=}_2jJ3pv%wJd#+ui!jnDa zY^R}VO0??p>`VRor~KJ^VTXfIN6D2Ijki~4{FGOX>21<}6%@JbW5>%?Hj|GQ?Tb3R z?b?qWS+}O$pSnUX?2x#vhnCZyIQy-9aqB%p{$1X(k0;#xsQHu${}#3Mx~QGKQd;;o zu0QhgTO;4ieXpfAZa=bXYp~?46|BYScUSCQJLwu{*ZUOJL(BH$YsGb5*sHaiXT~X` zrCW~~#I2d>vp)a6h05cXL9x{?+n(-QutVg;Bsce!E4w0iRmHQW-hFXox^?!h&a#zC z8zwA}elbtv%JTUO^<-;8Z+1Jew^p7=xyfa0!rY(c8C0Jsn{VWMd0yIDttZ#M=%}u3 zo40j~?39`7l+FzCJ`~LR5*|~0Q&DE~C8C>-yk-H-Fems%g`R>L4XFm;+pKLMXebQ9syJYgN zNq-jFH@vBweE-x0(Nk{MPiJ2VnDlMmo=tz(x%$Z*Qd{10`D<}-N#CWZ9}Ucp_T67} z*7whbTYDB*`OKK_#FQFZQ1t3T;kh`?H{C~8eewER7=MDlW}S9gq%cFVM)l)@S9*>M z_k4XPa7WO%KJ%`kcd6kS-;-MZx9!T>Jzp!-ehBou z?0@dbx3{W0gWWxYv?Zo(nBphPvT5q2<=5HLi-b#awI58BoLt+#x5w=1hEJcg)uYzm zoxLPkbDQoT>y;CemM8a^-JbdFSM;WLfBTvAUOKmVzFKFU+jT?q)y<^sOB($YINuyt zRv0}iAgM9Rc6;zs`PG+Fu15a5YOzjc`|(W<#=CDXTOFFY@zk~Lak{GNTT{48i|@ov zmX=wtB-guFc=m!LJtE)gSDuca6~(lXkM}~wX5G?z2ZXlvZsS~izIqnNl3h{!Y%4<7 z&faB_7q`Ff&EigrUs-!Z4YP74{oQ&j>+Ral$=9QiQ5JCiZtt z(AAlLA1{+XvRwDd(XNHJo^Slscg6P4jiiMO!e%w8@;Sb|Apfqr^y~9C+j^fG+_ex` zRM@dB@u^M3c`+}mW7@ytCknsFw2x^0@$iwm|I|HgQP(VUrBAWuN@dwbwXX}1SyB3K ziR`wOSt-R=c>K%et9)H(x5hJfr^oj-3C$i&g~g_Sr5G>QdY#L-?4ft{ujY;K$>*|6 zC%@+Mp5bwD>R*d5Hcslxx=qi%6x`+gCr5X7^`>ve8ZOm~<_5{D=H}GTowDxLjS#6- zLgqf~_mpZB{}iy8u{MedKV8xvH~qBTuYkUvPt|XhKgr0vn3P_*X!g7J!6z2CseRt@ zyY+bLg~a2h*R)k?uX!V1T>d?5)82n4wp^WhE#AcU^OOYzA~ij0F7d9rp2hI>-{HDx zPnpg1Ys%b(`d)ZFQ{BaX_T(?;%81QYmY*k`_09g;UTLB|`C-Su1)?$MHhem|YNeK( z_||Nddciczw|SENt$Ey zif-H2x5G||pL-i+!kyrredna)E-Aa{sgw4LuKzmoYFEHA+aQ5SW?#K4pY{5ue4di~ zbk^c;6Zx&SOg?72`{cj7uR8yHo4Mrd8ePr3Q+>9sn7UlyW&JHZn4*>G zc`H}k^X=W>RbO7}YFg*1pWIvJR9)@-rRJyII}7hnDWKk5N+5W#aNwS+=S*ZJOG& zgc$z7qq%t!`%_nDr0f3MaKqU7J8QCE1R$y;$@~>h0nCtD^7!zp^)vBd@&b zyNGX&Q_$2ltMsiGZ)m>z>9E<@Zgoo9oL62dZ)U7->GxeCon`s|)t(<;@3^kY&or~X zdjCT0389@nUw6zGZT{%h=deIgJ1&^F#)#0nSm91`@|FTJ4b5d^d*3GFB%isQYO`Wmut?cqoA&Jq9y`I?%TuOoz%DA5v zmqy1d%vH#K8YO$bdFq6Jt0m4~E$hE}L@O&NSbWDT$9wiymVVMSO3d2x$g5U0VdA3w zs)xVU_FVZn2{jy_siDGWTT;TyB@P%&^)u`Pa@>5^vUNb8p>ZoV0V*V%s%&-z>d5&jiR;oGuRQ zSuCsmt9kPLfSC&?&%d-N?nC6d-u>lT2a+#s}h@;aLGg!~($(H`t91S@c-j}Ua`y%HKC!9$7rZ?+i{&Tdi&ff=?kns+Da~E6@9N1!$FPMz z_i5}uD0o7&*B?S;~k_m=jW{QO@2aRshd;Ox(22WuHaqI-Q1)6z#P?#TJ;a30B?wZC2=}{}<+`T*})mK5vQU&KHZTe;O?N ztrdBAQ>gRn{&T+duln!2Ulp@PKFxK`?2^0}i|d}MXG%RkYsxM8ImmbJ{3uPwyKX0M z_gR?>F*}}m^6S;B&HK;E%ZkjFFa6h`lD2Bm1XY(gJ3UY5Ewn$Co>lPkU6*Ee+>+oa)1ws~c{GvkKO+-B>O*1x|_K6zw`=?eAV+gB)l`Yv#CMv(U;_AkM& z!yoT3R(+E6bfdcWv7VWqu3wvd+IN1vTE5>rmG}L!y7K?*oW3ucrP~{|WrADpjebql z_)Q@b>V993uRkg5Z=+~>#p~)a-`P7-owlT%ouzs`wtViD_3`%Ce;?8p_Ik%Ie`2z~ zoo4mBo#_*dw-SIubZ^%@3u)h zeZC*q^{eWus`un6`}T4(Ik)a!zQj{1dCF0Kqd&a?b5pIl_gi|EMZRK;ELK{QzDr2$ z%gTJy%v0{OwmfoHjgQwZY%md=+-jQH`^tC6)4x8?4&Ll`YCHCRYEI(BL$MQ%{wjR+ z%RJ)wKL3)wUqPPt^p7mk{3*3CZ1d7x)64@;E4O9OscYi2I1>_6cTG3q*b1JMo*uc3 z0PZVUUpEWQzaMg)XJxfhjQ@}1ziX|g>t6lcwo@m;li$DMoK zJ&W^q(COrPvA%PB%PP;T5XgKYy>7p(w{uR6m}1l#me=K~Ayf7XOcgp4lGpKkx|fT6 zP`P{3sk(6P8%bB+7rbr1vT5z1AEC1XjVjVQ&s?y|JT6`=ntb$Uaqjo~v(B&tx!V?p zEI+1OwB*|PYwUmcCU^=l{hmC@d7I0LM^mnS+w{g>G*9cs5w_IV5;xavd-fr0-f__-VhZ=L5<`}u)YoKr~f9SeZt7SBADx|MX6Si0vuzHPY$hWG@>?O0N zt>5;4&3lU#?)%rTD!t|%ue~)xdiM8A`y4J8nXGH@<~_Q|udnCM<96i$50;>;Q)kk0 zpS(YN>!gn=f8zOGB{})KRZHL0UtP;uRkgBjH@9i@Rl|CpiNXKoC<$NQ^2kYG3sdN% z`@T10ntvuu*>y3-rfQdr&7=EsDm!<+)Ky)ZrkSo|bpOib$%dyQ_>zO4tC_j4J!EPd ze0|-ek83|q?<+LoYb!h};voFB-*8n!j7^!_jM;pDzRFhyab{`9y4w1!t^E3v^W3bg zLlyD+@2;@(k+mwna=kD<`nAU;|IL>I8Q&(H%UqngalPN)1 z(rt%+_MzpA*6h@5E!vt}8uHEHeQTw<^pcX;^3-=pd6#~#dR^;z#`oawgB`2St+ett zvcG=u=jv^zW~n}I`zY~q@3ccT_Dkof9lP*q_1a&)WxvcnsVoV5?CWBF#P?Fgv({{Bw) z_nXcB8+JuXeoR>#xp~=}9gq7it&g`~xhzt)IGg|JELXj4`(z?lg@tM_jgr`CA-ARe zfS}Lx_j{(V`TgYcp*?Q?yB>q`ssCQf4G;4BucKC62>kC<~R(*N|Y!04stgN`*VIDXT~?F|3CYx}3=ii9k) zTruS)i|*^MDHESqMjc~G3BQ^4+BUoR6j!t5>U9BaES80hTU_{;oSwh<)5Sg=@$V|0 z!O5&*7Xy~fGv0D$>J;G~wv(G&Ue8&X8!K3HK*#b@Df9cCRk=+`XNnUxwxxHhy?p*D z*8!cszOifeTio2qr0RRy$+FnY<91v}-}CL$yji%WS*U18-#WOp^YhlpK7nyYuGSYs z?mkZJ%4+?*^|BW0<++!PpSti`)#k7E`}B2N?9;ot)+bqKn_jYY3NUghyQFaT_^*7m z@bV0u^0Hh#`TVsfIk&xCv-^Pg#9xUEZyL?h_Xl5%G$@f>UY?ymx)AWMk zvYl@(>6HWuW^65}YcyZI%-;X|IB-WDjjFN$|hend}vHZO@UNA@x5dmz)utQ@eJTRQ)F3blKgpPkz?DO<54t zpY=N8$l9l0&PtvB99=fy@~rFE3h(OmyuZFZL*C3m^`DM=@&!Y`HAiyheqND1=bZ0* zEy0+T_P2I$OqA4d-+5x@?2>&!Kg%x6=yo@0{qo$(yk?aV&#Oy1r;n;f|4bB%TBW`9 zv}fd6Lrz(f6SmqXB+TU}{+(?eQ0`Sa{o?z#R+HH_d+TpqcX!pwjhfRxy?=i(s_Kfz z*|&bCk6l%K-Tmg<`qa;SMRRspT)Qr+{mkFMMKW`Buj~@(!n$3d=|@*h-Zj~r zOV7>nwYO`P_fGpQ+in|P`u*+rlG|sSpDZgYyYF;T!ZyTM?A+uh=VY%ux?HvR)%5K7 zk%k`(R(?G5I5KRZTIB3C*Vn~HSABh@YW;T0ix7$-69%{9`pQviQ$z|taw_dKV z0rzJZCObvSeB<0XYjyR;+D|9d7w(%LUl$oy@sL&0JTGR$j>yVy=5K#}JT9O5;=;mD zKOXm={{4Qxez&;(xs~$W(qX%n&#UsP`~BAZ=bO#vQx3IoO79F&)BMSNeATR-o36Fz zd;a3L_q=6(U;Oj!Z;v)TW1I5t#+8>hyERi;C;B>1kP`Dav@*}tYhBZtZ3(hZlAX@% znz!;hi+&^Tv;S*Yt=FZueR{LR=hnQfA4EFBu1HO*wawnJe!lqTe`l2RBhS0&o;BR@ zbjrlidcU8RsV=wE-}anLO?a=7_w2}<^7Nwg``*^YDW?xAC%x>x9QKZ1BKLxP4^MjH z4CQ)-&1LHoB~(|W-l;#e>V8;L6}QKwO>L>7p==*c=FXU-$UFJcmopP~tF5+P*t}C{HBaW+u1oWz4i{xE58JeSnTGH6scmMj zCRfiqHudh36gAZpYp2a8SFNA+Z(&xaNuX+C^j4uQq2>!TrRG>UcP{$pqV@3Pr=@&_ zqPr&DiM@Q_-ny>yH>F}cKWJ%({?1(RvRTUda`^t!n|8kBU-a+B-xXXz$2IV&{u=Vga*-mblSqq8j? zm;+X8vK`lU+#CG=u5Wmq`zrgANm_2=L!MPxXtH-NlJ9d!Qg*1hqMG~ah62?^~gejMaJxFLRXjZ zuCa2p_%ZX+2j)Qk0|v*W)Rv~tpBdEt(?!>#w^UDJ$;+ksH`eT&Boq51Fnk*0t6Sy% z=gl_F7uuR-)&KR{nW*0{EB37|XMY*6JMBhu`s}K$rEytu9kb5Lc66*({v5|Fd;09x zo3$m0%V+(w4SLQSw$L)-%Bd^+Pclqfm0Xpu_*K#VYv1KB*}mSo>ejR3TQBE1Seaiv zccO1yL&hrUi}PPSyX2|+>u+}W#fa@|el}dW!gA}sL3QJ#z_k53-m;%nx&ywKWNWHt z-Fms?!{Ygis(VeRv{!AN{Oy(2yj2o+7Ii0Ge{m-?G&Ozx+;dU?*4o}W;rZ&8?QYxT2sQ z9$Pv!rsSe)B1w#qB+Z^a?_qKm%NSY{!~P6$(VSj?)TeI`~Uy-U*rJQiCdb$PRJ~GukX4-J(w3;?#XG;vleyRdB!h{a*F$ zcZ$3fas~W6Zm-|}PwIpUOV76lOq>&)pZ$KfTYrmq(5d1bY5zW-w|Df*uFfmW_sp8> z9mKtW?YQT%WBXFmCZAB^@vT?UV=`QAaZz-xk*iL$o_AVWm;Z#$_exI`V@|8FKHMkM zHoZ3g+x2g;8k2?BFTHaszqkKdYU%N5^3liGj2*V|T&!M{dt%zbThlL}(|Fiv=e>Jz zZvWcb`?gmv%I%+Oe{0(fqvC95k?A+SCv2LMdHmxkZ;3{x$tHD6X1}_}J6&VpubR(i zpKSh@`}Fd^+$R^;on5kf((+lWINq$Xe|L3i#+@JEQ!ImH`_KK$?&1mDBz9?{Rw4H~ zQ!9-&-59O(-Wdw)f2_jJ{jcs~oB2xbz0~TfV%HD9Ie6dYy}iHi)IEz=-;!UM%GKKC zaP4_%e&5MgWs+g?o34i|+Hd_bfA`8wn>I%svzxOupilZ!zs#z29&c(d#x=Wq4=9S5 z9GYIj`*+T&MSu2HMBZ4vczfOzj~KSX{G3}qHs_^v`WKl<)UfxuD=y?PR6qXL%(3sn zr}q-prbqU_S>*U9_o{Jv^xEdX7IN2o1!jT04txcF3hA zo@ozHPPz6y{mk^g^-`+se6srAU)uVX*t}oM=^(ZG=*d;DR~8?+yUX$A?!UKAsYjPh zIc>fD%Fm?UT}|gh7j3+^NaEe~%FRptYTI8q`FXE>{cYprCmM6V^IhC%AM@CvEO^t7 zx#z6!9RF~GZOtEYH7X8qd>Nm}HJdncPTey1uOtByrP#9di{NziZ@sm{-gAdQMW%+J?!R%Jn74u!3 zuMJMzxC#Ee|JEyx;r1 zt@!)h^6C2f{}`#T-AtREoATko!A73MyR*!4qkhOSW$1MZD890Ov*GZQF70&_cE8)@ zEoGJ?abosP$s+snb>HunAM_P+`g*~czxBsve)~TL5pU1Dy?83`&W??9%5G&kF6-9c z7qRM9lUypxR{z_=G0B@dGfq#_Z5DmIY({-|=& zPr9*=4iWsXg8``E6pxYs(;i=9ht4MkT6cE01m78aVxE+$x{7*F>v~(!X9U z*=Wk)#{K$yO04I!^CmC0c4|3VXLwyr+ani>#x`ulZ}&1b*(Y+L6qef;UC=jN2o zZHn4a4!4(d8+4cCEtsQk@}PEI-^*!M?pv;~D2B=E#?G@f7nC=M6feoj_!DBUE8aaz z_TahoN+m(uweGP?A}76dx*PqxZ(UXHuTM{R$$Cusa@{x2`4ZDIch$;>UH_u?#Qf53 zRIUuH{_3ERYCQX-(3f*<8)XDeyJ#msZQX z{NwY?Q)sTtI=w#Yt83Mazs!v_$my{$$!kkXPQJ*!KL6~TW!+v|GPg!OcFRm+;Cr@g zh3ZC?d&QgbI6CTHwE1kCai;*29m?s=wuA-rADscS)#2KJ}^lg%!%l zr%XJ9Hc4i$)cL~wkzIxnyiKaB$AQsPYF(>T!OADk?|Bxd?7y#evQBwf%jY{AH>ZA) z`WtwiS9<;h7y10`YjftUD!FsJ+PHFy|LzUWRm*q1nzTqo??}qZ6)6ltd*>hcV7jW{ z%$1^_2QwGE+_8BIKd%9Ar}MLu3ukdWP>8*0>l(iQf7qh(DZAM6)}IP6*j1>%e401c zm1C<~9Ix?R7yZ|6_FjYkTdscl%4kK#O-)nPad-I%?d9y=98zUET`f{wP z4J}_0e znHU_}ylL{2eV&`II&F3PcKwMQ|LZ*k%cGw0U)#CRFfaCq`->Sok~1WxTj{4}C75#u zeZDGmJgztAT$WDmmb9KdC1-dnU7jyvZugz?`MA$?t*nby^LP3M-;eTIye1;DwD*eT zqCUI(Ub>mnOLKMkzrSBnd?IaA>f&DqzfSsn$ftQ}yuVq{-&pZ3sTan~S<6n(%Yf%B8dI7RDM^F4%s~_pjBdd%DuuWgB(2pP1^k zBX6!ncwNk%sIuVI51RMhI`uc|e_-xx@6)q(8HheRBGWZNw13h2NtL&ro&K7s=KX*- zeciNu$?T^^^Xrv*Z@tyAmnqrtE_YhB?aJtU&CFfL!*BKP&nXGsUH10Lo6YC>!WOg3 z*BI>h#VccBATT4tC;Zy`o^P^WV>X!liaBg`)Ku)7LP*5Cx?eAsJA5mUZ1MK2nLSecL-})9Z7SE)_cmMKR^y?=E2hC-)PqjZBlathvEncl@N>@w9rC`wx81 z_l2%lc1q}_r~Etv=XDaMuJbPFOb9OB`fbHD-lu=~&Mui6wXJDl*nGu*ibvvJ_9u#N z_1kz-k&8>3xlQ@Bzs8f#9+y?)!cIoUrseaQ_2(|%D0HMX<>-}kLA?~k-;a%AS^q`p zrhNZn9Q*E4=p*Z|dwlQm$Isp9ur~R1WWXjZ^UG6u{;rVb`L-f7wlDPQ+Nn`5+g5T5 zRmZouC|mC8j`|W}7PN!!^4%xzWzA#XEz4Q^_*$Xy;eQHi+>#Gg=Y5jdnROFL2wc_3k zJNq}5#f`@r<(5n{-S+iC!zxwdmoN6XyCP~g&)d~ z&@4T7+dI~i|ME`G==;-Ol`>59Ia^qsl6PfO=r-4=|HqBRQ_lMSKQrNWY2L2fKRT7} zmwl2Krt9s?y<@V|&9I{Il}+U0?%?zluYYmwT5wfki~YSl!m9a}!o?Hs1;_E1{`hXa z^VeDbrDep{j(Ylf@H><{qGc9@@e)YYC%FAZc#^b-`y>5ly&j~P5 zIkm<;B~it9-%d5DPQyh(%l+miG%8Kw))s$$V`K8txV=>mpZtz3ziX=U_{aDA^~|2# zLK^?tW+bK_W-9-BHJrW4qiCY%WVNRP2JaTjtw|PE_nY!G%W$f5Xvj#~U_OZ=9nWy-jCF@}qs7(W)QwD+RZHDSWKzYrZ>8`OOotv)zAVs~f=^ zNG+IH-4`UN^oB;;n)dq_nC7f(j9okF^5L67{*%~Dt}^^GHa+mCgtzU#`}dBDgikh= z0ips^+RI*jyY6#dXZ4i46U((vsR*uIe(EyA)-J&X`+Q0leB33Kw~8T5Cq_YY|603U zy}LL{gBGyt!x=^qP0(y)Tj9PE_RoEBfjAeUU=!s|wYM)tl#UR2SF$ z_$%&id)8|&6R}OLyH@>eSNoH|yzA=`O>J%EE{SV9L}C;F`@XI$zxQcF{fUyhg%c;s z?|W==@POnjn^xI}^Jne!U|YG##6NaIgyZqQLfcP%eHDE$e$xGy98=hs*5~e@E%Wtp zn^%6`*K1ceADZ}1DPEtJws(mybEfCFKBWm;{5CH?{cZ8TDbKgxUz*{|Bev8>HDuR( zrQg#s|3sGLty*6koIW>O@8a2)$>Iw(s!5T@t1cOb zes_K16RLeyW-h<|^~#rbe`-JB@8yhMRn#9I?sA*|?1@_uf8Y7v4O`G(rMjW>8%OW_ zxhwRR9=w#^Zg*P7EbO_?rtXPnURcMbRE2lm6qx(Hz*~O#d51&muDqGbT*~}T(d*y4 zQ2ti&E58f$FiGTbKic}Z#B;S;BUFECT;7f(_8t4wtu}A-F{YCp;%PVB=cS12zE57=zY31j$=FQou zdz6zuP0``8|LYXA?WR}Oo$_>#v+vuLA8>$nQ`}UXprk&h*tzcN^`O=_34a^kuMtx> z5}1^cobJ1P|K?*wZx^Kpt12E@@@CP&pY1E2u9iabU$6DLkT}a=N&B?pZFTzRqjXG5R7>u=ZC$cl%eyr6{$4HE-#5&9564^#dH>RLQ(s%@Ro4w&HOnI7>M!q1FrT~IW6Q;u<9a8pBzCuF{Sg!U z8lJAlBfZgR`I~huYqr{54|#n%==prxk~8uvudQgSdYe1<@x5zHT(4dYt>T|6ulag+ z>*=P@HOqt)jl90C3^qQ~9Gz*@ceo_(mRIU8zt|(eQ&Qe8-?(OLiCvQN&HYmsT)k|W zD7}_H``+SRFHC(NN0@5v*JD{3w7&S9^sde~B6G`)r^$#W)P5+d)>-!BptNyJQE%P_%1=TM9n!EDzm1$Px{}vU$4CtMo zYQIqL)y?u5yQXiB{Q26dzIBRQ>rHO`YZlWFGb(-&G4t=Md0k(0ZiVZn6`|~rcJ*s= z%pc0z9`{^+zkcEu{e9DWuH*%!#`|8oxb2wT$Sa?!sx*($-huBFHQ zr=0Vi?!9-JugcD!ZPOGw1g34(-G0YthwdXL&dR1Q8$OzZEn{S6Yl$$~C$(1EGM|%a?FWY9k%w+{RxNG#qNG45Yi!eTi}9O zO7o1uq>akXD!`^)r{ujm>nH{Z1EBE&Fgv=RLz!U zbYF0SW5dkSMvqx~F5cMQBcNgejH*V%bpEiv7*cNg0v-=2=H zs#PHZmk%y$zyCt(NAW71KU;&Qa{N7+=0Crx>c9T3_FElKvrC^^1gB{*u3CC+pWdvb zSq#5KRA<~@Eo2nodC^ICfACqYNvl@wtPk1wFDR8I>G(IsCC~l^aNg`ljf|AFekJET z^B41vty+eY!&YlAUKcU>LvwDgRhFBFhg7vrZ-D9xZ{7d#d)wzezx+iq>~Y|XTdO90 zn=N!DeQM3$Wj_S;ukWhLiufsa$nTpLf28K^%xdu$U2l$O&5iaeJMN^l%k>@Ozl5Le z314|`em}nc>5chpr{xy^n<}~hJO`ZY$%-(|$|r(D=j zVO(=D=&qGY==1fn6e};x-c|AC*c{b^T1Wr2o!x5pO-s3wy*OBR?(DRehfdv=>i@Op zT(R+0^GP~Axg|c6U0ys_U7fmUl1$7B@zt(*O}IJmR0lJ9|}2`P5S<{+Riu)`y<_o-+-15$S z=CaRB4uJ z7N)=6HtEvd{Y&2M>|XNirN&9q$H%SJu3oS;*nT%E=<2V_E3(C3My=m>C3q9_OOM~5 zq&Vw^?qj37oJ%FLoD85OZde#KUJ9VitBk`IJ(wYp@@35pzEw zcT==8{%}#NDBmF#$L5H_xxXu>6|L1fp&+TgR*ysG>jxR5k8^6>H}OxJ>u_O3|0l(S z3GPoexoBKz{H^k2V!B6C(`w~T-xd8wl;`gI@5!bUrBwP~O7rFZ-U%tC**-zpg{S{YAKi7Dx@2dRotMDs zJ!?$12R&uVmhHL{6x#Cd>Jc^WP_Z!CK&yiPGnalZ-n8jv#Ov%ur!M6D{mzuzou@4u z^ve7~cJP*StyQ|gqH0%IUL~(?m=(n)`HtgHdV2LEk4alLpO8TBzY7i{La)_ZCClW31?yeqB3`hGsl&fJ-??@rx`jg@w;@%PtwEsL7H zIy?T{go|D#9kaPS)wwE4<{E~+t9)m8^Z%yVPmZXWE-ioDR&=t(lC@@5rN{TokZw8Y z(ClB`5By}jc|4?yoR1fn{_Xp{VTa)PT`sBf4pm$~ucEq~Khvl;`6~B=s$TV@th*LQ zZ47%6W9-R!%|`aHiulUapB=O3MrXC@$RAmjs>-HV{lsT)zt5J4juXr`qdcWvzE01Q zjeHV1wPbJKYxC$%f$7%%vGT`P->egzCNwu<=In}g<0%oFKDZjGJnPmvV)xQ!SJdKX zKlZU#$7II}Z;j1abawxZxFuW9dFy_uOS)FjJZDKtH;ev>lX1B_Vv70KEj)d=t}yde z-AeBKDE`k;!nLJ;s@I(>KXZ1+;_?fX^*hh)72a?o@G6`1$zzqFif)#_)m5#xSu_Ye zn(%gcTgkth*G#pBSE7$>n}jCIKAFejnH^?6VT=CMCA>kYIZ`X< z7ndzq{wGJQqrR?8r0Hlz%2knG(arKTzt8;Bn{Z;ov)PkVbN{Y;8+mGyr!14BlM#dM z;eRO_Gb7&!sG4XbS?)fUe%)CxtMl}w%Ng-ydl$w8+iWUi5?c3p1?%tjvzaT^t3zjP zOVh5gluAnsTfH~ln(1hV!`H3ZPaY`Rd;Q+I`N=is{?mJ(7+hPJYQ=Ww#(BAL);GJq zhUrYd9arZy@9tN7#Y~Tj9#YKRg;HwEcc|!Q|LZ*G8_Df!8Oiylb7eb^M8%eS#owc79OEc;Cc zX?nc6eASy$x7sEMB*o>fJ2!83(H-IB=gZzmnr)Mt&AfBn*A}tV^vP#d*~~Y1#-f;c zv2XS=G3CfjC4ZwYE|Gn0_d_G^{<^A9oKMf)&40Qw{b;Ut(RsN^mzIZ{rJuW1l@|J5 zE+|^J_hQeqqZYMO&M%s?Wa|0v&w{L(L}ePACT`&s;rs1wwzIP?Jw{V(OU_wLWE7HF_Q{`haH8o3r zkZ-RUI9qr7)3m^3pQkx060&YTzuyg+%N@5ff5M%bADo}cck?ZhwifMOa&-3WBC$lD zWie{YPE`0sZg+e4@@5#wjcfA){(tFNs>oV@P4NBCH%I5mB+aUgKW8JHFO;(DW`fX3 zt?K5Tno;3;#ldS<7AWudJY%-g$De0?&Avao^yS8{9l86F>Gfy_q^K z|ETCwHuX%=r@mFzGL2lV4vL;pWw)Dy1XfE*D0V*B%*dGd((3fx6}ykMoDJo^W&g@< zGna0@@;wnv?d9v<%-*DadCz?(oko|NC)ypKMI~$z6m&fNRD&(7?n%@R^=XCZ;7ug({L&32gI zW`5~owaHs-o-M0Lwol7Dlyz_Uw6uh)OO%vLy&F%yl3VMT^jdnN@gCvSye@P7)bolv z&-z-5{e6=rT9Mb~F7vr!|I>ygPp?`VKRa{xfT_{Fy^{O?Yi7@{S$5=j&Ze~RHC5kU z{=7E%(#qt0-+%D_{1O>!P+0E%UbU}5d4dOrNbeHSt*kp9tzdH8l)dPTQ-#x;RppJR z&h`EfdGS!`j!1ih=t7bDBZ3!a_Jp^A06X-r1Sr+PGik&z&g?J_lKpb(gibF21DN0g%r$q8fowO1XzY!QSZOb8!r{AaXFimY(GL@$w zE^DUZ1a{Bct1F#fuACX+Y4r@!V_w>M;IB2WhQp%?m!_O}UBn{O*y8fw5dZxN!ux+a zv1J-fjNko2&b#VN&J7P^=40wP5%)d#4i!GNUVq}-|DGwzdb{F|ZS9);=(cB+)4tD2 zoKIEOdTjXVoZ8_ar;;0Rd|6RJ^DIm0$$N z(sy7%_`GSd$NtIY?fCT~IK}g9RAK(T$Lk*!UDz=HQ2BM)%1u1)Zkm60n!6-)j^1;X zwU73kK6Po5jmIrp+uJXv3Gixh3*GZ`KLFa^exv!aXB5+30r8`tII{VBV(z7apTDj= z3%q&%Zk2nb<6UL$5(`gl0iUOj)YLPdJ~G`oL6GH>LMDIiy~w`&%U2#2fyQBU%AUEP)Z@Sr zvOexE$aa45qk=2uaDWxx^^7`NIeqF<)yfH5)VIA;RNtB(89CuYnH5u?1CwGvsPP{+ zGiEP^lQAOT2^8?aaskK#z#OW##z9Oo65(i zo;mg1)aqtgh<1bn6ebFKSiRc)aAv2@0?^4bz&Ls>(i$vr+K)s z1yAz#e)?@~_O?AfwpT6P?l0Qw{`bs2xpfl*^_#EHS@_=C+5U@<|MZZ*-@l!eJ}Ei- z`=lvF+zs4u>jX}mdv{{aG{Ir>F>{deYR@jqT+}7!_KWF8Dk&fTr`$bV zck(~mYLC}qx?YYP>kom#w2@bkLnZgsm6d)M_oRacmsTn#u;}XQ`rhDQ(l=>~>M8}+ z(t9@}SMKkBv|>Ve)_c8QlE2OWKWmwAd*-@nCjXYnZ^^v8%;LyLBbi1ge+iEi1XOQ+5|OL%Q`PhOkWbD$Y43!wtCx|xV=$P+j2C& zzP`Rb`J;AgIUxM~T7nv&f&P-~aA>0!h@>EIMWz@0cHv1`p?fLPtcI$?sB^ppX73 zP@FKjCpfdrX{qdo=M%1ehe^AncJ5AoSom38hS7b&3;~03_d8vSUViUhS)sp7L~z5J z-iumqqxSg6$n5Ofy?VFxv`5{aRu#D(xGTH27!1(b$$?wVZ)LSl{j|nUR=(O zMMj8&Q9YCMixDjQHZD>W=&kMkyL-vwM@gr**NdkFf15Oo*W~7c%4=&P_Z@Y;xNDmI zQ&2W^n5pnUWvx%bcQ0tp>k@9{6jnRYt-nu0*}dLvO%0E$bp7|^aX&A|g-NR3PcFL4PkniL`SVEiRDsDS;~I}@M(0Q$c<``Yep)NH zxY(A;y7|iBaCDGVo>265>-Bp9$BZhzf|6IK;D)`MZpg1z5xwfgQ+UYi?!E1~d1vy>JqH1oNF*Z2GN`zQQZ=-w|Ev^s3>lh7fS65eyKbdb`zAmQxZt14K-)?)a4qMyu z$zsvhPx-(8DfinbeSUU!vfo^*)^zUTBijx?>0kT&y#4ho)a-ZkFX{IGg!*431on^Her4Ua#WzyGh;JW1=a9EHbw(vJmxk^OvbWw+tM ziiiJBPFCmU7M+`6sQl&gl`B^Y?7yCwXB%Dj z?`L}C#w1sb+3(*lBtKao_klJ277ts^t&=;luB-^W;XHZoYrFq{ihn+vo!`PLd8@DH zgk|2H9T)OmJ+xvkKK0|{W6-+MO*hkKi&o`K{bK$7lWgN3fd}6Y=w|+0_~bscz*jM0 z(rDATsxeWcaQ~lA-W&W1XIzq1Z2_%cy|~z&`;_YSo#Ltjt(Q1>T(Zy3GPS&^9vBxm z>+J@s%4xp>;x=BJIjKwMM2eT*{e?b$v(0p;bT8tyY<5sQJ;$=R$*8aB^ugjQ?T36V zH5N~4oRe0$c#dK6F_zZrx4MhGeplo@J2P`(Z-Rj8ye-1t-CuoPoFn+acJ@~BPcHQa zlbV)JEB|+hTmOXZ_dCMB&h7vIPn+NNOTY%csYQJ7x z7=J{s#_>#C)l1c%FPG1Eij}YbvvE%GIm?Cri!S_+tN;7;!ull7KZ{(upVa^VJ-_Df z*Xx!=L2Q3-{hGhfGWC>5<-48F4=J%2x$*x}&#n4&QvEO!d-B8G@Aq+k<&!qkS^jrR z`!{31b$dQ}Re~lM_Al$*e!p%vXwhF|k7L4H>(%Xshd-aU`q~*k zl(yNB+}NNzA%SB<(!D*EpBGeY+LV`aT*q4@Pou0|wrs|n>UTSfKAlu&-+sh%vKs5s z_s#rv99+szp6&ntH(!HYRe-7VUgdLER-;v4g7m@~bhq7bvitR7aX|VTGodS;LY3cc zrk~DSK3DCyeEps~mCxrM{8V^5cYEvmdzH^+B~3B{D&%GsOm1n|`~TnXqN}0dtgSPP z0xc$rAL7_({pZ8s=BCut8u3`ooG8EO>Q!mJG>~cJ?qCNZ0?ak@_J3k!acHUW*l-Smg zv^8x{Ox4S!MMp)$J*Ml$KG}TUj{C^eqdcdQ*4&)&eDbD)2W?GGZR@Xo>*{{MGwR~T z&+5?9P>6%^&LYrW4uy-$=hsPzcv){ySt#Rltk3Gz3Woy$KML+Tb>>`J;<+Jc!4<~^ z0lySVG^WSI@2S}M<=x%t=4-d)@-;5YT9@&3>1qEBEO*w4)HtniXh&?2{WNx+((*fn z?KykDUMqUD@i<%fS+m?*Eakc!5}T4!8x}mg*Q&(DnSFg->;y(tvC4wR#Wf=N9FkrZ zPq>##rhfZ$TAzPi#)+0sYZdt(%1z1srdGnb?Tz(@ui|(33jZHAo#1~@^F8~f#@CBt zZ>LU=Wx9D>^pXeX{i|ytjrRnv`~LQ}xq!qkr#IHm?%&B*=a&9bT-?}OI=xqD{*CmB zGwrf}D@e_na&b%M`YT zonQNnb4P^a1e05dzx96Yo1J=pdu)@h^8wFW%N{=WhZP|lOd4%#HpmwoU~J3~{G4=6 zY2RU?v=r`eqbb%R&N}Hf?a^Xkn-!hgPTZ@0FRK$BbJ(><^zX4=>Epejhq;d5YIj38mdS)CKE%d}R%*$|t%Ep8bf4|>v zpRrNC{*PhA){f1$icaf3otZw5Q~jFpuQ%&@3&V9Xvv(-8RkTxBp`>L9YAxsrRe~auWn2H%>9E)0l1NX8WwqyZ&zO_Pd9EII10E z{^WS6L$Gy=>sp|v4Ev$(H_UL0`V4}nW_w|6NR(I)+HW!=Wy%Q_4V=| zQZ0O~LYtf?@#;it)M<1_=We~~wytMNb{JR8)_{`u+OJn1%xYE2y(!&!{@|_FuiWO& zcSKhjUpvIu*c>6~-OS2+wB)?)_cI$`Iqrzsk|B6PI!tVHi{l3!MUJaxA0@DBiv5nz6s4wXA~w$%uwB(dwbi#OFWuKpMGD| zt;hBK!hGF#!rL#H->)%_dCtNeq+YAhdB= zq?5oJ_ceQjZ#N0KKVn+rC7>wc7{d}6S6JJ^^LEQ+zk|6#tRj=d-fyr9jO&zsb!%(3 z#_cKYYlI^np0jw|BlGm*L3Vi_FY~X35oO*TveS$^l&6U#z4`V1z5Kg_P7N_ByB{z4 zlkrIS-?1b&>Ezh+6>?7OhOU{*&PKelIh_26M=s`nfn)2O!b|3g{u6XheCt%7C&3}{ ze!_P*`OQ1LqZk)1zkk_$1C#LM488s%wn8S=yEOiSJ4!aAic)|O`Se-jzoU4 z2{SsTo69a9W4n>VSwJ!U!NrBn?GAAV`IOi%+P+*e*(TBBQAy$SB^(?Up4v~^4BU_s={Cpk$9ocRBH|9@`1xhdC;Kh28B-&Ew~ z9B&XFv1ZA$Dh@&A*NW?UBI38-ExSD-fJKPqrO~3l^Z)-*-?4vEP0Rb2>=X9<{eItG zVB74xU79;QoSsN>UrE>7cEo$u)SDO1>q;wq-8dt`NJ+gmd-vOI++Pcl0+-E?`hR$n z@6Y`Gf6HF{@A0-?R4e-3{nTcomF$rl?--^{=-hbl^On{^;j?X7=ar<>P42BN`lgj% zTIqh^yQlX1{0mRZSx#9p?emYatZ$sHuurpa_AY-Zr3EZbRyWr7M97F(aqw-liVzQY zrQk4UiN*2It{t)>hx6|4diqAV^(*hvN$G`*hr8O6a>UlIC~Xw2d$rVWl72zLNvFsq z@$dJ1KG%3B(7x31hzCnd}j(zeNVzV;~>8NYk|JEqlf zmG8Adt$?-{rwH|Maug6 zqN;vKA6>68J5rg|OI6HZivR5f301z=f&1(JG8sC)%(=g>Hb7)&|7(ryZm$9+Df&k! z`aDcY&N;VYQu-&95;QnKxSTZ#KN8=&c!+$%I~IXY}sK_v}lFXtql98 zitI&&{725OY1o!%kg8;VKT!OmyT*ANf6Iv50a?0nyh>f&lJ}LxjTT)HD|`Im(rn37 z%a(0Y$ebYS!TW@TsmXNuNR7dxccex!MWx`;zY@wEI_vg1{2MMWDYM7Ud*r74+RsM{0i`6%VX=j3n^)dQC{%zC+dL#m3D z-Lf+Z;U&$NDvD-lGftNE>iPR(ddln-lR2mJrFl7fUVEN8J)h-Aq3LPIN{^X2#ll@& zMT>VlG3P6mp8c$B%KW6zxO)aSB6Oym-5glH?8g3s{N+v}Hd8eIomIc4XD`#Z$$_Dk znd$JJBkxt8ELy^^;~}u<2Jb>i_GuT|?uWd4xZ%pTwA9uN$6qtMKh5%%sNZqcDQm-n zB8Oj!U%y53RyK7UeYfy96Z0pB1v-hH51Z=R=IC@ucs9RL)K}@g;r;4x757Rf@q?SX zS$nnx=((8gnlGOkEY{LjrYPPrq5QB+<5hu~iW_X&k6sjW?OAd$Y(r{eOWCi798=U; zUffo7yEG$5Vgio_8;`K;>eg*RGvovQL~MTLlofGG>BKD2nTLdQrYM|xFga9QO<4NR zMk9kp!^{OW3r%MDn_mC8IBinmQZ=)0igA{$Q;qFylJ=|5W6&}D`aJzu`XSY95yuZp zKBaCt9NXkMrL!+|RmEzbYkv1w9e1tSbW>~UNoVVy^E@YgKjPvSxg)s2dUf#{0ckna zUhl-hFTYRAHC|H7Jma~>=JVonPE1@(k8QW*KX2pt%rJY!q_sWiH+*ggIf$_N9y=@S zZzHIq5>dP;S?k^$rvSTY(o|v_+B~xXV&dRFYmnK_{1mYUdP=!;&%4;+wJk^ z<|XZ%Xp*8djmun3NbrK0h^f)OM9~k21vR)?PBkrgYW8jMF1Ih+jpy-Ay1h57?8+?T zRe$f7pbyq+318mre$OYJzbN8K`2+F4Pp8MTJ(W*N{9XIxd(58qirs6jE#h%fIq`@2 zWf!kj>V?hQ*@~Q!7HNC3tebStbbV*b`+ylA{^f#{kLzq%%i3cx3br>KGm%HkleQZhjPD-keSC({d-Io71IQY z@72F4cCBpx*P|Q|!?8iYIm|#zZ{N>nvnTNOOnI~8(i8J}6B@6^2>Cy-b3e9!(XNXd z3Xa@4Veus4UB&mi<%hM(ZCE=iSuJ&EOP+tg#Bwrm->K|<3-vtYiZ`il(a^J5{4{|7 z=52-OpRyT99x(A<9nZjtCK>Rqw=k7FUmjOijl~!iTfIHbLU;* z{EJI1?m5PZeXlS&dU%<|lMhBRjV?|Lj(AAfU;D$69I1F)K>b<46agl?#Y-5R*k>Ai zwr&>KZ_a&#w!!m7aBCCv1!fNfBMHMqvJG|Myn0d}~j=0tbM`nGlb7yiFU|I9 z&RM;oDr;qKI~ZBrbPPB+acZW43bRD+as4@y9ZEP%{(ackzE#v!h+9SbZlJi@J$dFx02J4-^6xM@tShkex-To z8(VfxZWW#Vd&T5d9kH!zZ05P9JyX+eH`~16kP}z^?K>&=gYAqWcbe^Y{eQN2ie%}@ zP2UvR?;l-vBDinqqrT8p5gU@@6z#g7Z}2P9KdJi%G()>cal-BQIf))I7qiY)t=jUc z^V-v0N0;p}tT&q|sIl>g;-8FUPUVD>2L~Dt3b_?%tiGz@wByHuFU}n|HnRpD*=Hd3 zTYPb{D^j55RvKXnW#FusGzas%7iY7?RHEn1I;30kI8mEU%{N(xFGY} z#RiiF918_pjLQ~kOz7?S#9?u?l|$UVW~QQfOnb$G7Da6_%~wZtCo7bQXngzDz;e;= zPJh8q=beYtrG%Irm&oo2f2e&nZPky$&o(J9*6n`BHBDD1^-6!g=_%=_lNvdhI_k}G z7uaqQSABa-I-f`T7fbi;gVqk3j}!h2esGJ6+0tk8>BIz2E>_`G&Yv7B`sI9TG~ynd z{hT1U)htpnRFws#u)+@2MPvsT|zym`q+Hc!c)HB~)2Q;lz-zp?`fap4QvH zv-!8g>>l$|%kr9T-<0q89T}3CQ+${2nZC}?DJN%ay7ES6ib1-f`P;@d7x|v~pGu1{ zy1r?;V6jrSM_T7$>xSkXd>k^@T>qujC7w$>uO?Qgkh-O@c1lih#%X~cSHt65Eqixz z{w!Ge?EZxCQ%U(M>yk@*s+h`>;T6#SuQc$D&h~iYy z?ELNEi6e$8m(IvbIa3d>a<_csE+y1BslljGso=Ek_Ldo{B214K7nnXtn9qMnO4!{k zDzR?MP1kuATHh)YS!IqgdiSk$TI><|hE;`U`;r%{%)IzF@~0eDN^AP-oWwt`y~n{T zz$ReUqLVy7q??Yob)GnW?)#N~X~AhuNzVgjbOayd;yAIU&zWH&XDjQc4r`SqLXwJ& zJCdC)1Z+9LG)G~*#^f!MOdOi3s>WX>9ya>K)%|?x7@3l4zpIP&Sj34O=8!3guT+->3VE=>_)Ui7A4%62(2vVvZ8ecSuCfER#<)R#&o4-6*J|(#g7Zalf5a zg^Gf`^d2K|p)3B?nzLOM_sGo1&tc9GoA{wKU9kPA+Ovnn=WWde?30oXw(OsIU!g4G z+06DebI%m(deFpz(_Gbp!`IH%_763 z)}MOzG0CUTuAcYj)Tj5dji1^aIwzShA7&FhCAj23Om=|62Cxn6fB0~G>|9m{& z{HbBnp+;#z?VB3H5sN#PJN#_aapb6Mt5%$&p~uX|!*Plu(2_G;XGshCHR-E z;p{Vp6^t9)#W<@JOa+w1DkBy&)EMZ^n4P!#t(oP+7U8C^9H#}6wc=u?=_ zA$iWviRW*)sHmy8@GW9#JhbR|V)ATDwn?q3T$P2=DH}P>ouYjl^G;}ND(*EaRMP(1 zCuOQtp|@d!ljNE52?jAjHyy-uKtpL85{~>i%E)N_ZpUM%S=qPc&xmcB(7N-=GymDl z5i6LBy>I$jp3#3Gman(+hwa7ah?{~7?i;h^oIh~rLE>lUz~5A^lYB#r*m4TWaNF4ZE&&ek!8#|wuYzlW$+ohJIcB58R!SAa3*tx@YI-l}A+Wsr>M}~2(%fg4dUuYR?KY4Kc zRh7+mh1ofWUd3$-6hPihR+H4D3PjOlUr&KF-U`}2os-I0{s-WsZrbUftEofIzq zCtQo#b}Ox$YQ~<#t8OQvZ9mP#Y}rv;!SF*jnm?YYVdgxXI`L<-pMQ5nOp|E*dt+yXt)ODYGZ|hW9#$yJ0 z=eNy}?%ipgbHP@kc-lWZuZA<;Y_iR)Q*NZhPU$~7f1~7Xj->x-I>r3^f1Wsa|6)Z| zw1;Ecok#vV`}CY8|KxJ-K5>po{M|LN!*cn7cfSa}O53;f`NjPaGmLgFDqekd@|j|t ze>XC_4<`ujJ$L_(x8zP;J>~o_lDB*;Pl!f~y+6zEzF>)fM&YDm{OjK;x}>*#Nk~lk zDtVM)wct#~_d6cXX*(?!Do@Pdo;wGPE;p+dVJrf&c|-gZ9Jru@L6cS&K`#ViO(`U?8mfPx()sQyqp>yH}mt} ztFH3tmqXuKzTI-U$=bMZy@2Ly{U2K@7cMY*mg?&imvl5@P9u+De}Ti48Tuc59N2;$ z3O{el2~l-f}eYk-cPknZvY*2?tvx zzdydw>>B?}-6l(K$AhK^GlgRh9DDj})#`Ozc{eV$WL~Unb4`B6J8%D&OWw{JTo3ue z_#O+{b{y3x>3g9hRPTJQW8?len@%74tRuGYv%$WPvvxh1Yu+7KZrT-@WBs_t_)w|eoehec zKk}V&vOgUB>PoOb@7q+qqq`f-KGtbS&xts&*tgDtNwpPJU|U(YG+Y%9o8?*AYt(X@ zN3Uwlrdz9c-q}7=650JyfmvLY(Wr5@Vwe3>!xJ+ccFfOd`ZIf~%9Ldll9w1JS}E|Z z+4MxLT{Lu+M2m(rPZZznEfYDXJYOlkC|TQBLqkJeL+wtpk&|`g%x5p|?XB)GPgY90 zwAn>7I)86z2meDmHS0UA8Qd)AJ)+i>3aIvave+o@m?EjVTp&@o-sj8~*`(7cJZ>jW z9AU28Sgm7dsl=@}W5TH>nU5mguN)GO_MV=)zG=aW?^8^ez69%7t~q05Zg?%?$3Z8S zoe4aX4;)(a_!-;D7_t0=EmITu#2h4=BOc}`9=Y>##`iO_6W=F^Pn3|Ja<(Q-`@qM7 z4Ti^RI)xm6F56&mE`#~ALa~0aaPgF#QkV7qExUV$eS(RH>}SUFkJwJm>)he~=-Amk z2X>_LCCy7Xm*AJAw`9$WN4{?kiEOOV@L87N8a{jH^q=#$8RSbnOkU@Bv*+%GZA*SP zPk+xbMV0A=vCgAS8cZh}H5`+2viXj27PJ2netdIJl|-=4g@nlFk_F2hloT9Q-anL_ zm+IHJ#L(Zs{ASyYn&bC-98V?irNjt7I_R0fQTX$WxQvd{W{sA*Lw^!KYkXZW-%x)+ zx#J>*IcG#u%9qt1+3?8Kg~?>ndd2mrFSpr-tz@?BsCSL~w~X&q`o{L7H67BwI6fXs zi)3NGF{YD2`4k@S| zYYL5JVwVf?I&9t`x_z95r2nR(Pgug2KVLY{DX1?-KjI9~NAp!*SvH#oQPF zdpyMqW+ea6O`qeOCLugw(u(f_VR|JJ$Hc3qy*2M>=iS9$%)!3@Zw2Ru6{i;&es0WN z%y=`=a9XR{F^#-S(=vK0f=a}n*`y@KCCMe6OL&)DmtZ)3C)=X7t3By6y(0=7)i*@c z-g3O1!F;_^PIAK;LwUphM&&%RRwgG--)UQSiYo=2H!vzcSy+3f z_1W6g)QCUYpM}(~FD?$dqSbLP$#dobvA>)@XD+VLKhr-afy+##zv%C>O+D8x<)?Qp zeUN_O!o)xYg$NBJ0l_H@%F+UIA}nko%*~CRPrKv`*aW=Tug@shG5us?<2eTxPlXM( zQY`21^Qft5s!4e8m44=Vx}d?((%|EQ2@AdmIOMO^tcmi>N)GU-S+<7T&-cq^QD<+7yws{^~6Me!-Qp4ng&)x~gHyqyaZ+1?;hI_(cQ#JMA zV=vUEGz+yf9l5{eSHb+NXKha$d(ybHp=g5a&jWWjv_1V#NMz=k>TGxwsAuH*_~N6| zz~E;S`oBf+$Y^J-*)#D&>+3vE?Z1q1a#=cOMgBZ+j89)<^i5J&!{dx~Pq?(Pnct4- z^6AHv-}Uz>|2u23c)#(sp9haK9i5RrXHia|+GoM1#@`L5<=k7O6TD=`>>2$(QsNT# zEwY)He6RDI@14UI=BLdb&#i2!WKcR`bFz5LVX^fy*d-rtdy&c=f8xlFWpmF=KI4DF zUA8Cmi96pjrFjc$QXW=^9m$p3&Z4#D>Z8nw%$Azca}IKCI$l~_?II^)7PI3(^R5-` zmU1#ly1$<;l;7PW%G`L#@8EfvCi8hr31f+kVvk7kJ30u1twH zd0lz4d(X3F6~zl5N=-G?ihmQ)ch2|YGq>5dlb>#UzUckL?2Bviu9;17eN+|rsG!j) z(mAvF&C#^&I=%lMlv*$;G%)3z)cx_-*t^_w@nIQ`jR)6G)HzonW9U|JIk|C$&gmm* zjvF>Cn7y5U3Fmj)ZSNZQ7|wt6>BJ(TBRy{$6{J*5m8!L?m4wt1Z`T;uDcT*`yfJ>Q zF4N5&Pq)i-+ps?t>1X8N`&jsGci$ZSeLs>8^vy6y+5K|a>_d-4xD!q=WF~!No$k@0 zcK-IfRTGr=^k;CFelXv~9p^K_AnA$*_oI`GjF0ec_d0!nw=CtKTUhjlG`_|Xq3Lb6 zmAWrZh)YrXy!NQPb5G&MC3zn5oDVnLbBu}nqGUE-_9ODCHa8!goF5)<1Vty;t#9?d)@?d!n(wQoYRYdf}L~kKRWBa^Q<82-1Bkxl%!*NdJOPnqzk;Q0~T z+d@)~Css693fzpyX%lgDE8Myy#zR9QK<_hq3ujRJ@>0WeZgLVu$X<1bM+_P-~H{qY~vw?fF#4rR>4E>bt743bm)k! z+ahBBTT#>N)xSxuhrRtxSiG#&bT8hDk9=wvl*TzPXSG1s)4R62Hht^blwY2Ce2L?l zj-!W?j^2-03pz*RK~ZPw388++3I)d_oe#M>vsJ=$JB0J;VN93ON@9!{O;FIJ{dyhBas zGFRrJ*ogZ&fx<_oHe0f4%YG~q2>W1|Gf{hcMKylG8t}Y8)k}r8N8~mQA?`K?GVrss=C^@{ATglm`vRHe$Laz1++i!AQ#rbjX^~`I! zn%q6F1Pi@&IoGu3{-qb6MK|_s>hjd~)ahO=@0O~QG1FDD`{!BTi~r_Nm#N%Y;C5H- zZGLgTi=o@XM=qIL7W|u3;~uw7S%AOsQlIDI_s%Lh%*IBBb`g6N{+&4{SCnYE>CGkq zrFD)zJJ>&^+;=XEn6Wjta>sGLc?<0()jCX42%ob<-6=)ELuQ+<`eZq?PsE9wB<6hn*GT&+i;L649rB zpSk-z5}f&>W~1he=i4ljKIepN+cal(l4n%Oy`D)X-a3y|a)YZ{?W2V_rkHu2ervA& zZM$L_--eGV_YD6?CpCZfy3(u~Hoxnfs9Q|VyGOe-CM~@4Wy6cV8?6HK&d;uy_SbTr z-&A?Ai}%0X_~u@EE@Ef*B4J6fw){Om@7C}4PGYH=+2+ux#<$TcVs4_S%KkOEJRI*1 z>&WgtX>xv3SJTa#W<@=GVXqVpOtbCSv3^5WosQzkx0O>oPxW6rs3lQ%`kC-qLA=Xuq6w5?t|+&0YY>kT`-z?(bR zuKf`>>i6u6EO(2ZM>SW``DI^}wdErpf2}{YOh5WiOVf$R-$mq^9`Jt?doeHk2X?U&5&#lC--^Nf#FO^~Y!{uQFb*mdgQ9M4&~(U#Axe}$XObf|lA z(eIbmV;wE^`r9^gvelJSj~knrAG14Lej-)+(KGew_3t@C*jN^&D99Oh>^-($H?#Gn zb3|E^Gfxtyx5*R5Uwfu9b{Z*(@_)~${Frj$ZRNYIf3L1tcjNiu&*_dII@IfJl4eFn zR_gYBlFjX0tE^Qma{AN-_r1MK`yD`o@cG@#=hsvD(T!8w_K(c(RU`+f z-B>s;;kvipP8YrXe?C2!pTqurqqj|StI4NnO4r!99k++a6uN$pJk~F{C_S<(XX&)N zuWs7hK6ClvNruV~`;Wy-)+c+{pSE4LCcs>++H8Aj!lZXot3DoYvq}1;UM#@4V%y2w zlA~Rl0+xJWRCgBo@gUr{%(&Qk$M1K$n>%GE|9iQ7zTBT}iu0DNf0}T};UVK`(AYK0 zrhCQbZ5=HSvp$*lV43ZSR-@y~=6+$k$M}BY`wvImKZ4eLHrrc0y;NtWx~*Km zdPz>uvfMA$_mpSqc-s5eFRrQi^l{~Kd%=uccN^B*rxw4Tv}{J5%dLbvHUal;wlSFV z+%wed{P=b`pP5J9|G(dxv&Azfd9FMo7c+a&k?H@w1lpKR+osCk5 ze8x9>_KtST!@o>ax+cv9!Q z$h#8NXHr$<6LMtg|9nh*V|ZWScTMloXld0T&VkJHm~eKmZPkX4)(J? z=_-9PZi%fGb zt*)7Crzvla{FTAH$)P9UkAm*?`1;x(^Ueg!uldvon&tU)#`ye`6j!Itr8Q=op1I6? z{MTX6%H}nm8tYkDCr3>$lNLPw{r6e(`%~QIDplS%ANsvOzfreKnM z^rLT+Laug#-Gh< z^EtzKh7&ZsKu&3~#jn~X0B?ixL*qr{M{*ht!?n`VO_dyFrpSc~gEw=9# zt!dK#Ec?~K{KlMG#D-tpPT6&rZI}}sZaQi@SUPsPXC5#*Vmd|Z)!|!gh7O62o(_!*@=oph zbV}R#=L9(s<~7a&ykUhmrImMi^?v$%N_#y^H}^EXJM%08js?1DNVl@8-U-^Xh{aJU zg7?^=_M+xB8Ubq@oFi5WO9n;+OqtO9AbL$I%T=d+JJK>URx&qv%dScLY@0VdMIy;m z?zL3oh8Slv5#yAm3twMcR2E3osT2sfVOdm=WV;||iY@M{syxlChZhEgC*D4wR zNW9u`RhKhRm;8`@yk@>C#I#wevr%VpY`fCyQO9e#6j{5n5* zC=_22>f+H@l5n{CX=HWZKK7r>-bYL@J!ScKR(6EVO%Gw!*<}t(PA+o|u=sjG{=iOy zymeD+A8BR%ez|$VBG7POkMXbmUgzrj6U_XKX3tFR{iPt>!@cHjzwNh(iu5$kRDPvj ztm@pXJ%Y!CpY+!!FI+zVvj58dgpzfViVJl*6r~SJX(r1Zncuk6?F(X&5j6ko9GDfmy># z!^n*#;6?yTV2487G&PB%C;u#(c+^8;(?Xq_bqX~~#wv_SM^9O`JOQnoO<{k^`-vrN zhrN@=gqh9X4}KT(cCWp{{IT`M1-X0bUNba{zws&D)tInNT&PRlOXUbJ6RY8|y#kdt z%UNV5GIulQwz9PIm`ACv3z`u1?x`@(hINiDKjfOcX1@%7wvD52N7ToA72hYs6u2*A z=Ce@vB7Iom=>)Tx)oE8=wJmM@e&X4kH`kk%3LKsF?LzyD)dF^>wOVWTWEby#a#vyT z+lx(2-aqg7@87&Hc%s^LmmKcov)>Lclz*sp!g2r8zn9Oytln|?()Qk#k0);=9&EN_ zKbgK})@!Bn7ta?=ObulB*7-Y$Rn)uP_x?6lrW4Y&jdsZ!*Y!A`O7KfK*LcSH!^>zh zwl|f(42wCEncg#sRZQq_HgunFw8=SsvEICeF+nxP=8vn+$#~fAP%={cIq#_U3E|JC z6^3jt|L>@AS`iVkp;D~!#p{S&GwWYIKO?;5+go`iqmy>De(WDZ)@4JT6?E|O+@#N9-*SHrlvg;MDENuGpn_!NtA16z@H7*W^yl>%6qZ3 z^544~3ZF8KbP^ZzN;kQRP?9j~{lV+GDEe|*ourK1{v9}YimfV}F>90|t zu}mZX$o!e7S7|mr=)20XMsxl**T=V3NIw;{@o9AFJaVeNmAf)y^ImqHlq;>O%&LN} zQmK+Q1=2@;H6&$n%}d?1sQ-Z+=SQcLOaJajbIMrB?`GD?uFk}Up6Ugf3NyIW|yvy zj`j=I8>Tzli~Nz8x2cuw_WtfdhxtkG7TPD0f!0hkTsu{;4N|MefHy?Gr zwa>xqzrns2-*r3^bkpP(#y-32P?v*}!#Bp z>w+TMECPf!K3ec*!I?%!yG$`OI6A4pX9w>hfsK>;Sh+qq z?cw{Hl)EVIRnk(qPi?0~j20|exWmyeU{k@%Bb!((olYKpl)16y$n=lz&hiS_Y|vOF z;cO9kuwCR6&on;{jdyK33KQ2Z@OyV$%k%b%{=;TZpVU5RJNC-{l%YfryO&Nazv`U2 zHX+ad2Oc>lNuF9Hf2m9);K{jOjhWTrwQNl%IwyJ0KG3sGN&LO|CXZS%`IMBizc%bF zj+_>>&wNhuaZ*z#`r@oCTN8DN|MvCn z>}T7&? zzt()UJ!Le#=${$4_<5s$8@dere*B&moyS=$P^2K7JgKn7TQzyk&!&xsW^8<7t+UGE zLc|NlX##KN%$>nL^Gxsbo$akwV(Sk~HSFa1qjzHu6JuK5N3=q~S@J*=o1#*K%yzda zf+EsW80$I$85D9guKYQBuvkNLg7OLO|7O>vy8D}A90eA=YY1e$Eo8YsCt&@~^@+TV zH3_awMyroIB2zYhDSbyPuI# zxw^(XqQ2ww<|o=V%zO#HvJs04UM{}7jfr3J5HEMPg8Tn}f0+X}|1jRHb5ZZ~mG&Nu z#Z&k-P0WKh^siZR@V|8qbgo>ySL5u2uwNZJ85g~25>}c1P2hH`5r?2_UWYl$^iPGC zO^P~%e4d@1skvFBQy}b@@j<)k|Y;F%)jL@VqDeckb_u{YL|%?AN}T9OSuuekIFm!@uIEC2B9-ncf+i zZ1d5sQon-f!#s)hKeNBP?%%4jrpI(&^MgRS`@wZrZpps&p3>_ou_-w+Yh%I9B$mk7 zpXZD$+~)U2CFUMZKc@4^V7=io%MEuFwf_pvjIc@A6Jej0*tG9t6H|)FP08)kIHR#mdtxpQ(9=w8Wf|?5SN-zeRYaM0k(yhxCulM_2Ay zb4&HE`JNisPGW^X0gsDsy3Iwl8)%3La8EM*kjz(^XexD-opq{wiAbE^>1iFGPWKyY z?@D8QB1h_9@G0nRoUPHfNj~btb5;(G<|1 z=eW>N+)YI2)G|Mh-id}H>?b6+1kVesZ}geqDxxq&W{T*PG7cW?k2{Pn#l3oHo%ChL zxxFX4FY&ppari9TCH8%hg`zT-s9wpMs$$uoW^K_@_fDC-#uG=3H5?}Bd~l09A@P=x zUBrH$dzy~tm1OOw(<+#s_P##BaW5g;Fi}7w>_VWE?p*t#0RHohyyq{~bqhAzd^BHj zB57;Sj3$1I0wH$x!snG!LhUlepE|cqkX}>J|HzWVPH(-&PL11Zo;a%nioG<}IJw99 zmZSYcTLpQRS>M}iuKf>iIXKIwB5h|w#znPXS~sn8*Nb?lKmB-4v%fC+sY93Ibd6O< zteuJ!Crq0=>6-(`hPWoi8`1k8THbQ5o%(UX-!x18%4eF3-toszHH%LFeoo?X-7)3=&uWeQH*g&OvGo4Oc`x0+ME~Ob?&K%oe#W;)TWo#K zyGON`%U&&ie<}S%^cU|K=9lg=e`Z_qhNu3LNKIb2NVmuF)YAK(cs3cw7xdPhiD_<~ zd6Xq3VPlu!u{#dcJLZ4dF~7`^O)=Mo`DT)1XbN zxg^QtmiIHl>@{$BKDYn7TUjm-ic*8_nNN1)XVHPKNC#!0&UlUWD zpx7wzf#-U_Dc^;Tg2|;auf0qBO+DHpEdpLCdYni*fr&JmtC0g=Cr`L zD_SQ?Z!t`>X`Ztt;C7vN+Ct|r5sl)XPbOCfYE5K(J^6}@a?yp$`3o8(r!f^Q zvfUOgdSrI?mhN<2PM){Vo)SN z-IM)l3Ewg0+Rnmr5lMNA-WMtGf4{Mau~y*cOMi{2SK4iA&et3(7ki%labu6=F}qYH z_U{+JS4{iav+aWV#F;djYbeU{4!^pMr;??ri+S}xQn}1(q z;y(Ypx0v?*jt)(wZ#^foyz0NS-?>wEruW3DO)e}KUw@msQ}$@^jpVgF*TN>B6$*E` zJu5oKJ8#oRX3?XzQR?-_r=`Dd@!xE5bYK4qYoi(;!|mxlY28~NR=wHXzt(=%uE$%xPX6<3*`8-f(^$MR!?oYlY6IBbW3mSR(Cu0sPJkzJkJ8Cv?^ zvTbt4#=t@fnrs9-o>uJ9O=aYr76@yZBzel4Z`4 zsW!*rnnCNG`yD(EP5qx+2RQ?!uR(dj4i1M;9UGO`^*?b^QA_L=^iYU2R*X#Z?M`Fd zCYD&6{8}W5SutUXdy{z57oFMa8z z@988F|8GyK&*$-s^Wbv1B)lz4v6Is? ze8ZQZd)@l`B#s<>DyS{(8LaUhbkz=La>wDv)*Rv|6K{C67J2wMUs61EFm%!po$q(c z<8NG_sO#UssN%MD)3GIDUu36WtCgD^7UfZG3kn2rCPkSOir|BLK!@g>GG^M*>QLfQ z@a2s0d6rGAh02+y0^MarCmooO`{~{8_k58J=PjSlnXvcs)9LYio>4b8_bt&**^;7f zZgfNFv|C&3q>za_ID*{QNI!if=Q%AVMp3hS&75spIm33&&fjOb!|UJu|9|5Z+Ea9o zd8O;kJaPD5vC7m$e~)?n;>pqT?suMk2Wk9o$mFo_ylwL5PbQ?cZ!~gaFn8MIm$dhg zP}#l8=dHPX_bMLuPBLyBof+Xhp%buIq3Uqf!hJ?K=QH{tobYCRRf#QkP# zGAAgV5d1D6ek@FLY)YF0${rC*C@9SLgD@cFMV8 z#hK9-8w&q|j-1%!msIP_m-O<^-Hnq@e|FmZ>{riB*Qv>%;T2wn1OInSdjKeqgp?o^K6 zQ&Ff;xABP1y=hE$+NH$;~}S);?6~)K-!15PtnkMc456G;hd( zE|H24ZpxnKs$73vuz-b$Y2Mw9pUPSuop{G#sATE>_SRPCBK}Ej4m~G!Sd_lHGC`x| zO&{ohf@@}%z7)#_ZBIDJ^z!cRa)q>%@*|-WD@-=oO`2GEsD-ofis0sRBU?FbO+$3<*3b_7<|Ti>T=rjRGjFzKaoUdQe{b!WzG>sUi4!*!K0c=C z$oZ*S>)$4M?s<1NzPaD%4LS)%NQ}GIIQu;(D5o#u_>p!km8*VZnA~~Z4O`4MFO$2& zep8<}sHK%B=*rsY?H|lpI4-eN9xP)YEv%>ypvd`|GNoer4L*amLkKC3y0aDc2@WnWN&# z`|5dg?5jq0kD|@Xe%^a-Zo%}F5w!dNq3+I}J_lCN0To9p52pHF5@Pi9xRt$rub58S z1pPae+i#}LPFSdXd9i$FuS2Rr=IeWv@kVMsw?4RP2u_MJ<^EE%O0K>~Wo30- zg`3da2}h^+ES341w{!B;rVr(l)RubQ&1(2-9J6@c)w{dB(jL1#ysvUsvG0eI5lbNd{alpm<*HpcrYx)x6g^EmM5TZ{98EuK;?PfqLa zp8`6oV1lyIuA2wj-l)&3NIDVG#jShO!_uRy_=IA6OU|O#?`l25_++OEE0-PJadFbB zu-k99-;d+7dC0PI_f;{=jk9{ruiy8}>t5yaxegO_#3r3t4mv-@W0~`_OVi`3Jj?Fa ze&3*2^W{M6chDHZ5`jxc;~J)TUJFjrQ_OO^bff9fQSo@5)OibvOmc1*EZ2DD#E&2wiWPtxBLufmA#Qf^^$%`?U~%-Yxl2l$$$HQ-sx3~-OX3lT}|b@ZXdhgR5OFh z+3%jKZQkFGeg&zjK*a)Q8^iCGLdJ$*gnP0BYF2$DJRJGwYbY8Z&-_9!H&mzu( z4F2!?{(ifyI9YQ^cXN@(?AKSiZ6Z$II4m%!-DXO5sxyP-3*sNbH_IhduoGmABi^S_YTha$FT#SZEPk^=*PE zyNuYmt>^bnNw7V3bfpd`7wF{Qx((_>L67+0Y-eC$n(mGWND5QJbE2v3c*uu+_*nMS0`UhefD-`^RV?d-mHxH zqfz|g!a~d0N1SKe&=v@~w#cwEC$^AVemiS6`716FGcO%C(fGo@K3 z#q96LwG$d?U`m96 z@y=JhT`!a@Q~GV6UbtH}Ju*5+|1`gKRMx+5lYQzwYVI^h?v<&%m09%fYWPX<`?ca` zUj1q(XBee=ZD{M2FjP8Ho#=YoX>C@-rULdSM?M{C6n#CX_}oO$JXOTq6E3Ire^$=0 zIc;lxzoxi@UD~2xflT?G!cQ-k&u^13e>KClx=f)?z+z2Bj&0*@#o4=yb8l_QJUGei zZcYAg)=xh#y34mp*qnO0=k>bXPS)i&64@=k-zh%)ulb61j7;v)s^gtLkB|3H-+bQA z`uR;~e%lba`ad6;6z2ce^}M<&%j}HR!!;id#0EY89)Eb}t1FvRkMiyIW?Q%LR+_y` z<0ChPr=U7%;STPvpTNx@4GpGkNvB?A>^a-U9o2U~{GUL_vHlsSg}tVyPSx(X6!9_#`D71KmUHecV-fpx4bS!y5Ig^#m(aLw#t>w`;ud(1|lnzWz_4#`56hevKOx%{!gH-7UZW;Ly&yWw%+qh?%MB@O?LiGTlx9y@=aUQ!dn(3wn|x-<#@<2 zRqlMZ>-7`R0uz_kp0*xAk26Qb<7Fmg+kC%M{ON>pf6Ego0hv2Y`MX}K?fiI5+W8QF z2X_l=)AjB7_Z>1$?CUtK%xU+gL%jc3c}#nR<074TyyY$)f?pF-l^MBOxEG!NJ3)I= zi^DB8emR*nv0HS_q;*n6zI&Wfeb%$XQY3Uuk8#==j+siC*Vk^pC&eM3n&|jsft`o! z`64BGmUT<|Ra6&m-T%*J)%~L8HM_oOMgBgZT=+djJJWK{pRP_&+v8{4-cWG10+$p4 zhd5I7X6wIH-ZyxYIvNaUFY#bYX!7LJKYwsAK_L?uWog0P;iQvx1j@cd&Had#~NoVUekNkz~VQ{ zMAIhyn%W(qmF+yeJM@wq55K+e=*EQR*J|pif##(JPJD|yT|A5~ZOe_``M=Kh(840e z14+j2pYmUAD9ZIWSyLcz-unF>;~l9=;-4b_%n{t1(Ia!;Szu$=v};cOoALzWUcU`r zdP=bU#I5OZlfQ5Zf3E)eZ?EzMeWrKsX6U=rf%@1}RG8kqiOVT~C~ z&6l=hsF+3R#d<0`{6AcK!7MZ_f9;*SvX@nhKl*sqKaY(3eyDqrUEa);Kh{B)>}$NQ z)ftDWY~84`JzpzGH*`{wpp0wXU3U4?;@x5=%C)Dl3c0Dd$B0`hs-$GjF-UaU;3W9I zm9_Wp(HmzZ!<~+?uj`5EyR{{A^Fh1pkE>*j(|SOueqrwv?w`sV*^h9RyL(9g?snW$ z(Cxza>Ip;ldHer16Xau5eEoC^A`UKkEN1Tf=<&9DRj(&3os{nJr*XB)G`%~*_5Xf8 zPnfEF_Fw+~zhXLV0?u}t(&la*s@3xg^}~!iq%?p2?0cdd@u6U;OW3B6O+QU%I9$ol zR{xlp(-AA>Uex_;m-CLieSe>tPg+uaST#LgDo(1jT2_?1QK0yxVW#NorB&H7ji1^Y zCU5!l?O0d%y&F>tyv$WkhaC=_6Zq-Jmdj?2{dI2pzwf`E{50Z8uj`h4!Tz?dZFV!% zJ-x5n?sVIupPg6bXk;y`c9ktw`6}N{&q=e_OgtmMZ%XO?-pNbqZhL!a-?i82J1@kd z`*!kq)ur|E6LYuo<#ot>N%y-asaLaPk-8m^w(t=SC3mixq+LI@^z`k}yOnmSQa8t7 znS#!inNs^Mp@qUxr||IUt1ACL1IyxUl|a7q2A32XMfNS;{hHzmt_zVb@muji&Fr@k#UKYfe8 ze)<&c`_o>zmrh^uN_yIag?fFj?f$3;YzevRF!S&x8L5+<-wLIvG=0X zqlsHq+>YO}*T#Kza`408x-T7lr*Bl}CaPN9dwVlNa_zethBDI3>8UrHdJ|b9pWby4 zOTH~K)qMSXv!&}rp55FszkceM#KWqk^Y^MO^?rWH=GAvAro_O*p0mnX+z$w91cUk* zntzT)RoYCNCLg%@hv4s?ccQ6B&U{NQS`@Zs{pw5NX-5upFdu0#P6@s2uM@m;(v~k* zCmH_q-#V>FIP0F(R|v}b7xx7R@QZSUro)uPHl_!oci~h<_F`t#mWp!ewX&h zgARY4sr*1??U{JTjhqwYJr^%pQL1Bh!`|yglKI)*+I3rmm_(#4;sTtM1h#6;EIoBX zDz9qC8__>Av@`YXl(Xe4!;RSggs+M%TC-%!>b;G-laDPqx#`>0>q}47U)g1}p#EU@ zH|x`?OfT#IXuh`l>$FOyYC`noQ%|LJtQXE*GFA3l+-Btm94R0dJSu73<1tHc%MFPw z6)tCW(#!JKu%11&%Hl+&_bDMp<*beKCv3T))AjMmnK|pae%xN~c`M#n<+J?RSJ%HA ztgO1`IqBBgMwO*`KU{9*A5MKM?C*Vcxt#84wd%V|I}WTW)}N&1E~~cEIw!nqYgOn| zSy0#ZFYm5DeGaS+2Rx%#D>`rbp3qj)mGkUYP28B*8(4n4;-jA5H7&pUnyaTBIp*Zr z8FhD8DYx6DFx`n~BrmHJpR_vp<6UN?c|`Ds?KUo}Vyky7*|KK4W0|{pX6n~vr`N@) zXQsB-R<5%=GH=>*wjHn|^g3G)EL(EsysnwX5~Xu}!kf-VTsJ8W{Cs}R4lR4`_QtOp zU!*(!E(+?cnsjGnctO06?O&#qmzFO~`S)wenz|3!Cu4T0ZDh+}T+-?LDY(JC$ZT1k zUH18--clhGjD+9LyE92%b=Dj`|D%ovgxf>rl)uz1WI3hEl;^2EeZ6k@!o}CNWZa2Z z^J&^_?c_Z-Hs5%noaeYYqITJuXyIP(?x;&1Qb9ldZY@!M>i>Sy)$RK=B{y%M{_6fc zjg`8k@7KL?OWQQ}h0%s-S+^mDmH)?>VN}g3c_vxxabRB!w`+?(gp_PDSP9Oo%+Yzf{? z)@t869x$oscD#1+&m7PCUrCXLZ*nJ@<;_*e-`uKl{e+a~%*l1rw#d7$6kWS@$-FJI zRj#j{=X`0>6{R_r#nV!He!2&h-HJ=T7Ic{H9Oyt+o8NCXCnjg!(YZNYXBOLh-#Pbd zzu(>9E-~BMeY4c2?YC#=@6$YQ|9{V)uh-)*?J9k}!F;Cd=VxappSSz1^Je3*WtTr* zdbD}x*9QlipMuVBoE^D&qS;po&!>`??(hF_hRSQk}?6 zDme>y#ipD$-Vy)*<8k?gef@U7JjC>3H0D}sGhQy2cz@!G?UPIUfBH?b-V(m(+R`P< zBK5a7DGBUx)5^HLClcBM;%sr)b3l8R=0HY=1Js zBwi&q_S(#Oz8AuLruD?Tc|EHsExwcQ?zwWUw9WCJ!&hR*-n%E5iaWx;0 zI(U`c%3SVnPH+F8qM4@IVPX$2`%Y9;m>|q?#s2>v;dPsrFLV{o=2OR)7Z;t`cq9Z) zaJFzK9e3O*U-x5S!1WIg4;OFR62w;IaA5*J6PstvyPeNdZfr01i}Z6cl7>kxJFe}i{M^9o8U5mNjeoU5hrnLYO_T`<(s%8@ z-AG=zZ}0be))P*0>+Mj;@v^J>lCksszTZoHXPYgIeZTj6Tn4+*an-OWag`}$m+H)3 zwY@2HxG=Bo*GmV1?>Cb9EB}7I&Rp0ReQwj!P2UzCS4sU`H|u2ad6rq7u~R;^fZ7ya zw#9^jhtW?lGF9&Mx>>$6^7JPqshvlcSz2mZS+0C@!}Vn38@tTPoljJ+=4-5Em(g80 z`>I&vkNze6^13ewd5GnP+1e@kya^`TV+F zpf%A&KOVN9Uf3=twI|M|M94}QT4Ys35t(*C->Wi z^;y5$k-+*`(nIdPY}t*(O`p$MpT6iWFMEsaWm89z>gB)U zEZ>G5iL%WyPE2QO3*X$>$jCfzd8`e0rPbFf!KtUFXd0EjyQ6S>T6CVK`n-xoG8GRR zFRcz=U%>lX**#~DWT|7;!T10Fz5k!`;6P*JgF^Y1qWiVqd2cwdDPBF1DqVYf_sN(o z5!d#8S*5ZyQ#X{)YJtp|8+u8Llpd()2Hx*E4H*n)byV;=lM$2`wsl2x@{}V-ClU$vt>dx;DE5Cg(opdW{>5}~=+t*zDvAEQEy7wiqrI09J?$kw#DtOI%stCVC^5?n-AnpnKv~(=w;kTbYjZ7+GWrFOewsJyrE`>GY;TvQ0UhF z`v1JoHuG%lSy%nM)^k;O_$s-=4Az+k_e!R=HcZ~K$>|74cl%c zalgE<(0PM}V_1fC-qhXi_f;#XP0-e*zZ6zPyg9?2{D8xPX{tnH(*!f+oIvlH6V26w{FZpF=6@pHA9m)kMX0Xc<||oW z?&g+zOpb3qbztkuNvn=2!g8n6^&!l;*9ShhyX?HX1F8jQ+`2)Z(o& zZBOwzOZPs@XEPky6uS2KdkCK|d3!4qbi{4K-otN&uKVdEN31!(FsaS7Z(9QI9J|`8 zbK7Jhwkb@qez#*WXqUi3xk)V#uAOj<{vmg0*X_LBtVR-Ne{`r$EGu$4H-YoIPmISz z>DRJH&Ke0iPbkzrD*M@4wIDmbGWOcOrJk{MeO1lN zx4XgLu0N6L4a+v_OP@DA)ZboQ$0cI2f`@>#hfrSQv=7YQ;ws#W7y}RM@+Y;Fa)!G< zI(Tbqws?%t#wm%hO+5wtd6OdAJuW#)3h4jKl@u&AY>{xvs_0vyI;B}AX1j&%35k=Y z8p5k(mvC%%TBpc=P4kZWMCR$9Ld5}mpMu158-1J(DRg}aSFRT@Sd(Pq{I{lwF?H5f z0i!OT9idKhIz%7O+F6)%^sw&a*AW*NMH;WvD zd9}*M+~eV)+pp;FpZaCx<&$fauIBFzTqXCMWv$6EpVXCygFl2%Gk^QGghfb%(f{E5 z1^Q{iM^BwyYZ#TYCt_>FB=P=)8As2abbolF{qa=$xWMU7%Gt9mL_XEXyykiNeWRG; zw6M|~{l`*QCcEzcqZR$_o$}r1=cd2&p5}SE$7kmC6^RP$J2afKHkf?maq0M>$-#fp zR>4X|u`$FUO>ftWMG2{go?c8^tHI7K?$Uik!lqeT{z+Gnr+q)SQt88aH@4^B4={J> z{O$RU@nrSWso{tAD~4WSZ&dln7N+y)g+f}y`wb0_k0$g#-w;s9UwVm6Cv-=dlD?SB zsV1A;tyjYq)}7FL!ueXxrTg0hzD}DRZL?YhCw07;;E=N9Ub}pq#g$IiNxv0Jb`(1m zyzPimQ(P5sPoZiH>!Oa;suF9%x+6C~toL}e;o+yF)8kKm+xcSHT6vBXkH>+(>`@= z+1pzWpG59EYA@Sm3yC;Vhu^B$C%+^2eY{PHypkcy-ma2n{*$NT` z_Nf>0PW9MMkS#i8D`0E4Jz}%puKz|pvwEbI*P5i|H9j(_{;CzZ^Sj(7*W~jDj`F)N z*de%JzuHTM^E&UE_S%5kKU`f6KYxC97N31;$AZ)!wVk~UN9$c4J4~?HU$0)aI%Zc_ zpF?KEB?;yULLB|FG0i!yQ~&o=zItR)iPY`e<&C|Np8nK?DUzD0W$vVnmrvw*`f1Eg-AUdhYL zypMg6e0XsSzx}kHlabY{3a?zyne3S&{blh5jx{z9L(a^2xKm|oT$p7=!Vl}6sob7< z*SB4G`jffuz%qpgD!IWIKBt53qlpkGm=vaCFs<9EX!DAhA3k!#?97%mv(9cb*}z%sx{W)ju4t{*QyZ2ye+Ig3$Vq8}UEZEGR`2$+ zPFnkH_xkQMHq8rrXIEW5^_NdCK+tqvolj8JcdZM1>n1Btc)?-e>HWq0$tO#ush~@? zran-u76!$Glj4R)4bHCD*62<(yZYerwF_BO-XVH zmMTfTE`%j;?S7Z`@WVM^`@y^kqZUyRbSi6OcbgeEJ}C?Y-Q+Y$aY6BfB}0!9;C!^~&XXd#s~5cO z4OctwcDifr`F-7MmAC0m-Fv?Bx3GMpR_LmZ$y<)p`|B-K4p<0UrL-`9z4 z44({&o_PHHdObe%=B8B3@}j-&{c@Xb=j}fE=xBH9hX)5gowxtr@_u*eYqi^XyVsiJ z--`haCdk)*nWz)7L7}ES>G~2+;g|RK?=N|EC6n2Sxe#<25|6Z*&oupbIgYrqvrLzI zPuJUYx9qm()m5RX*VaUSy6kW7d48Vl((LQ&q&@a!TvYn`pqW4A<)x($BcnsIZ*5sA zQ*eMWap75y%U4zgKYc#Ge%dtM=u=_QxvHjFS59blTmFB)|3BZR53%ihvMDDfD1JIF zU*BS+IosIMGLnr?Mq_sFwwa(#=DwfLq-UDv&%3fVIvg}OaI52wjd}62GYy{2Y`jjk zFJHc741ZQzFrM~de;0t&y(!CyGk1^iau@MdjC}0 zhr5NxWsOo#i8KnVi`f|zSN(Qt17{1r5}VqTZ1$A=Gcybq#@=~6ZA+I)l9qq;oF$?2 z%HQgiLRQEXu*fvBI5P+_8iFPmpUz0`n+RH>Fb8z-oI+s4gtB{;>6YJaBp(KCt~oWo z{-36RUd&NN|J(Wd_sUd0nfUPM;yx=afytnYNg}2!IzG!Zn`z;#>i2sO?^T~u;Ittm zQPFrk+nSs|vegbX3guT^Wlwb2Olfu1u2K+R9(m@+>?*~~3G3wRekh(`(_9?W5kA{2 z_sIcfex9dF(&^n|x=m9BFI60qOmE2s-6^ys_qN#-!{lQ;3w>?!b4{JxZl-DjS!TqnVu5#;5 zNS|9et>n!OLw5+|te0B36|d=>2zV6>l^V_bHyPlk+C}iv@*8O}sU7-5f zn#j!p*55xI=0ELc{Z?d6%N9YW&aF9LUR*pFXZCiEP~E%choD}%C=`;*W1z` zVV-npgU+R88)ql2m>ypMH>vDzPeSwXlz@3iMlMm>vMy_Z?c^gAlhn@WK@o_N+ zy9?`d=QUo>=a$clDZg9VSP)bH_iNFsmCK(UNp56b(kZd!vcG+-pVdp1pYQAcOY0P~ z%hzlGU4d2fe((2FwcqcGcL;PR%{kKPctXJ};&bQ9Pha1!|IfSR#rpmKxLo>+ z6kM+qpSS(qGGn`_b{Nag-{0O!uPKcEo$^j>8#f7Vc8ie>+u(2aQ)NfClig3dU27R~cf8$pyU4)e>6G9m zRvxDA*~(A8Obw6YY!TMJZG7HlvhxwpWsR>S#pWFi`hI|!zvYLizeLcxcDJQ@4U@Nc z{r$D~BckpPxW?fSwjm=?(kNTpXyUAY?{>eRwzK#-+osml%6dI2uNuBccq+@-G+)@JWZXVyh)y$gmr2HuYb$nnYndt`diR2ZqskIY33gCxvCSnrn#P$$^3rn^A_%= z*Lq)4?{41v&({%jDb#}J=dD0pdQj~QTK?(6(z9U3Zn9)!(p~*8Uu57RR`s#s{N8}E9q4c(^y)7~|{F1Y5DvSJb#6P)Ao*-b}?!EA& zXJo|gN3v7e=S)~NJ+4YqK+fx%RbIdvEDOF>3uP-mH*IKF6@sx*z(>1qS<#!5?vrclG zB=YLP&TDI<+xK@GI_te!oZ}&7H1X4Xv-w9sdmLlb#-0b>#2Qi6$Q0*EGG)`fu|%btvlSW&!7<%Sow? z8b;nxI;lM|TrD=uF-7gFT~VOi6Fw)mIg;0Gbkesunj1abC!s6;-H$_V z-Q*M8l1u7RR9`C};h$OA_h|*Uq>ftO>n+o*bk){x`fi$E|I<<7OR9S2)90-_m-IQf zf)-D^)~vp#2AZzbVcO`aw`g5;Wo`PAj`K|_r#j00*UV1H`}#DjE-$amZAQ|@j=l4y z-?{ii>0;5D&?_A)cUy^VJO1-;XHUcAEiQG|db5=SJVAXa&$un-;G5MU7tC_CFsPgr zZWrOu+7Dh94iSabPmPyALjf-zyx9tFNkbDVlnMYXj}_%mL({0B&$6g`o~V0I#1cEta!^m;2?t7TsU^&Ikr19r|?f1FhiN5~zm7kw^u8-UM zWLEY%PHBVvU#~@fx>J0fHHh`1#itX>ssI0ezrUbx%0at??{|v(Yd)P+|Mcti`gYsH z3ihCBjXQO}-!__TRF`5|l6<_c=w9V>)M*KCrhezVc?{>JyW zjL+K$&vW%VA$tDphQoYC$0XBF%q_pCS^xKIxI)gA;zyn8r{b#Lin=_vd-wlQxBip* z|G(#(WL!|l*-?8VvAs1t_x)MWwV?0z|Npn)<@fskzwbA=#vNb3|DRTPTxIIHec1x{ zK}*xj?$>;l`MGZQJ1a?>iVZb?J|0ihHwP`Kb2+ftT1AQ}ohM>ao19)O^PjJ$?AF6^9W?!RGkt!oll8>o1`Bddym{Czug7oqV}Z@56Uv{y zT=s7-D+oAm|NoCNX!5F4SiQ}6>)Q_T&&Nz}u|(GWe!HESamgOtZ8wwzf^U7hnLZzU zf2!GO(8)ZSEH-LumrVFtgy!62VR^N<-)>UX*H^5Mm^dFV$m39Xk~ced+r-)V`y#j9ExVn- z*dsS#!}Yjo-6^*hyZ1YAOqBe@ns|g&NOD5=x7l-S+N+hPxtvhXeGvK2?H{Oxp1`p1 z$^9+z-Ad;ZZq71HcGD2IRLm4e{P663g{SYESvzZ+IG+e}D?FLknC zKKQ-kn8L#3cgc$>R3E8$lWD9GXb5qrQ(&CHcZaw7`#V{k*a^yf9g^(dokVu<&9wUQ zpt(6)X$jM7p=lZ?gv2a1w-nl^uul4Vh+Dr!c#@MOX#IGBW=n+w$NwLX`7OU(aCYty zHB4~x*-?LELt^6-2a&f5-?+ECdkAhi+C8<|#_?E%+;``9j~ATFySwY8r}|uxJ9;}o zn;#B_e*XD<{`By;O4W!%3M~TKYf2(EgYL~yFfPose!r*qK*M`Y^CKTJcD6~XaOW)Z znW8DfCY;KGlA}p-!P` z%H{R(`xAb4mj|$i*>@<4neoN&SI+yiQd(Q_=>@l2Nk*SC4NGj=V~M`F(Kun_hllE>Zo5B3^- ztK_(?*e)ZKqT#L^;+zwr=RcUcupMHY_*m-c!+#!@0!u(Uh+-Z`o{}+0aM%!| zD6YcF#46Y%;q)SjUpXO1y1^!PR|)4$$&J$zd7HmDEZA@@kUd4@wai-=xs;{30xpvp z6|}GE+yO1u^NA5Ij6LGjaa!Yh!CTNWWS7;MmzOP+>5!IG?EJkr_wVoT{MR*adq{mO zig|x?bNY_?Mjh6VYZa6$E~XXSOzyW`61%(XVA4rP+n=f3Iu79IO5+U!54M)$9&9RYzjv}9 zN{kfMHCCCX>A{iGF++26USelchC_=&YQ&)mu}5j_D*V?S-l_gO`{?n6$<-StBr+a8 z#gcf)D$Ousf@ex^Z|LH>%{3owp9m>&crLEpGT+sjPwd?R2FuQ=%@u+2*K9l_-?r^& zl2z&1rRWoJdqQi3Ww(8EN6>s(fv|Z?-$rOB^Pggh=8%#|uxwf4YW`!H?t{LVE2-}m ztKJ2j*xTHYpB4-0UU)>X%vqxPW_95~(4fK;fev53Nzs=k?3vVgsx zjrVjwo%shUYi;)GPQMBo#(*?&y`&gDW~uXOW=`1)KmP%=Ulp9f3Rt)r8zygYYIwRG zxrYX8_+A2ap)*@XHI4=jQj(!OofVWSf2XnVxzZ->CPc$Ba_ zJ3n84bJ|&SS>=x5q616o;ddl>fGc~!zbSB)~oPK&&>FbvI@bz)FQ$qLG)p~k)O?rBI z`sp(>jazp~n`8*AyScIIYt~GQ!lVh4(=9EtuC39OwJw{Y6}qbB_s)GeH#Y_N-Q88X zH1qN@M?LEq&&Bm(G(e4)XJ==#S5EA#cR#&U=hV~cTkdy3%LJpgW_jM;mb*0ZaNDFg zb96pGJIh^kIqlYAN6QDNZZ7o}_w@Ai{F3+JlO>b1g21uKlTF@>f;#KdK$*__%icUt z$>+_YpcG&Gb!yFzhwY6gBu+XkJ8OPF=G)D5`JX?Z&p&J{AasI#{_c0XR=?SBn6I(q zZt3+{%aqRd3Z=4EB@>$Y?INVi^X6Q(nfLeE?EEK-`|Y?``X^Vf{0>H`HGg{E{{Ixvj-hRLio7dduifrst=KJ~b!wR|Xjh{`(G=6VKR*KWje_NF zfEEVY{eELCX`I$01FED8iVDtOkFS?4*!{3g`jFCsiKjNz+>hI?91y^wkooRiM8K9m zcJ4^2u1k_p$1!3O8;^tnyG((@xtpNvDu)y_BKj3#L5l}}{BGvA_ z8Xnp5L4rm3okI7Ofs2o^g>6^$o_1nI;NmBHzu%jz8NBRCh3Aac*KfDq zpSJ(sFYPPL3m?fT&rfjJd7@qP-S+!+!td@^KA+3Hve}_YJZ188#r_H2PaYd&%T2R< zwIH_6g6XLsXc_d!wL8y1J9waoeC(W-VRN#OvGD-`l`T zb->kyL!>gp&&E9oKNb79y~XDX@Sc!PVe1xo*PtD?hQlz-UB0$NAxl7B+5Z~fzuwX= z|9$`JXH3=0rHc1n-^yOk zd*Al|pU<1p&dz#}bb@haGsi=wpU0&0PcZY_2&@SOEzBrjKjP(PGkvGQxtYf9c{`s@ z3t&&39Mk+`T}6wY+=eOjuU0N+iv-=MB=DSVVUt6l(}WG56d!-Pov-gbU5{69rhs0D z;=8HUZ#KIBkh|Ht;^CsohppmmMgCSVRVx2{Jbu_V;_td$ue5$X?zexkX7jm8pk-Qh ze?A_+P}iYwezMKeSF6{bJNO=SO_<&sP->C{4PVZ?KkxKAjt~YGn?euPiq3oB?GHjs z0*k}rYfX=YPpo0qmSn%~dE%h@6E<(5o3b01)&6?9T+#4E<@34XIt2nkG5gjQ)^XtnK%KbcI+w}MSP_prU=8|+_LxAIzA1xO^Q(TIi?1hOJnVVQEn^YcF zWfo0(1D*jqBJ9s{h=nox{=Qld2PfP6wcleEI=0`dS`8W(KUgc+bYefh{htp7_DA@G zx>^OyJ#Xyr zfsM(>pQTjWJnk`mvhVl1$<^<6sz=OK$UIWv7^C2=BCq(8O{K5MGroVdLaA)ojl{-3 zKC{hqpSL=coQXBM_IKj*3Fn_Yxqqa?(ach@_sAs2broWp+z$zMh_36q!k*c4tUu7u zC2R9pGw!EO24|-h{`m0lU=UlP^5Z8@r^m~kIua_pTBiNs@y}&$#$Hwa#Vn_kL3_UB z?$0{?22=?`RslSfW8C~MK~Q-n+s5dKxfUDeInHC_l@j6Lyq}z_6v?*w&BeXP72*Ws z{feWX2!C>VBd+;EG2=>lm}ZPf=VK|$qLdxS*ZA(x3Nrs%$Uk4@--E~w@oVPmX3ja` z5_{!fXR5B0x_gpf(9_9W@|!%(PTXiNTG{+-P0WS^cPCctseZrLTp&0`y)&Cx{}vaU zTKJSNfpTvPUPtbfSDLzuQS;T~8t0{Mpj`qMOsQQBsT#(ti+ox?o&t};Xt=T{7z-Hc z_zI|9x}R+M=*ZswEr0(1`7xA&aL14Y?P8#AxC zYSo>PJTDYn_MnlyZB1v)bE)|eDIC z?A)pFC=Ij&oh{t6L*mHfy*D@1C<=d?aG_-pEszH@l+G6y+m!dYot!?P*a+F>9Gpn6R_yM1g;uO7HuPvp({C4PZ~*Xg)LY zfr81A)|oPQylY~*#dMwKM68)}rMo)mN|T#yj5@RG`3F^=%e=l7KCV0+uqEXY6hzKrD* z8{@C_>U^5gZ}x(grJry+AlzON4w~3H)#qSSlv=&OHTlq)-V1krB>m^@`xx}X{eA!0 zM-pp~zw6$6JcjdeYQvWFr0;6o+caO5Wqi8%cW16>LhYd^n|CF@-dvRwE3I3c@fZI0zfZF>C3^FOPB1#%zC&-R@&k?#7nV6oLXT{Jl?&QOw#h+Z)+M{B`YB7ME{Rzh z%Aece{>N)mZrnEIfLR?<(OhT#FYVg7yK^7&@s+nfkWx@liD+nPkuY?! zIVhwv!QRuu$;N?4UEt2$yAK~&lz-p5b>{!?tLNT&J^S|FskirTy?(*m`f_aO`hD-# z)y3Pc^zGcX9lXpNl2`g1xRnB~J6-v@)6s0Y$Lv+IPkM86Gd{k3_GHga6O&87vY%r|N zY2 zp1#YhfT5Z9>-Md>nG+XmRB^}_I=Zb!en-yP=7#k0966c2V(te7Gn7F7>^x9u2;zL_ znDC{Ebx!`Sm&-QnFn(P#x8#y1IBS)jjxhOkzW!fvgmUlJ3w|~qk8DtjoV4Mn$TyuI zf35`k-}JBlb$P=vNtPzp`Rn(7i_+HBHNCdv$@a5mx6kbRaa8}-hQz~J9&Du>Jif-3 z-xWOt+9PiIdYS8l@O3en;jyKm5#>FsizaJ+oS1%TiD%hq-R(Dazu%X==6BhfjmK{} zvdfD6Yr6oNKTcH4N=b0c{$*M5;lV6b<{y7wE}y^6`rQuYFVpMl-e$*hKQG96d1>jw zKaY-fzkRW|f89^pQ|>eG|9w}UsJpd;|Mil<<0}FeA3Aic{C@3r-S~YrU-DnC-5!^} zvni$Q)YaADw?Ch^mlwNqukQC-=lY1xy;U!`yHhuJd@tYkdF}?e+3E(F-}{qizXF9| z@bts2(8Gy9dFWgVgV|S-#i=3DXl60o~YY%wy+)g_NZH*?X=Y2ig!Do zGcEjikiSlVh~yIiKSd*7W0xyLF& z^DB2N9`|m0+-H63UiEufvBzz1E={+q{dMDf{lCu}W_%M3kGVKAFY^6sHJ1Z-Wz7|D z+?#@2;R-Uw{N8rAEZX{Ei*U~Fx7#+?e!puj&>6Wo?Pe1zcT*|Xp~IHD-|foYz*P9= zX8QcCzu)aXKTRi6=md`nOSjDPPt_%}pGaJjUsDjV{>Day(zmxvBbq@=W)!%VwC}I} z{w|l9jb}r6T;))`r6mg_cwu#`iNMn5cH+Lh~NIt2HQ_3lo!T%ygJA(pEEmuU*!w^Cp_F& zI|P;AO!C&-s9*o{^uubE2?7Bgde1MW_gw_dqRW-tdKA+k+tT&P`t6qB11j}_xxYj% z|MH)2XFKKR5p_GkGM&QK*4AV11#TSXvp%!!MpAde#3NCh-}YHDo$YmyDxUSeukk)} z9sibVwgku+v{n0&mewUo=KV=8wN)6eQ>Gt-$OTNE5` zWHk4F-k1>h|Hj7TW}b-}f#xoplbT97ue+FWh=&<_@TBl?-~RFO@nOSLNppWa)vuTA z;QO1f{2gfU{luSp)$c*aKcxJAzrWr>{*Yz-Mw`Qj@_F+$#yIJZMAWd+31z@ zviVM2#lzNbd*9cdpI7myGeNXFg0g6KE*_tz@~ z9$6H~XT%&>_2=X9Yz~eiJrTNh1UkbHE3uvYam;+5;GG{gEZCEpS1WGJ+Tkh0f4uVh z{rY-^$cSY#Lb{*u8+E=`dY*jr&?OP`4w2LiHyqQty1TDCHhUhlaBL6ll`#HwrtS3O zpU>y#2QT;AI=B2@rEJhNeof(;_#cPFo$hDKKVto9{d`XG8+ZF(fgO&`M=qLgXf$yW zE4;hp{KZ*qu9NEQJ{(~Fc1$||%8uDiYUgb}pKOX8-&WdBTdcPxBI0;~q6=TrNd@(b ztis-+Eb37UCkQu9J>z%M@Z9l3Y>|&8!glXSJi3_gR;OX6fX=@8Ww$aFZ*o3)`+fia zzih@DYO!K3xpU`MzuQ^%^=kO-_4R+Ve?L*Tmz>pdCTZ#6R-tZ%TqhUhhf8vwDBk1K zmin6XRJo2U`eOao{YWe@y_5E%C97Q^-KZE0X&Jt7eMRD_yOA1~GhD}a3 zVl5n>T4yWg^$KuIsXrl-S0*4+*Bs+8LxFqBQ{f=@Ig&0MpB(;(J)h*FT;E)CM5jNZ zA!%=l;gPH(`6nE2AKB?U(?~VqnSwz??~mT9jdvV*HaaLO=RS(!IQg*rmEucwG4)f< zG8=7{8|-In8*3%5D|C z#S>E!BM-Y8M*T}VBOT@#qo|lRLGM<>hDO7$&Mq(6BPN@)v?!*WQkf?3dRBg)L$Pvz z!v8zmvv&urKMxvbZ9JwFaJzfQ#?l`@!Kr(TI*Wo&#HtR)!;9R6xO74z8Up?(X6$e( z;xV_0;r@OzU0zvO=?G_2lW330=Ly_*{%trF$hV2vu}L7Y^6=As6}cn(K3d=8-q_?Q z;Mw8m=DNmhhp7{b=PSd+LoC)$rv$&b;LOi!t=FOQ?7%D+8!1T{&sB_?i{jQ4sE9O9 z2vq-A7V$|TJ*BtB!|97c>yZU-w_cA^Y~$O+##!~{qWj_d0$P_oCZAQB>7|nJh0*2! zgXF}<8>YV)KQ?`sbcMM|?6kugi}zQ<<9U||^;Ca-CHl)t_T!!>)*bsN%D-0m%Vh4M zQ_JXWaQu}=?DF|_RXgxt;~%NdzY!6nfu>rXG@>Ma)$$+QOkFnx##JAK+r>h zq0!;s@m;3g*9NSGLAmK&?Y0QvRl* z;}>Sj0_~O?3gXWLrkN>Dn_xeUb%AV$_;Y3E6#hwEhKu%{uX$#CQ0SQ0 zI>}12ea(qIhxx3pBsf|YbWAfmp*?MzGoPhO#KBixs*8O)bapsftrQMo-}zX|@!Wx_ zb{E{$GyX-Gn={>+t!X>w=CgG-UW4ysSg?`f1Czhrg_1v2u|E0GgdH19 z?-wp~S(jp;baO_aV3FAC&eN<4++UT&t)KK&CC@HB>0EiPJ5v4iF7^XDr`QTt_eWfm zE8To~dPixI(?-2Fw_ex2DB3Q|a>(|T%G$Rv0@IHOzBt2S;raSZ`2F9|BO*cLHG<$- zfsenz3mzde1+cQI(G0Zs+^obfMg&|}!Neh|?%cij9>mHJ=BRiy!K_d+h6AB0BbCLe zXX)`JTR(+(|dXF>mG}lIoD1Z-!u^V`HaJY zDOa#@&DU#>Hb#GaE!?uZ;uMp0{->sG8Y@me;jmzu%h34dV05%r>HpWV4*4CY{LPk2 z9MxO8L?m?bmaM5KJEPQhtl!)HmZ9-pYi4xOhq7;%{#}7d%#+aPo#?^Y7cv^Q|sz=av zg1d?W9-0D3BD}7JjGI)5cH1$ZE{Pv9d>zAebU6N{BKL064P7;ezXqiWUQgy-o z+&N2flV0q#bUz>%0op=S7Y>Su)d~U5m!?g>^-kY6Q&iF{b8+6;`%4O{llvSN3pOk% z)!)+o?cOQRsKp0AgO&?kxWnOa$%eJQc=4;R;1gme^gA3c*ZQ9Qj$=zci&ISI?{`h! z?>Q`(WVspDGi6^_Rpo0XD>rLvcNRn7)~$Tyi1*Qk6!i=$<$ z*VgrueIw&%CD#UBjg5)SyzMtX$^7rD=cadmUw(M}#(X~OZH{+szb#FF^V~cxy!c)D zIg8VBcM{E4Yv^pr(KWL2%obQvH%a*chY6@FI!ihbTy>doF{)=uv(6VUV~LV&bhu=6 z)n#p(Z;;2WW3FYqQQMAGODnr|pkC;Bl(;=fauW z{`?EdT~U{OYr>k>owL`hU$@U>Nx!H0&a^`dTvweutgvn4#T(l?Ht5D@ezJQ!^U`;x z)te5wvW89ZyZz>qm-hK*cVx5krncR0Ui|$0*1V^wWl!z@CA{>x)@Sj)tL^*BZRrX& zOs}`szkOey`1skGP5Cu1Z*N<*q}x}@N@>gE*Go)g^6XJo&bTQ}DBgdGr+%W?nQb|9 zUgoEm&i?hK?e(VD-fFC-OZP1^o@$h{;=Jj#UbQl%Q%q&AABdj}t((;GW+y!e0b*80Q@ z{oO^U7j6sR?(|yd-Sj`NW9sA8&df=(-}9Jl_n-LsZIjR2tuJKJ4X<6OR#$bZLp}W% zuUW-0^SYGZv7fB1zfY^bc|LE~xw08XE1&)8-OPCHzP~;ErU!60w$-8ck_p&PFPi%Qf5I-d`WRqyF`6yU%~;-!wn>XIkyUjlXlwcfN1AEFG|BUzYIk zotefH@0u@)>7Ku6wZC zq2KjAZa!!Gx?z64*@3bbaVj@AhfP_#Mef$_1q;?Me-`>WP}!x={^tJn+OhzB>0k{b zv0us)m#hwYH<`KmRyy0a4HGT(b{_1We0gS9Nf3Wx_@#iHSx>vd%O})1+vwd|vwyDP zlJ?T`6W^v~E}9#9$(`rf>}0lEm9zP`$?g`5YSamj$UXd2)UW#6>&*Ld%8Qo$UV3Yh z*vZnnvUi^@?kqcV`uxqWzh#r;`Bs-%=bXCczv*w%?1^P3#hl-=C$rz2ap2Oc`o3vj zUZhT!FW)5l^%eKRn`ug!*_|Hdj3Pe$yaLNKW$&*rDxBj{7xd7rPo8($%7vQ)7ao#X z#+&f>_160{%98%}YF>W8_q!8Tr#qy(DkM7>vAo^#@bBBVs@iWBPhHAun| z^t6@nIhIH2)Lg$?FJ|{xD3c-HE1&Z`-|Rx)6x9Xc7ldAi>nXbIFqbo2&_35{&HB_+ zr{1oMFVo#e&#!auU^}?QMGl`tP_*MW=y*^O)ye>ZFlXDU1vAXX2tTay?+HgUu!lMZ=3Q*zE3hCI&oU2gZ)v_Tx-q0_oZ&0=X-hm zFqffE&MMsrM|yZCs22X77n`$LPf6_{>lViA>A@#=aNdrLj7;S}6PI%P^}HVo-(A03 z61%BOHd@sqt^P}!?w)`8+s>agzjgEby}bK7zwiInTAm)g{nV~rv)z7G_xUP6i|)?8 zzSpT~LhsEDOTHe^TI;ndiF2J{*OmC!Z|a!e%xdoEQ8!ceJbmNDqA3w?8(z&^R2X9>Gb6%Csfz1^o(G(0qd>$U5<3eVKm z+?#7p{1Oqr`9Do}!~GTOmQP=^s?U3}>qZBab={|?WcUZ`9eEM@_}KR?r^0tfehOBr zQD4XYIz3)jxsI@#*-q1os4ti<=4c1V7ERrjIQ zYNsCSt=qnL9=r8rX-^e-BENb1E%{~Vw&#|{?l&*%$h~|y=ho`}=$E4VH|br!liPZE znub~K#M-YjtKZz2{XcR3{7qipAO7NfZ=UHWBSZqGv_y1QbuTJpk%$@A+=I1hIcJ0^ye_pu#>vr>L zYd38Vf1Q?nmd9-IT`i|3!M*W4bNt+gRAZ-Y*LKPhVYcyY0BG`B&NI{JvND&Zgd9?{%4OYs;}X+v#{Y-$4JS zqoBgShsS4Km>CNR{_BYO6K%w|>^b+d zH$(qaobkq~{UVa>WUP1N!9t;oe}w=|X6Z#93OeoKG3-WL8P z_R)-nnLFES=cex$Om>@`;w8d0^Mgv;m&LKEp4KT2`qhb%D`USrl&Rjj<3E3+ZK6EaoGP>L#V8)WO8!K)LX8ph7M;}uMjfS-jbZ+YMoaQFMV<8 zjcMU&Yxavg@6^5?F_Yu!HLdcdJ6ioys~_&Ww4W`TscL1(deKXJf3146{At?G^J zVcD!X4VI-5@(wM+f7caHB`xt@9H>bG~7xBE`@`K}wY^w*^#?YD{Z zFW5Oe_no{?Lu*DwWbPZ@%WkJ%OHF?nRj={7Wa+!5hg;6KOufAJzWw?whLgS4yfKd7 z+H}75klef7cXrwQUBx@|Porr5$HLvYA>Rw`PLf{v;rF_zO!N1}37bp8zT8$@7nQtz zoyO+tOS{+eeOC!J|6{p7Ana}0h9#-FQ*;dMZ{%!{wcoP-%x&&BGmmPoV$I|h-XhiF zmd#-A%YQrFWW}4St5?5OJyg=c)@wON zds`&s&MxDbc!$R&rG4eR%9ZKC@zty4qgR}9;I(5d49jFbDzT*c)m%sG=I+Z^mlfZa zX*BC%*u$h>qVLvHYs&tsv>WDZKBOzK@;p*XKyF zk#KE+{LZ@?4i)LF2I1NE(rf~fyW6XbuQ{vqZ_C;iVCEB<%@*-oW2wR0$ATARqNC2+ zY<_${pyy27UhgFjek}1Re(m4Ee+gVF$eV3HV&n-T(;NG{% zXWMT~H(}!ye)vND`_akmU$*M^%PqOF`PIku-6b=VUD_^O*4TgT(~O`^{ZroRFzqZ| zpVzp6_jObf`%2vtaW}GT`d6_htvh$+YIcs-ODXGs7T(j+C!Mm~u2?+t`Y)+G>2vQz z=C|&%`M37-zfE`NeXGxWB;jkZ#|)eM$qxCqclDoGeX8fpt)t&$ZX7oYp5HHW^4Fqu z-!)D)>?>R1aj(3Dzb3E3KJt-C+v)?H&cAM)R9f|->~83FPNm)AT7DllD}7>L@qfOw z<$*fIn*Pb_ykwbMPG??PU9YpI&Z=(4T+Ny?!HKUI*_{s8`obGj?s0$8gLS_==SHu5 z9kTP}yT!lWZ<%Jh_}AU7E=zvc7f-u2U%fUV*}meK-M>>=v(3}|cjvXAecLrv`|YZ! z%5QcbuRZGb|5^XrOSx5%srP7ep2{ILQ!oU zXV^ctNn4g!#`AW0^ju;7YI}E&`|GrQmEV$f7abFt9XN4@>Ma#L_tJ;6c6X{W_rF_m ziOc%)*8Vp=+n3!Izdt9fezw_twkaaFpPO9BS$uPv$g;1ejApJlt+RG-dT8a>1DrQG zkDpq*yKgi5WTR)l=A4w9Y;&AJ1xC-&!&anaEd&^ zcDi)q_JViX-;cfTT=PaHsie1L`Fij4A7__DUW;Gy#A4zWp|5}QC#T&u?%I8LQRUw) zm1Pg3FX!}~aT8ckcz5@$znj8pfA5|7?exjdIWv>4^@&B^j!IU`yz;x_$QR+C8Ygy| z83`U^DUFXjvFnLWy(8s)R{VwnQ)RedPi>KX`W@Uc!a&7s>-(~kM$Gr*9nQ5~%;l!k)*RsE6 z{Z9CCVwvstty7XNJ+e7^a<1B;zoL;L+p`{hzUwh*nlhJVW7}zw>OQ$!$zK@mEqbu` z|Lkoi($Ydw7rt93Rs8Q(d)?$+7g(<^D3v={ki1_l_}A*y+HZG;ik7|pBl>O9nIF+# zUPz_+mjxV~f$SJ}oovn)B&5-}YtwE;;)wNAGi;<2w787uwsTmKpK9OKy_6 z?bUbV>RQu|IZGnf)v|rN-1TtGDhq#Z8jG?!EZ8>COKenOka8L*jLp*j=_| zVEF&V)5S65M|khWX;M0V7tUP#XCzvFwV@;X+0~%B{aQTxEH`cc8=d-Z>9&0fAKXqn z6Y?x2al&o~#$)C$ZrE?F_nf)#N8P`cL^G$&$&iWO&JIDBjCkACk5i_;TVK6rKilCQ z<=2ZtWyRxWuTD;yw9@Wi$i8;1L!njqnsv!nuQ6=hJo8Rm-_^~fE7R;b-QJ6Nh(BMp zcK*D|B&X)9yO;gEk+#Wk)1=g<-CrAPciq?+#AT;8{c+Qttu4ngYJN<)WwoSS{PzqS zhJwVtkL<6z;^X&ETGw~=oz2}P0$;CNT#40_xH|3lwJSNVcT3dW5nLhLXgW(^rW*H2 zDa}{iPlC+nE|FPly^Z9nrtj3+ zKlzJJ$$Izax|^z>>2DXGKK&N&CbqpBGG_L$RlT3U!}{my{*$}b|6kepUE0OyzLJWn zx!{hSzX~t51a2$s7up?l?biP@aYA=2vYQt??o+&4rSop#OVg!h19{r>mI?Qc)~_obfy_hok3@4fkv^*iU~b?GjyUg#3`BFE7^GIww+~l(wdv=%x8Wv zpKBx_f2mad+M0$_uN=>1#D_*^F5R^-GJR2i*R_JXAC7O>c4)>y#e_2bpMUykFO@7j1Xle0Iz5cD&x^FW02zOgmLSLvot2iMCOkVfqeMgVi4+1vlA-h<#rq z`0Fpn%;(=P>WJ*S*Zg$V*WYj5PEWB{sCnNN8D0OsraN)la=Exkm2cEORm|~uk-S79 zsrPnN=>J&b_Ywcvx_9(m`?&t(pIXDr+NpI@*sJ!idHve3x$hzK-qu^Tj+;Zp`=s;I zSHv&=@_XN;$G@c(*y##ItU1WR`X%G^zW2|>80lqK-h^i@%IMTNk*KtA(V<;a*Kgc6HG56A>&i7JEl-)T?{Z&r7dZ)%)z`^+ z8|yT~lO*Do2(?LEebt$CZFZ80hA5-l)wSLQrpJ}a&L7{lc{=~?qwj8{OIwE+X4txJ z`{h}mw8v+X?JAApK>Owa53C6?W@(9A6r_F8+kc^{($h z-FKI44VMY=ZG9WEZi~OD-ZtmyFZ<^H+a!9wW-ssi;wifOALW-Fyg7d(KlkaJ@Bd@d zw%b^Ec4kM_1r_Z(QyM?%_`RoIVkQ6Di{IA9oqu@CBrluA^K@v>&(O^`XMDC_-OhLP zQOCpPH`8jjZB6I9ad6)4{OIa1>-p#FbEK=^CI8tw_wJV07PtM)gHJ?V_L6Q~;jH>Q zK$TlkcT&j?xjB2EbQwf%R!}iGk)@z@>ZfM>r-wP#9$YtTi>IG>ziWT4cF-63vvRWE z&g?Gx!Y%yPERs(#ZT=KZr|2To<`aGb;I4wc*2h? zUa0eKal`Fck;UI8oq0OH>LauD)2}PPO_$_evpy|gTdG>Sj)T?oMxjrkDy(m;jppX* z2mP4-^7ESm>i0J#&#(4>_LgsyR8rdvM!GUfUG&Cc^{Zr9lrPFus0nQE%F@!+oW zeV2MJ)hVUtpFI2E^7(z~?DobN_x(Tj#bDoNiL~028qqJsKBV2YW!mE-YQC^e=W2kF z|Hb{Ejabd}PtV)-R3~%g^Nriz9em&WGN1qb-levC&$H}K@7H}JQ>$*t_x-oJ$;}V% z@0w4aeS+cl>oVoHA8WU5I{y9csdMty4jY+mI4&mXtXrO*x$@qW&!PW$wthZwDQNcU znb9JX^LiFox;A}RN@mornKys$m!5cC?_bH){u_>ezkTZ8k}{5Y{(kXmE~#9sIqI3S zc4I=dZ;1FQJI^Gc#kbN<>vr5<9j`55&Hb7sRx9zvxbbi{mDd&FPfA{$9YcD>Ni1W6e7EMrm{cKjtFZ)V<8?8BV*DIg*OGnII zbnAJq&Eu@AG-g?I=V76#`o-U*Tq_=_Qfh+KU|*jFw%ebX~$2wzAyeeOx|Za)#Ksq^|cxC z)nAz-SBbTkyyP)k*>Wi0ZKuNx*SwD}zkYdHs{2221y|UPl-FCeAO86@QAhK?y7^hJ zx&IGyDla{pHHCL_*oSqm&Oe>?pt|m#(C*;ByR(1A?2dEmxWD>L?WRMa=Qloedpw7A zg8cpP`x{ogRoMGp;^^zdz&*Jeh1@k1Pd9cYTZc@3-hK63_OEp-H%g`RcjRtRc=jt= zBJIhBKcT0C=Nhc5dK3R!|NX32$@%LwwhDcHs2<(VH~Ih7=&vd<={Ku5yNyL(eQ4HY z3k~LRYWrq$?w;LO5$WBx4Xj@;nEd8?{O=auNl6?2=5D{4srY0?s`Ni~ zBvrbeW8L~O*Q{yD(d}hk#>|n`vDxuHmwY4j4Af4^)U*q=-7#_Aw@ZoFJp1`)wV6Be zuhm^X^Rw*!oTEz4XXLk>yFI_}okH>7zwB=l_&Y7^cHB2ByJ)s}t;@0(|4;1yVajWe z_$;RRvbA>Dt^KKCb^d8@xCB@IecELb-F4+|mBVw+pP5_&$}7s&`TUj_c6_CK^f}|i zok6z3?I%CAPTD*$7QV!`S92G_;ioNIhw)0YbPbIyra5J@S)Vw2RA$NGT4JF z&$T|-D!TfO^I19FYiI7I?7a|{n3}N6hrit8(=yv#Vj;g<(%;yA<+p!vR4DD$tW^)z zU5r($PO95>u}4U4<1=K8FNRJ zx@%0nrSS8w_(ZPASw`=6i4|DPSjzGC?~X}#-5YEu>e{YpEW z(-h=;{?g|g?#46kPhM?Mn6P85%VvqqvsgCs2TLbEIa^C zlYqv3hf2 zD?Wv?AAA`7VrHFZ*C8LZ2}SyG^Rjh*PtR&RBP^P+FXp`TS9z7yx1TCS?>IR@=vPe4 zHgjpAv;*-MT)gB3PswY2_``EF$w0bqPEV(Mzf4NB$I2OstkZ8y&OdEbKa1(^YT?CO z?zPOi|G&?{SwNxmk2u@+4eH+m_FVs_d?@MYT1&y0zca#&Cx zl+W5A8Q;LVr|RI{1>8^1t^Jwfu{`>LjM};xB5G=@cOScO*5sV*YKb*xm&~`>Rk8G} z(8M`=cEuymvzKydqNWT#SOOta~em~i_>)!d7d!6T8sqIpfRz6hy;z6C6 z_v63qmsVa7P0x(czr45Momq#5;>mmdwfB4{=Pq6KzIJD*QnqR18sB`sn~x`byfWQ> z3;WXQw|AFUpHR5xYsqBQYu22RPz3-FHeE(XkW}f7oiMwx2Vea3$f9doa z-9_@YAyq%m^WDDv?E5>-bD#FeetEa+p3>@6OWo_g_n-HgBxiO$Y{~E6U-ljS_WdLC zn<!(=*G6Z>#HQc)yX#^7s`hVhPIkY2 zqj~lBc6I%Y`Sn|F>|6YIlH}>PZ%wO&47YB%)Rz4?<>&(6Nkw00$t{q6uCwvIX2;il zWAWQeXHyIlE*$r&4HNbFtv%(?;`m?XFSSxNQn`1mYyA~_TrA^&;!bz@om!$R{nxL) z$L|ug{Zh{lzoG~CoV~7<*tY0%%=bzZliy-sK1FX!`qR~a#RVNN-TLQ~_VxC+841&K zY%i5v@9|!E>Buq3pAOSi_}8(ldA`x-xxUJ)jS;V60>#gM5NBcC&_3nZJ0<_ZnpNit z#TWZN;Xlo@KZSqylcyD5p9Frb++);~?U?83VYgaCx!A_AZDzo-sueF5)mN=p>-E>Z+tjdKAMWKGcU15ZIHjcL^f6X&)&I8^r{3KtOu6wt|Br&EI7@E$ zjmG9*FY3rah3n^nTN+ zBUjWX?AXzJh5K9esY|8JN!Izn6aMck`kcDy^Lnp0n|Z!ZDvetZx%RlS_vSTUqP-68 zbQ9K(ulUJq{dKA5w*_(6tg~w@B>I~SDi6Kca_Ng#;!L&f-+f)*70jlYc7D6QCR_e& z(tX?XzztGt+H>uyJTfhPR3?i5l%4hC*Q3?)H(%SCD`BwhZ_ zJC*VOx!=Wa)DJyW^_57R`F`<9(OYfp4;eqWMt=04bo)i<ANW`7G>x5?oafKNJ|&5S8}K9Zu~lFUJ49O-Mm_@Q-9J^w%4Vm zof*v2`sTblU$JfGAvwQLv0dx=U#q_URbUl0q^pvM;tc>}X?!5Fu z+=_tye(h`jgr&6WmwlYTX|PA++H;|s3(U_5eSFz@uerUpZRoa{`|8}a)hF{NT25N!D{%3kao3DZw+doU-(I!; zw3OhB`x=#;OSE>LoxJq*%+zd=;29HTn%|V1Y}#}G!jpUV%O#h$ZZXweP=16f=9TKr zib^wCg-G_*?{%a9+Fi_et;hfW5VRJ%%ed=x(&P2LyM^xL?mTtRINvj%!EWur z-0YneK35n%>^l)rpmi=c@tB*$)Q$&tl(_Gi-~Vx9U4YyC^rbp)7tH+odAr2AuXIrb%{(5qHDAMSZ`l8BdzR%QPy79+x5mw$Bkdq0EtV5M zdGn0KG`>6`DV<0&1;1p|aH%iXcE#_xYu4qkW2Wi97kcx~FX`quC$jg>@(ibxZE-{A!psZrkC!d7WUk@WVqjo7N<`YuYtQ-u`@lMoPH$Z>duOTQ5!8YQD7P zZ1DfnEjO!!ByLo(3W+aUQCrM%%YWKWS?$fVwG(f8jsaX3#s z7q1(BNqfrNUxB@$yH5svS^d^*bN66f*Pgrd z{ri;I-x?ElX{Ua)kJTMV>3{y~?i|ZtHC)4q9YE&i+yd7tSu(LO$J-f7P-Yd4DNubysN))!oT zf0Fd`ZC-a6GpmA6+TXjd=)w9IH)bE|E%^~_de2TX`&_jWmwwiw4Kb6y1Kvq_w6p#mTcA8@;v+Di|N6!IhP&;hHXrm*c-8= zbK;|SMk;GJZl1dI^Q-CiwkpNGX1aaGPG-}-)S|Hcj786VZ?9neJj>Oi<3~xXdD4@U zQ>3oR=QXK6bkj}aZB$(Q*XgcHb=O)sr6R*%bGe(@FW&50ebCH%zjU0Zg7_{^Qw9O{ zPnvgkT>iS@kMEHT--RL?XR}%yZnm60SaG&MFneo=`S-KM)3uYQ_`FbZu%5Kio&T|q z?Fso)QP-SLoj082dMn;o!#{J|6Gz>OonGQeThjvkt~qa8%9JzHb9dymqsvWSICQDK zP)uWd>nf!qH>vy4yHhvHZe55g3O#j0_tcBeYtCG&O?6aW$^N?X0aGc%>q3op-8*)e zhn)X6tMyjUsr$R7SeAM71#OqplH{A}ebsx?<9k0(Pgxl>^Fhe*-&KjWOC{&zE;%B* zNJU|N)`sc;$(9M(bJBkFPy2JZbgtY*2cKgLnN5oQ@7z+Jlv}J>mGY*>{rLN92RQvw zX0Xiud18OY)Jd`;nG3=WtV(*?;e9wZ(K>bJpEYSk9);EIw+@+JF?U~J#xiTy4Abs< zqr=>cKbMMh%&#@MpBKI1Qr_(CG4C{2=XSnx*12w6vj6e9-D~H~SN`(mz53FG32W}{ zU=wOS_2!mcmEXN9r@zg7I?M9u(Usy#dUj_x>{ZGobgr~(e^yF9v2+&i{-jq*yJN4d z4*$yZZ^o-I$KT zR!hIr(|9Je{z;6yvFv4}a^Au%YtFEpeBPceW-fV5TfGrl3xDHHF8o&P^^lh!-!=~+`YSY9fTDE@XW=e69djkS(n z_bVI?g6XSbz`}BEv3cc7+y8{WpJVQ2${4u3 zI>L7A#F^`Fif#7WvNYX;`_>IY`t~o#;ez*#WLr8FFPo?R;aCu zS<1R-`fcG$>2t%8?j*HyUNrOm2Ty{>hoUyI~~3C?`|$g zU`r>w2SoqT`IjDNeQzIf!@dpW;bK`-ED=dam? z*{hB{pB|#0sPX;Poa?4;`#ppBdhd2Gd9!cf>%Y^~?o9W#S6;0woO`5c@1m;tSDBui zKI~I*?yJMv$+^Y5ZdF9GymG#)ZjtJiE+1o|?ydG^{q`^4xlZm5s@StO+jY(Ll-|=m zDR*`6zFKcAmg%%%mW*Dy%yRonwKG)|&gHD+xM}ou!t&)I_ivxF>73hFUGhsy(0uOq zIVlk{C-Z-Op?uP>e%Xu9wzYOEvOlB?-U>I_*?&BE>ALeVt+zI`T>iaFY&G-8Ren!C zP28g&7qRwAglO6kO!!^I{U-ad>ddHpI)i+nN27OreLNWc?imKDLQ+~a! z+G%q3`htROX?f?S{rsD=Gr_Uz*~{(jrW^F86otOsWSi#)uHEH z`)8~+I;-)|m+`IrMV?#lr?M7pii@$?9528r=_I!4POks6s~gryzMk}QGlAw%yv8&3ozy6YrkbuQk1|)@{_YE7-}-sco!(LbXcn|LXK_;=<{-QoXJ@ z8!hrS5w2}li(Ow?-NsuxB8)rD&#wNqGG24_U3s@%#nrzsO!gopsg|S~FkI zx|n?-;zE=7_0?r8QmqbKZyU(z+RT0!+4ugR+ttgT)P--?t}jtt^C?$N#o_$#_Mm_H zF&l2>U8q}pDB_>?`$g$bKWz{A#P&CJ?nb3l<0bF!Eb!d;fAa3{??q;Ro6*74>8el^ zx@p_qB`KWSbz*eiv!z}<*Skn`liI!=i|(uoixK~r)32fTwsM+d(CqwdPn9?GBOc8Q z*}Z@6z7y*%S@yqr|2!=Hjh^cL8A{$4y|#Y5bBb$sa9-|_$D0-JpI-I7Tqz(tyZ7GA zODC@#HB;?x>AAbUb9MXGw5`{#M0Fn%x~siCd(wNowYqKUZg&)7`d6QMANpQjG`ynu z*j35wshb|`{LJ^*zrX+1ywc^C(Eh!c@BYH< z^7E5Sqh|ZDUJrj4^-t~o6Wx$_??Z)$!dCov#rgZD>~Z;|$=qoX{jb+;bI#v+_uP*E zV!t2OPR{*yt@`$RKf6Qo)+k^5_;2p_4d2`Pw>_R;A0)PQr}S0nL+x_cO!wz}V)Fg9 zj?ZP+!nQ4S+S6`(`rAw0%D3|5HkVXO)zqsBcz3D!Gk2=-A(^}3E4`Atp7*|bdQ4=U z;q&EBnCDzP^OfuDj>6SPao-6B51RXc-U7gZ-rzpZ+;~}GL zm;YrRYva9(x9nl>KDux5j+^{5j`H1nwd%O0TI{sfH&fi^O%L+=x_{-(`|T#i|L3hq z)I6(nPLZ*z;O9i6gGMWlRD8Z3>HQ{6EdHvq=l{b=_vaoIT_$_~v0l|-fq*JKU5-1K z^@78$b(={i2zPjA9Ob!G>UGxm%-}lLok@2mu!tTxw0ZM6 z-IIokf+Ua4pEKu6rs<`iU-~Lp0w^;&HHu!jvo@Y<&*9m zb}=go@n66mIfef~*B0UDa_W~4vsiC>dhH=^Ta(8AN6%K8?+{w__-V=RJC;{VpZK3T ze)89knQO!&lV$cdRb&`XTR)GHzo_LO%gmzE*AW@om2-=qhfR0i6Z5mcKO|bic^$(B z_R~w|SWSN|_-f6^Kc}X0)}H^Wy=P6)*CW~Cw$ArAZr@sCy*#^n%{QsM|99WJ?EbOj z@|#t*%T9oF9u=r5ok1 z>wTZ|6_gKy4RN+xyoa^QT_1~F$xRuF}gkm&2ky z7%X88myI~0miety(wyo0@fYRSE*+5BZ?HpXN$4`I9r=8sf@#yb8t!<`3XPN$6FzxE zKy!g|Xx_pnvsd0{KT(_9b4%eJ%cEmFb@6i#I~_Rfx@TGEm9LYcuV_q-)H-D0TkF&* zz1)cVYM@wN%ZiP4C!VQxou9nO+IO?#kNo_rzTAISMV?B|e6voKVcqRi?w<_nw_T2( zopw3$#vOC_1!62!FC}Bj4zN^v9!dE=eVGZ5vTgR^DJMThrF!0We%G~Q_5-)cHG+Zn zWjEFQ_joN+7bMKiuW{Bm@7(Opo{iV;#}7cj}0^fHZ?;%Zc78(vNGqlv8hZPOUnof3@;Bzih(( z9~pgrLnoWJpJ4rXd{Sb=JXJNLX)FE(RxSIb!Fe)#>YL=gjtBPJduNqE%5^30vl!UmQ0-O@G~*EqA}(iCyuzAUZ85v+;?P z=4%Dd9=!=yC#c%53-USc__zAz-y-p(HMhK$Oxk**VCJ9RU*A(B-|sgKDioL%@%!zb zsa@WIniAjU>nWsG0)4R-zH{jYS5_uRhcSFh#8MF-BYy1nLgU(et3)7MwNd$29i`;**RPl;<% zPp7%6-xJt-)1cY*&DZHU>pR~^h_98c`ncSGbEucXjTdXDMQ1se{`u3E?eyo*>g40M z>%ae*k;;EBYM#mR`oD+V%wyacH8{I`W;CxeoV?v=s`yT&9TKjs5j|Vq^;U|+h))T< z^L#_zM3GfnKW1GOmkw$Q?K1f`VbRV@2~KefEOt+vU4HzC@2smwXG~h8y>ymRh3L^9 z9nUqNkE{`%_I{_;bROp`*1@IIo;7hRx@nZ1@D0fQ`o`PphxvKIV{`iJzs6i`-(Njv z?qSu0ZLcDwekp8MubscLEc?u#Rn021H#qZ2)jbH5d+H|QLTl=rwH zGtpb@xyw64r8W1bY;B6zyL8inuTzYw7Nx!G+`ZF#Bjb0iyxyKk`z)EZDqgrU!{lsn z#{`iU7YPzy~NT;z__ z@7o^3;`+5G+(%q&*X%84j+t(1)4$a0;56@GQtDGFVXx3|500f~X)k8}U7uvOZu0EY zCbq&XGx(<`Oyzo07dEX_t>W3sohh}7s#RMzDg_^QJvS}mW_R?HYo8Vw?K5KQx%Oz8 zcM4~H*pe<5jh*yztGSf6MZa;;r@k>}j& zPo#b=d1Srn`ty(JH|9Us*3EFTYkEi?|T*{x$2g<{LbqNx%_Xo zRChC!ywm-^C3s%djkp_0*}pwD7M8!;=b!QX&_?Fj*C&N-{^owpy(~9Bs(FPgTkQ2U z?w=YvCq0|qS#+NHBbg>lyU`!iA$xRtoAlXH`O zkJoojG1PR*-CQE0ZF0ZcHGpk(-vr;O$EWlE^=mUwv*`Hu$o$bQHZL`?1;|!Kyp9p5M<4DNHcoc*XU}?Nvlnt;~{r{Fgo^O--GYA;;qXJh3oAu-b^E}tR%{#CUCvnI}2 zlinKiOYQX8(nUUfYwil_Otj;^FJxZUzGCxAmE5(~et8$a>^ZJ`Ea2FV%daYpC!L)v zH>*16*X%U~>6sV(J#O)C7P}={lo{0`YN_^!%+XPaZ3vre#0zOT6WD&yNk&dC?@*4e7L3%!$3l=Sp9J9a|ry55dJ zTlXo|dd)FAtDN@!ntL=s+3zZk?sLrxig}jrUEEz1j?81fKjl?L@jtG9e_@>iewVBs z$v&zrVwuItIBn0Zx?4{Iu20*Xc4NATx!Csm@|AP$ZaiKmox^_pWcQPEGw%GWJdpV9 z*^7;4g*P;`ul{QO8O=RmOWMIjGqqOst~s(w+PUhL@NzS!aQVR70dvo4M(4gYS^95} z5$Dkf;fi|cIcqpK`Ca>4Dst&p!I#H#IUCg`Zjovb`kuVDSZAxv}VMatpbSW+_*8-alk1om6BpA*F1iqk6{N=bc~X&6;eo zpA)ocoRecp#>v@9zW?$vR)<_Wduh@x&dA(U#Z2E1oEc91dpf{}YUnmPJn`av=(Dg^ zkbfu3xxhQOy172gTGe{(m0yAI+U$tUi(I+CuUg`&Rp^&{lJVN)_3J(>2i^*({kcR_ z{d$#(-upc-cJ1h^@l={-V}G;GJH}xBW!Rqo7Zw~-G;jav%DZd6!+hET*OEC)O6UAO zQjK{Sg)Hd0NZAKURpPLX!;nJ-K)!ga@IYnl;~4MvonZIEwz`6jt1Dq(nX{y{q5DNF z_*?>{gKcFR`agNt(yab5w<=uhmBrn! z^FYTFL^~|FdCfDMdv2j5$UT3RCv5k-wSW29&DqP&Wd8sANe-lFx66Zz%w<;&S^8bN z_4H`*LfOV|Jq?q$e5?5yQ|7*aS!sg%CA%5M=kLw8-OgNgWjE+(i(ENI-N>pp=d3L} zvp8Ah zOx7tq^>}^OkHY@r--;f___;4oR=BWs!GV9<-t&FC^eO1}-=&(e3%&2LgHEtJ&+%m9 zmaD77qcbxztKUsL{f@(e>8(WLzrVkuU;Vs(e??-3-_=_mua;btZ7geXSm<$U-R-=` zDr;q9jm57DzPJF=-POgldd-?Q7h=Di1D)k~+jT+q^>x!`873c#n)FQV@3!i#F-GnS zm=!1ZEzkY&;h|aWQLW!sUkl%zyMj43`hA!3g?j=AwrZ@cjk%s=ojpCb=|^a2sNbbq zw_8gpWgGcg96UWcmrm1(e8jeeuOx7NoDyJFE1TD_{60 z*f3|~(esZUB}tp-#qfGIuK=GS(&vz^aG+w!q)AUy)|y`UZYJC4*XJ-@FLuqIJ$J

H!RM4uaB0}sVX;Ngx$VjXlaAT>;6$~Bjmg#3)$dXib45NV)$5!QTF8>< z`}r&=2Q*%DVYq(GH2a!_0vy z&p{e=%aimT4ZTUq7dizU+`Y;l{j0u?d_oKd#}S==KG2CA7EHN}jN6xNZC(Aq$bxCE zKx2mIXOru5KRl|QF576<=CEbS*VoteJ)Rk6=DuOtY0>AP?tDNubMBj_>T+ZQ!TD^# zTjdAu`f~qE?Q8)D10+)&6iA5Ow&QtuuJds#37&hh?gs={FgLPIHrdo|Qj(v!WpdGm zF3#QQ_L-Si_SPYtZo|bCHTl}ReZTXnKOPllHdOxC>%Y(b(+TBmH9tSi%DVh=i|=f+ zvw3?yx<&6OSa|%DDQkiLV~!FA7Ac=gFS1qKOTX>DCp?RPht;a`~H6Hujgs$x!=)T{o`T#+iB5xoGChaGS%<*et)xl|KGJ|XPK@x zc6d@-!XnccC6FQFd3jI&Qj>~|l9QpT!M6Xe{@IrKHd52mk^M&S>}{F*XCjwWHx!s6 zK=j$i@lxz3!2tCKTY1TH@pzx2MQcj z-8v|7)Ae7@JEP6z=~3Id%rqyRo;F4M?RBI>X=EC&IWTlfaymzR1{EWq;xu|g!a*xe zmndgFaF)Gg$Hc3YInin9(ax^v)1sa|?N14uovInR=}`XDPc!*gJXLZZdabT_=&W6x zw-stDE@uB~CrRCw;21_~~D!mvbsJH73v9d195zKc~W-x?z5Ea&L-e zr_9ve-o^hs;^t=IwJ~o~pKD(T%bU7zU%1_-Pv*;7Y>zEkTB2iJKXa=0x>ZkCU;p?% z@AveBswIe<75kbTd=~zH*e>6;M3I?|hodkvf8Wn#qGDogJ4`sFr&(6Mz&O2gsojLxDS@{xIscCSc{o2J zvH#sPxeKRH}S>=GmAwM;@5<(;Bl>zd?ZKNzp*iv<^#w`7=DSy@Zh z30`^DQrZZrXPuQ99F-#7udj=BwtpS9e~qC+!14Y6zOJ9(_y3Z&{?@~M)@Q!mEsxjM z)8kXg{e0G(Kd(QfXw8Zh1{|^~e2>@~s~xc83*?760))uqK zH1_o}SOqofWO_f^p_hN!hEw5B4{3CVe_d9x>HWD)XI2NVxovklYI=WedMlUX z=^BIEUfbkyS!eTZoFr?$eG;egnjP8Vg6mnQ#csKzb$@o|OUt6jIa^&+3Ra4Sq^@ze zwo@zeKG*MW8}jDgxxGtv)8d&rDK}4k)(wqH)D%3j?ssnbUA}AYlM*$r27i6Bds^I{ zOS|4#Tz`O>(pi{Z$6_kOvuN1f`ozTM8xpPjpHX0N>cJwA&E z47aP_?`?L?-TihO_i-`(I2n&y?D91Nmp<~#+uiB2c*GICvncgPZ*uXoGn_7$%-s(N zzL4QqGUcD?%;$@*#T7ew9nt)JNJYE;NvXC~UH)mGndvjPFN7e5Dl2_EujHalKW0&FiqzvC7q9 zs*^UpTrDK3n+#M@bILg^5xrp3&!(t}ZnYSP`PX)ArP? zv`smz%fc7Dsw&_7NcLF7$#b)#uP-j!sJE#swQGx?$AS>4Eg~*q26Zb^#q>9?T7G}4 zQsi~6a>?|i`dZ&|zA0(nDcTXa@KxYq$v+#Mg(hXizwN10uU7fGV%e;mzMBX4>h67; zv3kzt8(LFOs7&_oxwKJ#;rDZ(HUp&9drOL`;Da(?xNhVaOcB*q#^ut7@ zW?OHO! z8LhW_Hf`aUtC_dv&)dvtkKQNNuGzHa(5kAEt$NkDiBn^_br#>)cILvqYpLyLb(Fnh zSEYxa*795H6{)11uDj(??Y3=;-nvCkJ+(Gs$C0XcJ43gI`o8>o+J#~Fn~h6TMfJD4 zo%9V-YH(%#J!8|fikPV1`|M+X@%1=Vd&&rj{#Es!*0Mt|?cmI_c{&#t%zNP^nv}M6 z`}bbEqY^V`bgo-xceEIEIUneFb&rkz4dJhggm+E{_D`@5+K1TV}CorML@w8YS&3ige%f>stQzIwqAC+x4Z|vs8#* z^^d|yk8QhkGPLH$1*c}N%{}(fi|JlUDfh~Z-BUx>mo8&bTJq5Rl<14^^Qsn37typ> zdr2$&q}27*R@KRkYsw`5P7AZ;*)*qLM)O?mf2)At^=qdWv)r;_nysbQThZ?>Uu(j0 z*IA{&_*P=OY|fPxfd^$*?pwQNjn0yD`~LlU?fh)J{Qn=v6))`HDE;^{f87V>+ezKJ zZ@lAw1^IsT$eXls>(Ad4-Q_s5wI?Ph99t)F+$qc=W=YxI(2v^_3r~DCKX%DLE-Ty9 z$Rns(zvdzLx8D4k!*<^`_U8%+J^69I{$H}c&BvD5!lR;$-bOb!UeN!m{QB;-=xigu z+H=R7x_;(9J2TTP^ODN#?Dc!IHhnnic2KZ#UhTKY+iA0Nb6#9nxGm`@*Y4NrcE4HN zZ?|cE?c3~Y`}f#9ROH ziTa?UTsf1kYO5--?(`DvR`{yCx6a>No7e8S)3SnmpRe!pGzHD)DhjDCy6)?^`Io+~ zt@7H(5f4{|FS({BelmoAt7!nIYsh@fz4aDqOkoPzaZ*n0BEMv3I_;|MQ2u1;n0@~H z($nuaOthH{R+DD*l5$=Pt*5r z(cKA*ZY&-=9>kBZ-xin(BFr3Q~7-E!LH^<=VmZnJP^b@bB4M9YZEPI=`a%x7O|Q8YJazt z+}d%jFz4T&pATa=m{^*6xAvWQCbr+1?+4#bFQFAzSBJA7`f!p_UKKRLE@ z3vVarY~Y^H%AAXOJB3*quT^9#j&L-2-kqRu%Uek6)Fr;-%9-7@kM?A*+o>jSckQ-YS`QdI@1)PKeRGIg zf6M$oPt+A{Ek8NkniO3#UqL_H_3gIXd9PLc9pYl74!+uQ*>82EgvB1=l34dX8A+Y_ zbC?bL_HW-k`vz+nr2Y3Igu`Usl2WaTJ)1$5ouGh6)T9lMWQtBGnw7nYsQ!92ocW}t zuI|>^`TK6}`~Ua-?f$wiiyvI-=vGnTIeq(f{{CF^dlifCg6?%KNxb;;rGLFt*{MJ8 z%J*}tFdizq8XC^JTJ~M}g!%FRe_cNqD%2$Mw87!;L;iY>BOKcIgZ*u{%AMJfBv*35 zvGGenX!j8v$@6Rze_fvcPb9^yZ~p&3&&^6-U3n1J!DF0uW`q5|kNu53NtuiMbGEP> zc7<83socTb#F{$cXZ8KxaVx~L?{n+#5xBMg_q*NArTR%zRQd&OgAR57Hv9e`wG$Jj z2%k|%09`%)V{Xv|d$G?;=TC^gBdl0C@qrp+BZCu{<+js$yVp2ZSiXO;xS#KO&i}vP z`NOtlq&^W2>dn?p>2qy<-97g;$M@aeLA`fS+wm{xfXct|&hHyRr!xvM-gywAV-__% zuIi=g@89?T%XM%qVH4juJ8zff5!<;%r!=>HK4;DTTDm@YYV#7oX4Z>`N`=gA?|e`& z5}2z}`Ox}_(k>k*p$_%t7Y;#o|9_spy|7(wmmj}^h(J%_<6}2xrq7d%F;)!8nb>fF zjYVjY`6Mm}-&OvT> z_j^9KJg(sVktU9bEdoiN%?*iD_dICgZn9R~)TBJkb4{qbT;&qq83qfN*~={F`Q+4- z%p|DO`at^kk*J`EZ;MLxIXT`siIm^UR9EJ$G@7S##EAPc??nAy43&qOB-T4C=S$k( z1=T#D=EaRA92TCp!3TskC||h6aYRB>N9Q8zLB}kY^%91W0z5i#6AiAoDNXvjEccpYQ$&D1i&A~}CfV{kg=TesDt>>S|6k@r zfQoYQ^K-nPHeM47vpI2Vf}*oilHdt3BSrR4d@>dn-1_BY*Mvt*wvd11CdBt|nS8`s zg`o5H|M$pNJZRhoIx>HTVRBQqQbdC461$YfkV#LNBNd_}UUV2*Kbw*4v}p1U*KDaG zW&R@77-Mz@~ zjW7%6p%!boYf_gEZ%$5Ca`gD8wB?Jqx5%{3*6(&4-f+ONO2P1>r0=d-ifQU@<+%z^plpC8BV<&xejEEU*e`~8mb z1o>AA%xOB_ud4p_)t=cN;dp5Uvy#i-Hz(`V3p@7*v$yDGnm(STqMI~vNsxS)Ny?#3 zC*Gf!>3`+X&xFZ6{~uTH`L;2pdRKnzwQG(Fiz~jb;@dU(yXj7MrCU}W)vNm&lmo<> z&N^MHVEm-?R8{&qW0uB~kF4iTONmRz9ouo$Bz3V~+SWje0GUUpcj+}9et%+i&CcLu zX0FQd^PVY`@xD}k+rYm0%e!Tl1&;iYaZfnR@j+(dmSzd{HHi}hI2c~u-2dmP{%!O9 zKhI836v&pmw&=;Rh;~=b>vlRmM{)y~`OmjYl<8z?+i0-s>(Tw6mu6`+E|$B)ZvT1a zc@}f;81|bRjwo8Mo3X=X65IbZo6l`BK4-Bw?DU-tiHDgi4`+3pR?wRsaZ{l@;+9*F zgrH8Fz~vWW9=9XzAJCb!)uSq@e#Pe%i~G3r7O60I=FKep^K|ki!-t%uPA@hvI0@bC zxZ7-UdHD+_$=B^Nn`9WT*`7#KiK;oa(Q04$BZ-;*6)Wd|TPa`kVzzRCJd?omK95TZ z^F3cH++QvJ`U7a_?v@lN*j|X}2R1$Dl=}boeZ72=go5`^mKA0U6$gc+9TTjL4+U~& z>O^d5UCnp!?T+goY>&-(a{cJ(KfUJnHh`|Dc8v5sw?`; z?${UZoDM_97jmwLL4C0d877vufp*2b^L!R56PRXg}E&Z4ni9y7;1h3 zU0xw>wYVH+gOiTAc$}&G{Z?G4_fGM7+r*gV@^iXhC-0M1FLIc(;`xo_e%|MTm)Y7pxusYH zn7U0T@b+xGTXtK3yZuarzWt8}%?`7aA3n8twZQ(!SHXU^xR}#Bj!T%?JlEKFKXx~_uFTz0A$I20MVD6bZ5LnV&l-cEEi9Rt^IoS;M2~TS!o47=j!BZ zXo7A5$?k{>dM8od)XksLcP7zro@)HEKQp@j{JZ{5)^hSYOBG%RwbraP zC|{Vx;ZX8wQ<3@d7@uVcOZzil6y`pe*&4E8gUJ%^PfU@`F%yO4yKOx66dX1F3rHU` zJHh6{c2n}Pf``BgkE93{X>Gxl&Z>;rg&CkOd#+<#hIQ174))|Au0u0a91UkHJ~uK` zR8D3ndep~pP%*8i=u7uTj!DcbpOkw@JU3qEc2o0o;gjR9TF-ouywv_$xijR&=?4d! zow@2Jbzk6Lljkl|=<>pThk=>nDG7Z=_bt1BzuVm&VY;S3!0=ZOPvORbbE+#jTsVKe z;NRq|bZ|~8Q$)k8HM0v3zVuk2a3zL2>8?^@8c$EkOtTx0^S8Lhelebu+#wf{@b}5w zDR-ihiq=}bk6)jCS>b^3@xPL~J8wR>+wi9MeAKI-{ML8!^h3U{oU}EgT}w=SuJZPL zsV#d&L1V!DjQ6rNH5GPVUHsa5@4Lq#F3XJ4CP!XwpRTI^T{>@v;*OmUghcMf1_T9o z1@Hx!%m{2f<2bE?aq|VumM(FfuOE|K1sx(EEj`aY^^=CZhSi>y!sCvW3eTD%i%!j{ zdbQGVrlRY&eZSvHi&^$|U$`X9mU1qYF;qm~Xu-s_y>Bmh>+^2DZTQyoo!OO6n-dM2 z4y~~^K09H{Wq*6yCDWc+{7sCNdvZ9HBd9g#K)Hb>3y39Mt~jTl+Y{WQZ1aB3dA>3M!|lCEoZ6hnjFJY+rP03XM(TLB-x6bce|GWrNxx@2 zZ~krc@|^g5b2Wz@>nD4a6`$U)(j%()^xdGZuBQWwZ7=`4zIEx7b*t~yggbFO6Ah0M zJQjVeqjF zJj3+)wPhSd?LQs_F)ut6bfoSOr+UlHggcWJELc8#c3N&1BUyRT_&1klw|-Pwd$Ny5 zNWqM$su$#hTzXiK=+`@kr8RgK`qmzaH~204Izdfbsr8PE-~lBM9_}LJ>@#olHorZ= z^RR_S=scUFt6Yp)q4T15I|Q_!DirVd=fD-2-04t%tFJKqfop)h3(xfh7R5X-|K7g; zFKqI^-8-l3e728$&%SN_3|vO%#lKsemzvDHgVXs|#X5ydUC#@*?3~`lAKGSOr@;OF zPWb*`R});jCAA*^R+`Dfk~;IGUfp7z5hq=Y*OT$@(;BMg6V1B~j90d#= z8-B~JHk=mw?(&N$w!*`+Vy}O`bmiNgn7Y_{Fyj^js;1G+Z$(Yh&` zYHSPH3u|}rPCT0;{qyb7qC#D(V;T3jw;a4N&;Oj%Bc?lM6G}DBB$-YotnK^tU5)v& zH0Zv$V@em4RC=c?>nCtK`zvL|`--~# zkCpBf_VhlE+*0HkwAHUk<D754QDZ>%`+AgJ==QE~R^{vFvd)hjJU8n;zu?+TgC#Q4tq&ZN%kDmVR#bl3P^ z{I@J?@yb*0+C2Pok4t$?diA=z_HAB=f{9z(ua3yz*Qex*Un{i=wtHOou`iD8Hj867 z_lIYbxBmLjb6oQE$F2vmLf0c!^EU4~cJ)`m`u-iJA?Kdi$^YNT@c^GkvOwS|NcB*&$qGY=Q}6f-B0SKDQ^&KtoUDC|1j>_#P=)P(k8J8uhHpYHf*i_RH>3@ zdMreBYW=^yn_`mukEF7F_ue-%VLbffNW}@3P@^`popRB&Z;x?qtvPM8-+hgg^QBLx zRZbt1*l_FEgBic(6e~4)?b35PG3m*gW%0ddyyvVj7CLBgbNYY2MZLU<0>3BQ`s;oEk4v_Defm=&Z?c5X9y-*Wa9Qz!&g&%~KQ)&)#3+PLkkoP8;kQwb_gziI zsvDMVzvWeWf3RG-cf3Mh#8oKGp-zMUq;ehiYMCYNlJy;2uQ@lGofr1E5wxp(IH_*_ zCJp}y+M67bjyiWX9+_x0i>J%;ySRvET)>g~qgT!gIMglIz4qo|d5rMJgha_D^LMl! zeafuR5b@Vz_xZj>-PX!sCa;{#rkHwYZTdX-^8uDmoP1XrT?KVcq_Al|`Q!1*!^-J@ z#&n&TJL;8`JzbNE+P6(nNw|D>-@Tw}w}jxeliQrm-Z)Zm=yUf|t7Gb_Q**9edUP{Y z=FuN^xeA3I<{hcWd0nS>O3e$_dGzySXocCv662LEtIbz_+_>tqwe@+cb!U4jbGF)U z-zan5cCydKW`)YN8_yW`IT=l8E}O9<@lZ$W{LIDMimn`-aooc5k8bwfi{ZZn@5Y?m z!!34&Rggvd*1^`BQ~LKpsdAv#Sam)sb@?{>^PI%+3()@FOBd>+aofZ6Ve)g7!$G$F5 zJDIv>ayj#-MpdKYwL94Y(|$>+OH7=ZePC;^xAB$I`&*uGT&B3`_{pYOrI(7{9plw} z{T#AP;tq$&ZL3U{Sp^`sI8#@aKc2C%O%mN%U{Ndoo3Si@#;ufh)()c~7f5^684;#@<4yn)WY> z)iEEezpdPvFR=c+V1_4)nJZV_JY$h}+3R*LOS_ZkqY!7}r||KPW5+qm=W`4;emWih z&q!d`{eR!KE9y-+|~8>L;~TX$Q<`PBAf zuHLpY(S*+ulN5w+&V=7BnAPBECfQl92i?h0Yx(H-WALKe)NE>OjW- zzu)1UI&OGRvBGBp=*Jq`26wGfwA}D{(^l9v&uRpIWw-a3# zovgD$*U~NL)UMCLAD%~hn=jS5Hx0aUB5|+3ri{25yNCw+o(ne=mA9Tf_Hj~!&Bsff z#z#K3`v3Isn$SL7Lf`z{Qooj^IXr65WBMFtO*)=ozOLEX_F|WKa_*xX<@s}@@5W7% z_#mBowAV~Aq440fC117le58Y2g7xdw&v)wCoHVo0PutwHdDh1JCqMlWG8w1lMW>3y+vTbh1ibzaeNx$2x3 zQrCE&9NKm4{T0XE0@kk_@=_AlJh|C!kjUHkLqT|&4hLV6Lq$Xp%OXenALlkHUu0r? zc{oXIt*ge{p49?s9fDo8A?E_$MHW3-GTD!7lcG9Ht3^zQe68YK+uWLepXc)z#a6%F zs`yjr$%K>Y25l1@FKqZSxAa=%LRk;SuNTyRwO{R9>Z{;BNB-E^J+7;QH!WWJAk$bZ z@~Q9nS$BVTrD|)PRm-^e?%v&Nmire@+hQ^8Sf{H;b&SqzPunN|-=y11uM@plIaAtP_?wAob|HWzXj?Q?n0jP2AHTS*`f(0S9B7L?iR*V-6P- z^B){$yV>2j>_@L^%nD(pgSEo_%>qk7hnKAd6An&t zKl8-(TZk?{X z>wDtbL%P2}<7-9^FCy;9U263<|GN0b<2cTnO(qFbyQIC$x-2`pC$twu)ZEZ$U*u>1 zQhb^O2h$VIO^vBMk&1I8&QB1X)DYCQW`cW%%Z#n3ciJ|&_nhsWoZ`b%D0hFNVX+`juMNwGu{;Y@F{&udigYU+Ro3Z zr|tf(OKtzU`O7`wp3*gDWrvxWl>XgF?q^+dKvHHW+a3KA>=V1>WD?w`#cf#df1~{s z#)}8NSQEPnuhvYODgIhIh5yshQug={&+Q&mO-kt6wTP9w{HO7G8{-LkmUOFp7k{_K zM9x>JieufLr^k}s zJcX`}n+3YVc-g~eOF@$lvI-4f880Reved8INNw7SaD9o7NyR|63^XV+p;jZ&X!%xd0(?xRPa-aQU6qaZy6Th*J2ee z({nc*y37As>WJzj{a>5p(oWi%KmNW&Pcm|eK!xpg%v!q3#&L_Htmwro~t{ZrDi&+yx-@VwLauU5XtZP=Xp=~h9{ zNwyBd?@@c6FIMON`w=uW3ku(_f(4VV{kncYwSYyYQOrqU*Vct=oWwTFaOf+$mAU-E zBDRkQPc^7AZ#r11v0me}##4>w5ltO$8+j5VA4-HNo{)cP(N-j{>pP+UiB*TjQ zO~9|Ny~Ba8p!kSXyC|zqM*IE3t4sPPSe|ewS)V5BaYyuJd9J6S(!z!{8zZci%(!9P zAi%j5%dFc!WH))QxwZHEJ?R+TnJEn#O6K3oRUQdgZRjg? zy(DA5c+b|5KX6mB+ioi#PbSw$zc zbt|=S|0(oseQ|YL;oXCxhkC7buD<|ZW9z&`j^j@0^;q$Dsq4fG+auB^8mc{ebnA#p zw7}aJWhZN;bZ7Y({Q%u{2r8_9f$nnqoBaU19)Vk_AzSLTnN!#atByt8MO=x>GY|3C z|2RBhA^&x^q_Tj9z^IKng5R4}IhM+rZ+x+@ty+2i;u+3|Hk{k|G(C8U?4x#zzNcD3 zx|4zzh$IU($2jtQx~|=wZ6BoH!d>M2N9MVcl!E`3c2oYy4NDTXK9=clRdLzzws6iq z2M5K(EA4Lt_;m~?2zPL?Ffp}0W#4q%@JZ91kDXWfg`Db>l(;V)4xUzafM>eRt+ooM zh94~_Cc3a(YEzxC&pAZV;N;eOOmz=tERHYOuwnqTXhUl3HXlg_kVt=N>+ z-CO67wJ2NtI@_k^Xyp_xo}^3KznY^pT-cnBnJOx4e3$t9_?E-Hq-`!6Qj8k1JHLv~ z+3A+hvHQ`}>G5)twoiD%8pP3gICY|KeXRXE`wo@sOhLZ4W0cn^&UDk?`9$dVt?T>5 zI1E{MEDMrbbsjZ&%J1yod4EHiTV)656T73gy1yFCG+E6m@4fyOWb$)?9Lp@--HVk$ ziw*i5k_B#v+>{OM+o(37Kv_sjNPoeaMKQwmY?VpwmhOgXyq27%8*&`13N9ZOa_p*D z;~%k5L2QTDq#}877aeVlPZOlg>YRV}XHQRb?RqWu$@&Oex5(2>@zdg*ngM%mDzIyu zpWxNkD7s|Qn`!~S4zVK~l?AJv^IjcV%IDOwsjsN-kiWf0SmT|DrW5BpVpEuw=sh{` zRN%DaDeI>{Wtm*}D+(V8zsR=#pm(7RbTtI?6N9o zN#?eW1Iwb+d7pBh_AUACtl1wam)ykn!BNsLs#l}=DARX`HQW$plVl=vS18v<_U?!bmzc zW9-x_{f||xn4NvVmSJ7OS-HookGDRsIjIKZW;o^rt9u*_+=Y zFJ1S{o`3!9(lQn+EhfdvDQtgwy?d_)Ui?@G+LY0)v_aD&s=MrfcD@PI!~+g8Hx#A* zOjD7X>9Y3VtZfBbr`CPtzqYkoY?|F~jnnU}tRl83WM*%FlD+2F_cj5GTWtl7Vv0$I zefpkNQg=P|FX(|PT0Q;Q+;2HbB3KSBiEV#7xBLt=FZn7>nBSlpWvRT=YZz4Zin`^dMK> z&*fuO&z$>iQgu7HC6y7&@@aFr)09Wk&fGk4f2Pt}#_}WQ!Y-~p^5&`Mw8|g4g4-@l zc(^ere*;fp_NFy~&Y@<>QFw!xK6?(dUkYVGvA_ifF$=W8F_y%}Z^Ul^u+ zYE8jTu_N;~pSiSNtL|-E`kGI@nP1lx%5p~bZ0uo*r+jvdUy`I0KcG z6{9C9uuhsL**;=KOgm+R`t($^U2R!FMQ=%`g9FPro!e}ZynPO(^J)XHovip zj(Vt<+$_3oo9-#cH0KjZXQpsE7EijCwcUNS*&CI$p%1?wlVx;Q;Ad3Ntn;>D>T_7F z_&{at)dl%m+dg|nZCU1cMImiTgZkm5NlT8hC=@on4so5jChfG2ap}I)R9o-0THGl* zw|CE8+WvKFX>P~TUnSf}*6LYD`J|rT#3-<32aA)@>c!EQqTZTiPHmgJWGmO3dn=S5 za9n9@m~67&6x0N~Do`-#TGoZ_QHMPzwFwxC+N<=+W=px~dQSLsGU&wZ6H-|l%#N;j z_)5Yo^5B{cRlQrY%C_lwdLP>@$jt8e*!)({%J0cBQ5tijLv~+1@TtvQ6V%?(ymM~` z$eLz{$w42Ur|cz>q`&h-Up9L7qD2_F?o86?(My=n2~rb;M#_(tHayBA8Na& zepP#6sJf`)*Se(@kulo3a;tfLUp=|odX=xqZTC;^@aLy*d|co49h4|Q!KABnA!CwQ zx|FznT+aLb|M#7pVR+c?#{=f-Z#UC>72f=r(OmOg%%LN4tD5gDk+^9ex8I68B^|YW z*}CMdf9L&=u6_Qa$};z3St)3*%YwxU0kchc7acpcTgC5BZpRwc>mj!rKI-f#R&qSt zCFYrEGd>(sa$~v9cT_$Hd`ed!m-YhXZw#Y2y!~}tK zgW_jr93K3bo%`@m>%*fS6TWT9j5_QlZM>|RO|y5?o#1EZ=4J;k_v0-sdw`i529=+le2{*w zApShK@6$%z`GL8o<~~f7X>3zGQ2czdBJbf<%JZkj%-0cG`eM?F?-uXY3olo5_$0pf z-2ZvikuCia&0@M46Atc*k)6Ay^vUkE@Ae&4F+1YoX1mz%p5t6$)Aw6JHx-L8De^2> zslpq?Y$;_?u;A;ftJ*Bp`#+zvHmmxQ@%zQ%ex}M-cXyX7xb0C;Eahq1Be8K$A&d)V2&F^nJfo5Y-dQ{^ zGnsTvqG@Mb(Yz|B@M|ac-RdxyK5bX@gyn{ReNSDKsr_iUcJ;71+1$%3VW1FkQNlzq`0uZ; zs|)KpKV6bbHM?K)*+FFG?4p|X8-;VG8<&14x}UfEZO)k)hGtn;RyZ6u)-Rvm%+AmE z+<}R4ar*gr3;R+}Ph&mwoIkGku54UCct9*$@PxYi$Lg5nJtu9B72H$I@wsED=kDJz zL)0iL>2dlW+q74U@0^;defY;Ysho>VV(Je*t*x+o_B;00G{KBD&l#(DgkSxvzBF~l zbLQIa&XRfSJ<2TFPxeMk`&=M;PTl$A%!d-sgWBeDimACRn&)2Y^Tz%CswWZ+bFR#v zGTCZ{ao)zc7B8J@e@d5LX3IUkL294+Z$Vz3jq+O@?;mU^UVP??tL=$tZ;JoOT3TJc z79w&|Nm<&NSu3Usi4vq(8iwWO6J!3(H;Ey(Q%su79}2TV_ki1 zie_-m=Cfv-Z*ER!H);#jImQ(r&(Eiyo7d-YZAJh4h*t{LR}LQ*WtV-`vgG5WWjZW; z=59J=Q;r55@0QxB+1_LGXV00H=23SP3ND^qrrPk`O1AM_UqkdItKYI4^8SBLzVhRo zgdC@6vMHCSa8b|u8u!FUljm-fKHB+MBf4gJ&YUf${Y56dKlhGhZSnp7+_RI~>?ZAT zpX^oS{4R9f#Od*MH`nd|R|Q&u`gUq~9OwO;7rQU4yjT6+_V@ex|JnUEpE~w_yOr(e z?i{bo!}5O7?Y!N(Pox*v-2d34w54T}r)0+KjmPD-E9Qoc@|A%XH^CpSoXs z-gev7u;|uwqe!`Y^(PfCmri$zKH*_&bNcbI-qn`Vf6c3UrMdfYpLNr}_BB^z&7b^# zZvS6$&sH^`83H{_bABHY_HX$YsGoa*pNDA7dyx7 z7ktFFOvZhE&WWuV@BPC}Vjgr}koLLyX2|lpN|rKjxAA54|wc*n3p9h0|=#-8t?O zGUvFpN~UdhsoGu5&$siV^!H3rzVL^`p=QPbwKvV@GeWagOzNp)3j|FiA5vjE?{ep2 zzJeB4Ip?oyV$nH*^TazY^_t(4I3Z{y+rlB+Uv#DLe(m?0uHvyGM%C)-p%!AE0_WAv zzuo-u6_M4X+$#{rlKo-{Rlltis(p zQJU%7QT@7$=j?vJ*--!7`ab7ErHC~hukHW;`TVxW_}qs4n#a;*hd9+)lA7a=A8YyQ zsXkZaRPF!Y_qWgg`zGCyuOmJ;W#R^*TUO8K6u)`Yt`4Bz|xUiI4r z&3s(PK{F6HukZV|RqFlf7pYT46#8Uk=gxV$d*b5r$#0K5>Y6@#x1HIGX?fEo&+@rc z;gKXGIp1c^#6JmNZ)Gl@>(nF3%<_CwuZ*RUfJNM*JvN%ckNF<`oUvl@*WOKY`YUC$ zV@axGR?49=;?j(F| z3Q4@0^@go7@so7FdRff%fK3-KXRvz5p-pu|0Rn) zZG~KiFB|#_`KrR4*VLal$x_KP*D-BPr(v7!x7oWcd_2IKEjG>aSED0C`7uyf&0V@+ z(edy#(@*}8@NdhJkSsWVM5^D;`!sAjiSK1$2bM(gEnwe77zPDcejB);TYHN3j zPT%1grwj7+e?BH$$S_+ZCcf=V?WFc)X=_g(+4$srrEAu?er;3r>!*C?ald;#b?y31 zVf!L{yF4E!xyJv`nC3TY-_P0N86~Ej#~LHltVj2M{JoLfzcw;W^IPNnjaM8#OqjX9(72_ik+IaRsmrG# z{b4Ta!CRHj=Qi&-((h>Y{dWHT3E~f5Ebiy~>R5I%@xZPe(bAb*o;5-Yro%py_@IlJB#m(-+5*vGaU406c668WPeQkI?j!4 zg*(?QD>GtMj9$BS()6|4LRzQ2)(SE^+%#olL3l*a(xA@tY1)-OD}{b;Oz~e>6msPL z^^i|S8+;5*HS2bIWhcI}spC$`?G-xxVouJ^bII#3%)7SH=t2V{OntA*`{+(?(y|Kc>h7z9~bZ@>nz6uo>9+t>l*3*xqWKg-s4*H zYpVYKbv}FUyL`?|yI3XzPo=3U(O>`0lP{juU*w$j)%yO_k8;~hC$+JvuhW!1Da@j! z_apAUXtzqQij)ACPI^mgf^E~1kLLS@+n6jHMI4=OoS5Xh^ZC4Ly@<0NosX@%ejT^} z_tBvzZ{N>n4h;%k8n0`d*ghsL;^h#?n=Byrs`*Av$C8V7pBDUacG>tW@#e1o|9&^m zmVNv95|7~FN8A4Y``!Lcaov}Pr?l5^S=?u(CBfd&4q7+gv_~NLm){x5q9F5oK?2sV zj%#!Kyi_hMjnGwWxbpcyGk=>=%cl$S28SIQbfWY3mbR;8_RNpC{OH%AgoF2EqSy3H zC|}1|@j&+am5(N&Q#YR4zC8M>bAH6Po1N1ti)V$di%E7}%+C5~YV3|fM*RCzZyvw5 z@l1BGj&=Rc)avrBIsU3_b9Nngb1z(1xoE2F#qc>=sVjG$Ui)U=be-TJl}$^R1o7|5 ze7St;o@ZZIrfJ3hyyxS+`rRt``#EiwA3Stp; ze70W#exFIV6aM9YH{zm3b56^y57H0KCpbKMTVZ}t|Ab<#qF(;>*mByTHaO69td_Yi;_MEy8mBWm@)hpwezVD2Vb{e=v+dLJ3ocCfy6V;6n_>U%i=T1X za_Mni|x_@Ex9hpY2^l5^tmDt5=lGlhO~Q8J&lU^z?2G#KKTjt-TpWAKiRWc;%<}2m4rocfzctagKH2nWm3O`3*O=-LjeCmc zC+tkA)Z#GuuF{!4!zH7nW|mHbWaN%Z+Seiu9=tufwnp81?uO}0+rMo6w0&7b)-PT9Ax=-_<|vg;J<~VcJtSQZrQSMefK0&y0LjT%z5|UAI7=xBgy}38BBvNo7a$Dy+U3CGlI#Uisvk zjP24#WU^KA*68o~;Pm1@BfE^inpK6#ix|v<3LfujR9^XWW1LyribPZI^U-Opd#fU{ zD(sCTPX#eoM_pepn|arM-|Wy=5ev_0PVr9KkoEOt8tZJ{O-wrtmx-O6<@QH1JLBEn zt*P^7XG~r1$Jw&Vp?XW%QrBafx>F=)Z!&l@b7@z#@;{%so}cyC^<5T^+LiCGEn$5l z=%{O$iO*|0`R&uEzS-Ei)N9q+qigq8`b1^NGv?nHe6fUM$L{3x-5BC1HCmWAU z6k)!80JNoO$G1e)g*sxQYrJ=`=VdI`ol?Ug$Z4Lz?EN5f`P{VM?{?>Ja64+sJF)d~ z#HNUDp2!VxAFcH|K1zH|e0X@)VX?cfe#ln-nCpLnMXw{G+=6|lVqV1m3EgFdcNhQP za`N2!-EX%=E3BHL9?7|`cE{(3Vx8T}@72#8l@)pC!OsxH>92=s_bhEN2wW^e}FoV^0 zOHP>i(K)_hXKqSQ)>Ym9<4V9@?ISB!J<|`)xf!B;qoyO3YwDNBy_#WvR@$!33SAj$ z^Lw6Xuk_LzrdM~aU6XIS)#iG4QFKlHvd}%6+v5Z+g<2l`no;}euR_$tf48H4UVi^- zSJ$Suo0m>Kd3NKQUGBQSzf^~5YDZPM*lWLt3QG2gzW#CJLjQ-y=88{J&QiGXW98Z2 z$%nl`#|EjpFt9dEPYu5I?`2CtX@lPREuSXaXHIqIGp+t(IJw>H>$JD2tjFgjs;1A+ zco|m|ee+7+e_4je_lJ%tHwbkt>e_Vj*p4ewE3YkH%2w_&Db7JgL2Ux-njp{A9sA-g zFMp9YW6KfO>-MLL=X|!9cV>$3eMP@Q&u(6=yE}^?-%7gZJKnNRYKzUv3F?A!?^5Y(9E3MO>JtMfup7tL3t{4j2A%l~G#Pd$^64`@5uJYf9CVe~&F99CAG64ws*hmR8ZVXa3Fh zetPQCu+q1Sr^Q94tvoNk;EDWSo#y$E&Kx~;EFzWlb9mIx_wUWDUl(ZK^A6L!JA0~^ zUeMS1%h&u6-W{E~FiA9Y-|6ptTUX7yyXMRC|C=5KM!iZrr?lFpGM?4@+qN_N=WSUR z-M{gRV$t%DZEmj@{&xI$PW1V`gwRECcl9hzhW-B+=dBx`>uUdFNvQ7R?x(AR*M8_M zUz?Otul?iAzA%#=<-Vu>T~o-s6aVhr4Y_%8(=UR?;Ty~P56GPM@r%65>>v4U!~Cf2 z;qy1oRwbiIsQhGB%K}RiO-z0AVN2f=> zHhul7Hc9%{B3`@4RXYMJ-NkNso;rBHaN@5&_mgF}%-wR~$c!C37MR@El-{pg9<$l} zn_^`78}+YhPuN(6f_MLuaaXPu;NObl&5$zrL^O zT%W`JUa7o1=SA$(=b)LV%}Nj6^_}HPw_cI6Bj}C)rd98jM@&E6zioq;ueqx8i*@_$ zY7S_{U#qfs{N<3my+M8Dtg;K{;v4rWUx<3wH^E3G{?dKtBL%i9tu{ABIyX0kEYMi& zGTCvW>F$UfGfVe(%ui~3ner#3dOzP;)%w`N9Nv}n$*0Vg{M%N=*CL;-ZkQQyQ6V?w zvC31scN=!BpA^9&9(lr4(jdEAaNYL$O*u^9wt zyxP~bJjCpHPtZH@KADV2Ri=|G_CC;CZL#%K`n}1!m#C@p-MqNQKYaBJoloCCshmC& z5i<8xd|{TRs*L81s+Og@G2p zTD;-ShQxJr1@!R7&ZxpB%a-;RwU(fSl;8(}~kw-M(w|XW6gZ4LbP~ zw#-=&8g<2LsmWj0$5S}BRCflw*Zyn&ZbG-n=VfJ=e&^=4{?-)pJL$FPeXY*!mG`~n zCSRMf`}AqhWYT7Z2ZotvnUa66O!?>@_Vby~-nmb+w%)yWYSmQX+Ha@B{>?tU_RY05 zqOqTtwe2d)zaP%G58ry&<5rYm=8@lj-q-)%Y<~aBquoVIlaKx1JpXTwhmTooDXK4uo$IdgEHm`xe|s&A zEk2jcK5f>g*Ifsf8{YQc_lJf5dRW{2ldmT4f9K9J&q$`X1xIvPjT26|HW=6)5b!T}pm=afk2Ldr zP9-*Ju{@O>rg=XW8?QFYyR&1%f|Sj+CcV4c|KzwY@Z{K`y4G{U*4s`quh*q7UH1Zj)9pIHo4f4tTOZGU=5#r)vGCw+7N)hSp3d7H z+#Tu@9^3AF+-H65$U@bFi9*x2p182zqMwC7(0>)L%a`66j^;x9m&^;0Haum2&N4xG zvhxv%B~MbSH~Hi~ov52O!ROlKHOk*?e@gxqi49b4^xfvT_l3K(*OQHkOdd=<9+M_Z z@mj9>&)l`$PmX=t zzVfMa+SSkJtluA67Sf>Fag@!l(P@u@o8k#y>myv7j#P2i|2!RksER8l!szLP^S_Qs z=d&bT+@60w!ENv7bJn@L-)`eht$d(RHbLxFqHLGSL8q`Vhy70`c{}aq39?CHx$@{T zuVd5G%g^%bKF>C|VA!Gh`T^si+=kFs8@B}d%N(%iH=AkbG0A{Ke98Os_5X@L2)F-n zt^a7TqTrAC{vWOdt``4(JZ@%vd;kBx?Vv&Hh5vb!3LBL6YDy@62koa@Q@lp}P@%!n z{u2i)LB~q)+9!rJ`2U(Ge5Ug2)o}JVioLSA2^ptqU#8yTXOf$It!`atvBxZ_RfeIA zw{Ld(N2h&DI=$!9o>04A6GHEN-*j^U^O_&p$Dj2X!pAcbg*Z&uOT73KS`8>N&4~t|^HF67o|I;^*y{

X`!Qe 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 z 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 z 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