blob: 0ab3dadf6862cbc50f4e51e77890637e64704d1f [file] [log] [blame]
/*
*
* Copyright (c) 2022 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "platform/ESP32/OpenthreadLauncher.h"
#include "driver/uart.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_netif.h"
#include "esp_netif_types.h"
#include "esp_openthread.h"
#include "esp_openthread_cli.h"
#include "esp_openthread_lock.h"
#include "esp_openthread_netif_glue.h"
#include "esp_openthread_types.h"
#include "esp_vfs_eventfd.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "openthread/cli.h"
#include "openthread/instance.h"
#include "openthread/logging.h"
#include "openthread/tasklet.h"
#include <lib/core/CHIPError.h>
#include <memory>
#ifdef CONFIG_OPENTHREAD_BORDER_ROUTER
#include <esp_openthread_border_router.h>
#endif
static esp_netif_t * openthread_netif = nullptr;
static esp_openthread_platform_config_t * s_platform_config = nullptr;
static TaskHandle_t cli_transmit_task = nullptr;
static QueueHandle_t cli_transmit_task_queue = nullptr;
static TaskHandle_t openthread_task = nullptr;
static constexpr uint16_t OTCLI_TRANSMIT_TASK_STACK_SIZE = 1024;
static constexpr UBaseType_t OTCLI_TRANSMIT_TASK_PRIORITY = 5;
static const char * TAG = "OpenThread";
CHIP_ERROR cli_transmit_task_post(std::unique_ptr<char[]> && cli_str)
{
char * cmd = cli_str.get();
if (!cli_transmit_task_queue || xQueueSend(cli_transmit_task_queue, &cmd, portMAX_DELAY) != pdTRUE)
{
return CHIP_ERROR_INTERNAL;
}
cli_str.release();
return CHIP_NO_ERROR;
}
static int cli_output_callback(void * context, const char * format, va_list args)
{
int ret = 0;
char prompt_check[3];
vsnprintf(prompt_check, sizeof(prompt_check), format, args);
if (!strncmp(prompt_check, "> ", sizeof(prompt_check)) && cli_transmit_task)
{
xTaskNotifyGive(cli_transmit_task);
}
else
{
ret = vprintf(format, args);
}
return ret;
}
static void esp_openthread_matter_cli_init(void)
{
otCliInit(esp_openthread_get_instance(), cli_output_callback, NULL);
}
static void cli_transmit_worker(void * context)
{
cli_transmit_task_queue = xQueueCreate(8, sizeof(char *));
if (!cli_transmit_task_queue)
{
vTaskDelete(NULL);
return;
}
while (true)
{
char * cmd = NULL;
if (xQueueReceive(cli_transmit_task_queue, &cmd, portMAX_DELAY) == pdTRUE)
{
if (cmd)
{
std::unique_ptr<char[]> cmd_ptr(cmd);
esp_openthread_cli_input(cmd_ptr.get());
}
else
{
continue;
}
xTaskNotifyWait(0, 0, NULL, portMAX_DELAY);
}
}
vQueueDelete(cli_transmit_task_queue);
vTaskDelete(NULL);
}
static esp_err_t cli_command_transmit_task(void)
{
xTaskCreate(cli_transmit_worker, "otcli_trans", OTCLI_TRANSMIT_TASK_STACK_SIZE, xTaskGetCurrentTaskHandle(),
OTCLI_TRANSMIT_TASK_PRIORITY, &cli_transmit_task);
return ESP_OK;
}
static void cli_command_transmit_task_delete(void)
{
if (cli_transmit_task)
{
vTaskDelete(cli_transmit_task);
}
}
static esp_netif_t * init_openthread_netif(const esp_openthread_platform_config_t * config)
{
esp_netif_config_t cfg = ESP_NETIF_DEFAULT_OPENTHREAD();
esp_netif_t * netif = esp_netif_new(&cfg);
assert(netif != NULL);
ESP_ERROR_CHECK(esp_netif_attach(netif, esp_openthread_netif_glue_init(config)));
return netif;
}
static void ot_task_worker(void * context)
{
// Run the main loop
esp_openthread_launch_mainloop();
esp_netif_destroy(openthread_netif);
esp_openthread_netif_glue_deinit();
esp_vfs_eventfd_unregister();
vTaskDelete(NULL);
}
#if defined(CONFIG_OPENTHREAD_BORDER_ROUTER) && defined(CONFIG_AUTO_UPDATE_RCP)
static constexpr size_t kRcpVersionMaxSize = 100;
static void update_rcp(void)
{
// Deinit uart to transfer UART to the serial loader
esp_openthread_rcp_deinit();
if (esp_rcp_update() == ESP_OK)
{
esp_rcp_mark_image_verified(true);
}
else
{
esp_rcp_mark_image_verified(false);
}
esp_restart();
}
static void try_update_ot_rcp(const esp_openthread_platform_config_t * config)
{
char internal_rcp_version[kRcpVersionMaxSize];
const char * running_rcp_version = otPlatRadioGetVersionString(esp_openthread_get_instance());
if (esp_rcp_load_version_in_storage(internal_rcp_version, sizeof(internal_rcp_version)) == ESP_OK)
{
ESP_LOGI(TAG, "Internal RCP Version: %s", internal_rcp_version);
ESP_LOGI(TAG, "Running RCP Version: %s", running_rcp_version);
if (strcmp(internal_rcp_version, running_rcp_version) == 0)
{
esp_rcp_mark_image_verified(true);
}
else
{
update_rcp();
}
}
else
{
ESP_LOGI(TAG, "RCP firmware not found in storage, will reboot to try next image");
esp_rcp_mark_image_verified(false);
esp_restart();
}
}
static void rcp_failure_handler(void)
{
esp_rcp_mark_image_unusable();
try_update_ot_rcp(s_platform_config);
esp_rcp_reset();
}
#endif // CONFIG_OPENTHREAD_BORDER_ROUTER && CONFIG_AUTO_UPDATE_RCP
esp_err_t set_openthread_platform_config(esp_openthread_platform_config_t * config)
{
if (!s_platform_config)
{
s_platform_config = (esp_openthread_platform_config_t *) malloc(sizeof(esp_openthread_platform_config_t));
if (!s_platform_config)
{
return ESP_ERR_NO_MEM;
}
}
memcpy(s_platform_config, config, sizeof(esp_openthread_platform_config_t));
return ESP_OK;
}
#if defined(CONFIG_OPENTHREAD_BORDER_ROUTER) && defined(CONFIG_AUTO_UPDATE_RCP)
esp_err_t openthread_init_br_rcp(const esp_rcp_update_config_t * update_config)
{
esp_err_t err = ESP_OK;
if (update_config)
{
err = esp_rcp_update_init(update_config);
}
esp_openthread_register_rcp_failure_handler(rcp_failure_handler);
return err;
}
#endif // CONFIG_OPENTHREAD_BORDER_ROUTER && CONFIG_AUTO_UPDATE_RCP
esp_err_t openthread_init_stack(void)
{
// Used eventfds:
// * netif
// * ot task queue
// * radio driver
esp_vfs_eventfd_config_t eventfd_config = {
.max_fds = 3,
};
ESP_ERROR_CHECK(esp_netif_init());
esp_err_t err = esp_vfs_eventfd_register(&eventfd_config);
if (err == ESP_ERR_INVALID_STATE)
{
ESP_LOGW(TAG, "eventfd is already registered");
}
else if (err != ESP_OK)
{
return err;
}
assert(s_platform_config);
// Initialize the OpenThread stack
ESP_ERROR_CHECK(esp_openthread_init(s_platform_config));
#if defined(CONFIG_OPENTHREAD_BORDER_ROUTER) && defined(CONFIG_AUTO_UPDATE_RCP)
try_update_ot_rcp(s_platform_config);
#endif // CONFIG_OPENTHREAD_BORDER_ROUTER && CONFIG_AUTO_UPDATE_RCP
#ifdef CONFIG_OPENTHREAD_CLI
esp_openthread_matter_cli_init();
cli_command_transmit_task();
#endif
// Initialize the esp_netif bindings
openthread_netif = init_openthread_netif(s_platform_config);
return ESP_OK;
}
esp_err_t openthread_launch_task(void)
{
xTaskCreate(ot_task_worker, "ot_task", CONFIG_THREAD_TASK_STACK_SIZE, xTaskGetCurrentTaskHandle(), 5, &openthread_task);
return ESP_OK;
}
esp_err_t openthread_deinit_stack(void)
{
esp_openthread_netif_glue_deinit();
esp_netif_destroy(openthread_netif);
#ifdef CONFIG_OPENTHREAD_CLI
cli_command_transmit_task_delete();
#endif
return esp_openthread_deinit();
}
void openthread_delete_task(void)
{
if (openthread_task)
{
vTaskDelete(openthread_task);
}
}