mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
375 lines
15 KiB
C
375 lines
15 KiB
C
/*
|
|
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include "esp_openthread_netif_glue.h"
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include "esp_check.h"
|
|
#include "esp_err.h"
|
|
#include "esp_event.h"
|
|
#include "esp_log.h"
|
|
#include "esp_netif.h"
|
|
#include "esp_openthread.h"
|
|
#include "esp_openthread_common_macro.h"
|
|
#include "esp_openthread_lock.h"
|
|
#include "esp_openthread_netif_glue_priv.h"
|
|
#include "esp_openthread_platform.h"
|
|
#include "esp_openthread_types.h"
|
|
#include "esp_vfs_eventfd.h"
|
|
#include "sdkconfig.h"
|
|
#include "common/code_utils.hpp"
|
|
#include "config/link_quality.h"
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "openthread/error.h"
|
|
#include "openthread/icmp6.h"
|
|
#include "openthread/instance.h"
|
|
#include "openthread/ip6.h"
|
|
#include "openthread/link.h"
|
|
#include "openthread/message.h"
|
|
#include "openthread/thread.h"
|
|
|
|
typedef struct {
|
|
esp_netif_driver_base_t base;
|
|
int event_fd;
|
|
} esp_openthread_netif_glue_t;
|
|
|
|
static esp_openthread_netif_glue_t s_openthread_netif_glue = {
|
|
.event_fd = -1,
|
|
};
|
|
|
|
ESP_EVENT_DEFINE_BASE(OPENTHREAD_EVENT);
|
|
|
|
static QueueHandle_t s_packet_queue;
|
|
static esp_netif_t *s_openthread_netif;
|
|
static const char *netif_glue_workflow = "netif_glue";
|
|
const esp_netif_inherent_config_t g_esp_netif_inherent_openthread_config = ESP_NETIF_INHERENT_DEFAULT_OPENTHREAD();
|
|
|
|
#define NETIF_OUTPUT_SIGNAL 1
|
|
|
|
static bool is_link_local_addr(const otIp6Address *address)
|
|
{
|
|
return address->mFields.m32[0] == 0xfe800000 && address->mFields.m32[1] == 0;
|
|
}
|
|
|
|
static bool is_mesh_local_addr(const otIp6Address *address)
|
|
{
|
|
return memcmp(address->mFields.m8, otThreadGetMeshLocalPrefix(esp_openthread_get_instance())->m8,
|
|
sizeof(otThreadGetMeshLocalPrefix(esp_openthread_get_instance())->m8)) == 0;
|
|
}
|
|
|
|
static bool is_addr_locator(const otIp6Address *address)
|
|
{
|
|
return address->mFields.m32[2] == 0xff000000 && address->mFields.m16[6] == 0xfe;
|
|
}
|
|
|
|
bool is_openthread_internal_mesh_local_addr(const otIp6Address *address)
|
|
{
|
|
return is_addr_locator(address) && is_mesh_local_addr(address);
|
|
}
|
|
|
|
static esp_err_t notify_packets_pending(void)
|
|
{
|
|
uint64_t signal = NETIF_OUTPUT_SIGNAL;
|
|
ssize_t ret = write(s_openthread_netif_glue.event_fd, &signal, sizeof(signal));
|
|
if (ret != sizeof(signal)) {
|
|
ESP_LOGW(OT_PLAT_LOG_TAG, "Thread netif failed to notify eventfd");
|
|
return ESP_FAIL;
|
|
}
|
|
return ESP_OK;
|
|
}
|
|
|
|
static void process_thread_address(const otIp6AddressInfo *address_info, bool is_added, void *context)
|
|
{
|
|
bool is_multicast = address_info->mAddress->mFields.m8[0] == 0xff;
|
|
esp_ip6_addr_t addr;
|
|
|
|
if (is_openthread_internal_mesh_local_addr(address_info->mAddress)) {
|
|
return;
|
|
}
|
|
|
|
memcpy(addr.addr, address_info->mAddress->mFields.m8, sizeof(addr.addr));
|
|
if (is_added) {
|
|
if (is_multicast) {
|
|
if (esp_event_post(OPENTHREAD_EVENT, OPENTHREAD_EVENT_MULTICAST_GROUP_JOIN, &addr, sizeof(addr), 0) !=
|
|
ESP_OK) {
|
|
ESP_LOGE(OT_PLAT_LOG_TAG, "Failed to post OpenThread join multicast group event");
|
|
}
|
|
} else {
|
|
ip_event_add_ip6_t add_addr;
|
|
add_addr.addr = addr;
|
|
// if an address is not mesh local or link local, we set preferred for this address.
|
|
add_addr.preferred =
|
|
is_mesh_local_addr(address_info->mAddress) || is_link_local_addr(address_info->mAddress) ? 0 : 1;
|
|
if (esp_event_post(OPENTHREAD_EVENT, OPENTHREAD_EVENT_GOT_IP6, &add_addr, sizeof(add_addr), 0) != ESP_OK) {
|
|
ESP_LOGE(OT_PLAT_LOG_TAG, "Failed to post OpenThread got ip6 address event");
|
|
}
|
|
}
|
|
} else {
|
|
if (is_multicast) {
|
|
if (esp_event_post(OPENTHREAD_EVENT, OPENTHREAD_EVENT_MULTICAST_GROUP_LEAVE, &addr, sizeof(addr), 0) !=
|
|
ESP_OK) {
|
|
ESP_LOGE(OT_PLAT_LOG_TAG, "Failed to post OpenThread leave multicast group event");
|
|
}
|
|
} else {
|
|
if (esp_event_post(OPENTHREAD_EVENT, OPENTHREAD_EVENT_LOST_IP6, &addr, sizeof(addr), 0) != ESP_OK) {
|
|
ESP_LOGE(OT_PLAT_LOG_TAG, "Failed to post OpenThread lost ip6 address event");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void process_thread_receive(otMessage *message, void *context)
|
|
{
|
|
esp_err_t error;
|
|
uint16_t length = otMessageGetLength(message);
|
|
|
|
error = esp_netif_receive(s_openthread_netif_glue.base.netif, message, length, NULL);
|
|
|
|
if (error != ESP_OK) {
|
|
ESP_LOGW(OT_PLAT_LOG_TAG, "process_thread_receive failed: %s", esp_err_to_name(error));
|
|
}
|
|
otMessageFree(message);
|
|
}
|
|
|
|
static esp_err_t process_thread_transmit(otInstance *instance)
|
|
{
|
|
otMessage *msg = NULL;
|
|
esp_err_t error = ESP_OK;
|
|
uint64_t event;
|
|
|
|
int ret = read(s_openthread_netif_glue.event_fd, &event, sizeof(event));
|
|
assert(ret == sizeof(event));
|
|
while (xQueueReceive(s_packet_queue, &msg, 0) == pdTRUE) {
|
|
if (msg) {
|
|
otError ot_error = otIp6Send(esp_openthread_get_instance(), msg);
|
|
if (ot_error != OT_ERROR_NONE && ot_error != OT_ERROR_DROP) {
|
|
ESP_LOGW(OT_PLAT_LOG_TAG, "ThreadNetif Failed to send OpenThread IP6 message: %s",
|
|
otThreadErrorToString(ot_error));
|
|
}
|
|
if (ot_error == OT_ERROR_DROP) {
|
|
// OpenThread will intentionally drop some multicast and ICMPv6 packets
|
|
// which are not required for the Thread network.
|
|
ESP_LOGD(OT_PLAT_LOG_TAG, "OpenThread stack filtered netif packet");
|
|
}
|
|
if (ot_error != OT_ERROR_NONE) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (uxQueueMessagesWaiting(s_packet_queue) > 0) {
|
|
error = notify_packets_pending();
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
void esp_openthread_netif_glue_state_callback(otChangedFlags changed_flags, void *ctx)
|
|
{
|
|
otInstance *instance = esp_openthread_get_instance();
|
|
esp_err_t err = ESP_OK;
|
|
|
|
if (s_packet_queue != NULL && (OT_CHANGED_THREAD_NETIF_STATE & changed_flags)) {
|
|
if (otLinkIsEnabled(instance)) {
|
|
ESP_LOGI(OT_PLAT_LOG_TAG, "netif up");
|
|
if (esp_event_post(OPENTHREAD_EVENT, OPENTHREAD_EVENT_IF_UP, NULL, 0, 0) != ESP_OK) {
|
|
ESP_LOGE(OT_PLAT_LOG_TAG, "Failed to post OpenThread if up event");
|
|
}
|
|
} else {
|
|
ESP_LOGI(OT_PLAT_LOG_TAG, "netif down");
|
|
if (esp_event_post(OPENTHREAD_EVENT, OPENTHREAD_EVENT_IF_DOWN, NULL, 0, 0) != ESP_OK) {
|
|
ESP_LOGE(OT_PLAT_LOG_TAG, "Failed to post OpenThread if down event");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (err != ESP_OK) {
|
|
ESP_LOGE(OT_PLAT_LOG_TAG, "Failed to configure netif state");
|
|
}
|
|
}
|
|
|
|
static esp_err_t openthread_netif_transmit(void *handle, void *buffer, size_t len)
|
|
{
|
|
esp_err_t error = ESP_OK;
|
|
otError ot_error = OT_ERROR_NONE;
|
|
|
|
esp_openthread_task_switching_lock_acquire(portMAX_DELAY);
|
|
otMessage *message = otIp6NewMessage(esp_openthread_get_instance(), NULL);
|
|
if (message == NULL) {
|
|
ESP_LOGE(OT_PLAT_LOG_TAG, "Failed to allocate OpenThread message");
|
|
ExitNow(error = ESP_ERR_NO_MEM);
|
|
}
|
|
|
|
ot_error = otMessageAppend(message, buffer, len);
|
|
if (ot_error != OT_ERROR_NONE) {
|
|
ESP_LOGE(OT_PLAT_LOG_TAG, "Failed to copy to OpenThread message: %s", otThreadErrorToString(ot_error));
|
|
ExitNow(error = ESP_ERR_NO_MEM);
|
|
}
|
|
|
|
if (xQueueSend(s_packet_queue, &message, 0) != pdTRUE) {
|
|
ESP_LOGE(OT_PLAT_LOG_TAG, "Failed to send to Thread netif: packet queue full");
|
|
ExitNow(error = ESP_ERR_NO_MEM, ot_error = OT_ERROR_NO_BUFS);
|
|
}
|
|
VerifyOrExit(notify_packets_pending() == ESP_OK, error = ESP_FAIL);
|
|
|
|
exit:
|
|
if (ot_error != OT_ERROR_NONE && message != NULL) {
|
|
otMessageFree(message);
|
|
}
|
|
esp_openthread_task_switching_lock_release();
|
|
return error;
|
|
}
|
|
|
|
static esp_err_t register_openthread_event_handlers(esp_netif_t *esp_netif)
|
|
{
|
|
ESP_RETURN_ON_ERROR(
|
|
esp_event_handler_register(OPENTHREAD_EVENT, OPENTHREAD_EVENT_START, esp_netif_action_start, esp_netif),
|
|
OT_PLAT_LOG_TAG, "OpenThread start event register failed");
|
|
ESP_RETURN_ON_ERROR(
|
|
esp_event_handler_register(OPENTHREAD_EVENT, OPENTHREAD_EVENT_STOP, esp_netif_action_stop, esp_netif),
|
|
OT_PLAT_LOG_TAG, "OpenThread stop event register failed");
|
|
ESP_RETURN_ON_ERROR(
|
|
esp_event_handler_register(OPENTHREAD_EVENT, OPENTHREAD_EVENT_IF_UP, esp_netif_action_connected, esp_netif),
|
|
OT_PLAT_LOG_TAG, "OpenThread interface up event register failed");
|
|
ESP_RETURN_ON_ERROR(esp_event_handler_register(OPENTHREAD_EVENT, OPENTHREAD_EVENT_IF_DOWN,
|
|
esp_netif_action_disconnected, esp_netif),
|
|
OT_PLAT_LOG_TAG, "OpenThread interface down event register failed");
|
|
ESP_RETURN_ON_ERROR(esp_event_handler_register(OPENTHREAD_EVENT, OPENTHREAD_EVENT_GOT_IP6,
|
|
esp_netif_action_add_ip6_address, esp_netif),
|
|
OT_PLAT_LOG_TAG, "OpenThread interface got ip6 event register failed");
|
|
ESP_RETURN_ON_ERROR(esp_event_handler_register(OPENTHREAD_EVENT, OPENTHREAD_EVENT_LOST_IP6,
|
|
esp_netif_action_remove_ip6_address, esp_netif),
|
|
OT_PLAT_LOG_TAG, "OpenThread interface remove ip6 event register failed");
|
|
ESP_RETURN_ON_ERROR(esp_event_handler_register(OPENTHREAD_EVENT, OPENTHREAD_EVENT_MULTICAST_GROUP_JOIN,
|
|
esp_netif_action_join_ip6_multicast_group, esp_netif),
|
|
OT_PLAT_LOG_TAG, "OpenThread interface join ip6 multicast group event register failed");
|
|
ESP_RETURN_ON_ERROR(esp_event_handler_register(OPENTHREAD_EVENT, OPENTHREAD_EVENT_MULTICAST_GROUP_LEAVE,
|
|
esp_netif_action_leave_ip6_multicast_group, esp_netif),
|
|
OT_PLAT_LOG_TAG, "OpenThread interface leave ip6 multicast group event register failed");
|
|
return ESP_OK;
|
|
}
|
|
|
|
static void unregister_openthread_event_handlers(void)
|
|
{
|
|
esp_event_handler_unregister(OPENTHREAD_EVENT, OPENTHREAD_EVENT_START, esp_netif_action_start);
|
|
esp_event_handler_unregister(OPENTHREAD_EVENT, OPENTHREAD_EVENT_STOP, esp_netif_action_stop);
|
|
esp_event_handler_unregister(OPENTHREAD_EVENT, OPENTHREAD_EVENT_IF_UP, esp_netif_action_connected);
|
|
esp_event_handler_unregister(OPENTHREAD_EVENT, OPENTHREAD_EVENT_IF_DOWN, esp_netif_action_disconnected);
|
|
esp_event_handler_unregister(OPENTHREAD_EVENT, OPENTHREAD_EVENT_GOT_IP6, esp_netif_action_add_ip6_address);
|
|
esp_event_handler_unregister(OPENTHREAD_EVENT, OPENTHREAD_EVENT_LOST_IP6, esp_netif_action_remove_ip6_address);
|
|
esp_event_handler_unregister(OPENTHREAD_EVENT, OPENTHREAD_EVENT_MULTICAST_GROUP_JOIN,
|
|
esp_netif_action_join_ip6_multicast_group);
|
|
esp_event_handler_unregister(OPENTHREAD_EVENT, OPENTHREAD_EVENT_MULTICAST_GROUP_LEAVE,
|
|
esp_netif_action_leave_ip6_multicast_group);
|
|
}
|
|
|
|
static esp_err_t openthread_netif_post_attach(esp_netif_t *esp_netif, void *args)
|
|
{
|
|
esp_netif_driver_base_t *base = (esp_netif_driver_base_t *)args;
|
|
base->netif = esp_netif;
|
|
|
|
// set driver related config to esp-netif
|
|
esp_netif_driver_ifconfig_t driver_ifconfig = {
|
|
.handle = &s_openthread_netif_glue, .transmit = openthread_netif_transmit, .driver_free_rx_buffer = NULL};
|
|
|
|
ESP_ERROR_CHECK(esp_netif_set_driver_config(esp_netif, &driver_ifconfig));
|
|
|
|
ESP_LOGI(OT_PLAT_LOG_TAG, "OpenThread attached to netif");
|
|
esp_err_t error = register_openthread_event_handlers(esp_netif);
|
|
s_openthread_netif = esp_netif;
|
|
if (error == ESP_OK) {
|
|
error = esp_event_post(OPENTHREAD_EVENT, OPENTHREAD_EVENT_START, NULL, 0, 0);
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
void *esp_openthread_netif_glue_init(const esp_openthread_platform_config_t *config)
|
|
{
|
|
otInstance *instance = esp_openthread_get_instance();
|
|
esp_err_t error = ESP_OK;
|
|
|
|
if (instance == NULL || s_packet_queue || s_openthread_netif_glue.event_fd >= 0) {
|
|
return NULL;
|
|
}
|
|
ESP_RETURN_ON_FALSE(otSetStateChangedCallback(instance, esp_openthread_netif_glue_state_callback, NULL) ==
|
|
OT_ERROR_NONE,
|
|
NULL, OT_PLAT_LOG_TAG, "Failed to install netif glue state callback");
|
|
|
|
s_packet_queue = xQueueCreate(config->port_config.netif_queue_size, sizeof(otMessage *));
|
|
if (s_packet_queue == NULL) {
|
|
ESP_LOGE(OT_PLAT_LOG_TAG, "Failed to allocate Thread netif packet queue");
|
|
ExitNow(error = ESP_ERR_NO_MEM);
|
|
}
|
|
|
|
otIp6SetAddressCallback(instance, process_thread_address, instance);
|
|
otIp6SetReceiveCallback(instance, process_thread_receive, instance);
|
|
otIp6SetReceiveFilterEnabled(instance, true);
|
|
otIcmp6SetEchoMode(instance, OT_ICMP6_ECHO_HANDLER_DISABLED);
|
|
|
|
s_openthread_netif_glue.event_fd = eventfd(0, 0);
|
|
if (s_openthread_netif_glue.event_fd < 0) {
|
|
ESP_LOGE(OT_PLAT_LOG_TAG, "Failed to create event fd for Thread netif");
|
|
ExitNow(error = ESP_FAIL);
|
|
}
|
|
s_openthread_netif_glue.base.post_attach = openthread_netif_post_attach;
|
|
error = esp_openthread_platform_workflow_register(&esp_openthread_netif_glue_update,
|
|
&esp_openthread_netif_glue_process, netif_glue_workflow);
|
|
exit:
|
|
if (error != ESP_OK) {
|
|
return NULL;
|
|
}
|
|
|
|
return &s_openthread_netif_glue.base;
|
|
}
|
|
|
|
void esp_openthread_netif_glue_deinit(void)
|
|
{
|
|
otInstance *instance = esp_openthread_get_instance();
|
|
otIp6SetAddressCallback(instance, NULL, NULL);
|
|
otIp6SetReceiveCallback(instance, NULL, NULL);
|
|
if (s_packet_queue) {
|
|
vQueueDelete(s_packet_queue);
|
|
s_packet_queue = NULL;
|
|
}
|
|
if (s_openthread_netif_glue.event_fd >= 0) {
|
|
close(s_openthread_netif_glue.event_fd);
|
|
s_openthread_netif_glue.event_fd = -1;
|
|
}
|
|
if (esp_event_post(OPENTHREAD_EVENT, OPENTHREAD_EVENT_STOP, NULL, 0, 0) != ESP_OK) {
|
|
ESP_LOGE(OT_PLAT_LOG_TAG, "Failed to stop OpenThread netif");
|
|
}
|
|
s_openthread_netif = NULL;
|
|
unregister_openthread_event_handlers();
|
|
esp_openthread_platform_workflow_unregister(netif_glue_workflow);
|
|
}
|
|
|
|
void esp_openthread_netif_glue_update(esp_openthread_mainloop_context_t *mainloop)
|
|
{
|
|
if (s_openthread_netif_glue.event_fd >= 0) {
|
|
FD_SET(s_openthread_netif_glue.event_fd, &mainloop->read_fds);
|
|
if (s_openthread_netif_glue.event_fd > mainloop->max_fd) {
|
|
mainloop->max_fd = s_openthread_netif_glue.event_fd;
|
|
}
|
|
}
|
|
}
|
|
|
|
esp_err_t esp_openthread_netif_glue_process(otInstance *instance, const esp_openthread_mainloop_context_t *context)
|
|
{
|
|
if (s_openthread_netif_glue.event_fd >= 0 && FD_ISSET(s_openthread_netif_glue.event_fd, &context->read_fds)) {
|
|
return process_thread_transmit(instance);
|
|
}
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_netif_t *esp_openthread_get_netif(void)
|
|
{
|
|
return s_openthread_netif;
|
|
}
|