Просмотр исходного кода

feat: enable eoe with lwip

Signed-off-by: sakumisu <1203593632@qq.com>
sakumisu 2 месяцев назад
Родитель
Сommit
68c6303eb5

+ 1 - 0
CMakeLists.txt

@@ -9,6 +9,7 @@ if(CONFIG_CHERRYECAT)
         ${CMAKE_CURRENT_LIST_DIR}/src/ec_coe.c
         ${CMAKE_CURRENT_LIST_DIR}/src/ec_common.c
         ${CMAKE_CURRENT_LIST_DIR}/src/ec_datagram.c
+        ${CMAKE_CURRENT_LIST_DIR}/src/ec_eoe.c
         ${CMAKE_CURRENT_LIST_DIR}/src/ec_foe.c
         ${CMAKE_CURRENT_LIST_DIR}/src/ec_mailbox.c
         ${CMAKE_CURRENT_LIST_DIR}/src/ec_master.c

+ 1 - 0
SConscript

@@ -12,6 +12,7 @@ src += Glob('src/ec_cmd.c')
 src += Glob('src/ec_coe.c')
 src += Glob('src/ec_common.c')
 src += Glob('src/ec_datagram.c')
+src += Glob('src/ec_eoe.c')
 src += Glob('src/ec_foe.c')
 src += Glob('src/ec_mailbox.c')
 src += Glob('src/ec_master.c')

+ 11 - 1
cherryec_config_template.h

@@ -42,7 +42,7 @@
 #endif
 
 #ifndef CONFIG_EC_SCAN_PRIO
-#define CONFIG_EC_SCAN_PRIO 1
+#define CONFIG_EC_SCAN_PRIO 10
 #endif
 
 #ifndef CONFIG_EC_SCAN_STACKSIZE
@@ -80,4 +80,14 @@
 
 // #define CONFIG_EC_FOE
 
+// #define CONFIG_EC_EOE
+
+#ifndef CONFIG_EC_EOE_PRIO
+#define CONFIG_EC_EOE_PRIO 1
+#endif
+
+#ifndef CONFIG_EC_EOE_STACKSIZE
+#define CONFIG_EC_EOE_STACKSIZE 4096
+#endif
+
 #endif

+ 10 - 3
demo/hpmicro/CMakeLists.txt

@@ -22,13 +22,18 @@ set(APP_USE_ENET_PHY_RTL8201 1)
 set(CONFIG_CHERRYECAT 1)
 set(CONFIG_CHERRYECAT_OSAL "freertos")
 
-if(NOT (HPM_BUILD_TYPE STREQUAL "ram"))
-message(FATAL_ERROR "Only support ram build for demo")
-endif()
+# if(NOT (HPM_BUILD_TYPE STREQUAL "ram"))
+# message(FATAL_ERROR "Only support ram build for demo")
+# endif()
 
 #Set CONFIG_FREERTOS_TIMER_RESOURCE_GPTMR to use GPTMR as system's tick source
 #set(CONFIG_FREERTOS_TIMER_RESOURCE_GPTMR 1)
 
+# set(CONFIG_LWIP 1)
+# set(CONFIG_LWIP_STRERR 1)
+# set(CONFIG_LWIP_NETDB 1)
+# set(CONFIG_LWIP_SOCKET_API 1)
+
 find_package(hpm-sdk REQUIRED HINTS $ENV{HPM_SDK_BASE})
 
 project(cherryec)
@@ -37,12 +42,14 @@ sdk_compile_definitions(-D__freertos_irq_stack_top=_stack)
 sdk_compile_definitions(-DCONFIG_FREERTOS=1)
 sdk_compile_definitions(-DUSE_NONVECTOR_MODE=1)
 sdk_compile_definitions(-DDISABLE_IRQ_PREEMPTIVE=1)
+sdk_compile_definitions(-DLWIP_TIMEVAL_PRIVATE=0)
 
 sdk_compile_options("-O2")
 
 sdk_inc(.)
 sdk_inc(inc)
 sdk_app_src(main.c)
+# sdk_app_src(arch/sys_arch.c tcp_client.c utils_getopt.c ping.c)
 
 add_subdirectory(../.. cherryec)
 generate_ses_project()

+ 103 - 0
demo/hpmicro/arch/cc.h

@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+/*
+ * Copyright (c) 2021-2025 HPMicro
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __CC_H__
+#define __CC_H__
+
+#include <stdio.h>
+
+#define U16_F "hu"
+#define S16_F "d"
+#define X16_F "hx"
+#define U32_F "u"
+#define S32_F "d"
+#define X32_F "x"
+#define SZT_F "uz"
+
+/* define compiler specific symbols */
+#if defined (__ICCARM__)
+
+#define PACK_STRUCT_BEGIN
+#define PACK_STRUCT_STRUCT
+#define PACK_STRUCT_END
+#define PACK_STRUCT_FIELD(x) x
+#define PACK_STRUCT_USE_INCLUDES
+
+#elif defined (__CC_ARM)
+
+#define PACK_STRUCT_BEGIN __packed
+#define PACK_STRUCT_STRUCT
+#define PACK_STRUCT_END
+#define PACK_STRUCT_FIELD(x) x
+
+#elif defined (__GNUC__)
+
+#include <sys/time.h>
+#include <time.h>
+
+#define PACK_STRUCT_BEGIN
+#define PACK_STRUCT_STRUCT __attribute__ ((__packed__))
+#define PACK_STRUCT_END
+#define PACK_STRUCT_FIELD(x) x
+
+#elif defined(__ICCRISCV__)
+
+#include <time.h>
+#define PACK_STRUCT_BEGIN
+#define PACK_STRUCT_STRUCT __attribute__ ((__packed__))
+#define PACK_STRUCT_END
+#define PACK_STRUCT_FIELD(x) x
+typedef unsigned long clockid_t;
+
+#elif defined (__TASKING__)
+
+#define PACK_STRUCT_BEGIN
+#define PACK_STRUCT_STRUCT
+#define PACK_STRUCT_END
+#define PACK_STRUCT_FIELD(x) x
+
+#endif
+
+#define LWIP_PLATFORM_ASSERT(x) printf(x)
+
+#ifndef LWIP_MEM_SECTION
+#define LWIP_MEM_SECTION ".fast_ram.non_init"
+#endif
+
+#endif /* __CC_H__ */

+ 618 - 0
demo/hpmicro/arch/sys_arch.c

@@ -0,0 +1,618 @@
+/*
+ * Copyright (c) 2017 Simon Goldschmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt <goldsimon@gmx.de>
+ *
+ */
+
+/*
+ * Copyright (c) 2021-2025 HPMicro
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+/* lwIP includes. */
+#include "lwip/debug.h"
+#include "lwip/def.h"
+#include "lwip/sys.h"
+#include "lwip/mem.h"
+#include "lwip/stats.h"
+#include "lwip/tcpip.h"
+
+#if !NO_SYS
+#include "FreeRTOS.h"
+#include "semphr.h"
+#endif
+
+#if !NO_SYS
+#include "sys_arch.h"
+#endif
+
+int errno;
+#if !NO_SYS
+/** Set this to 1 if you want the stack size passed to sys_thread_new() to be
+ * interpreted as number of stack words (FreeRTOS-like).
+ * Default is that they are interpreted as byte count (lwIP-like).
+ */
+#ifndef LWIP_FREERTOS_THREAD_STACKSIZE_IS_STACKWORDS
+#define LWIP_FREERTOS_THREAD_STACKSIZE_IS_STACKWORDS  0
+#endif
+
+/** Set this to 1 to use a mutex for SYS_ARCH_PROTECT() critical regions.
+ * Default is 0 and locks interrupts/scheduler for SYS_ARCH_PROTECT().
+ */
+#ifndef LWIP_FREERTOS_SYS_ARCH_PROTECT_USES_MUTEX
+#define LWIP_FREERTOS_SYS_ARCH_PROTECT_USES_MUTEX     0
+#endif
+
+/** Set this to 1 to include a sanity check that SYS_ARCH_PROTECT() and
+ * SYS_ARCH_UNPROTECT() are called matching.
+ */
+#ifndef LWIP_FREERTOS_SYS_ARCH_PROTECT_SANITY_CHECK
+#define LWIP_FREERTOS_SYS_ARCH_PROTECT_SANITY_CHECK   0
+#endif
+
+/** Set this to 1 to let sys_mbox_free check that queues are empty when freed */
+#ifndef LWIP_FREERTOS_CHECK_QUEUE_EMPTY_ON_FREE
+#define LWIP_FREERTOS_CHECK_QUEUE_EMPTY_ON_FREE       0
+#endif
+
+/** Set this to 1 to enable core locking check functions in this port.
+ * For this to work, you'll have to define LWIP_ASSERT_CORE_LOCKED()
+ * and LWIP_MARK_TCPIP_THREAD() correctly in your lwipopts.h! */
+#ifndef LWIP_FREERTOS_CHECK_CORE_LOCKING
+#define LWIP_FREERTOS_CHECK_CORE_LOCKING              1
+#endif
+
+/** Set this to 0 to implement sys_now() yourself, e.g. using a hw timer.
+ * Default is 1, where FreeRTOS ticks are used to calculate back to ms.
+ */
+#ifndef LWIP_FREERTOS_SYS_NOW_FROM_FREERTOS
+#define LWIP_FREERTOS_SYS_NOW_FROM_FREERTOS           1
+#endif
+
+#if !configSUPPORT_DYNAMIC_ALLOCATION
+# error "lwIP FreeRTOS port requires configSUPPORT_DYNAMIC_ALLOCATION"
+#endif
+#if !INCLUDE_vTaskDelay
+# error "lwIP FreeRTOS port requires INCLUDE_vTaskDelay"
+#endif
+#if !INCLUDE_vTaskSuspend
+# error "lwIP FreeRTOS port requires INCLUDE_vTaskSuspend"
+#endif
+#if LWIP_FREERTOS_SYS_ARCH_PROTECT_USES_MUTEX || !LWIP_COMPAT_MUTEX
+#if !configUSE_MUTEXES
+# error "lwIP FreeRTOS port requires configUSE_MUTEXES"
+#endif
+#endif
+
+#if SYS_LIGHTWEIGHT_PROT && LWIP_FREERTOS_SYS_ARCH_PROTECT_USES_MUTEX
+static SemaphoreHandle_t sys_arch_protect_mutex;
+#endif
+#if SYS_LIGHTWEIGHT_PROT && LWIP_FREERTOS_SYS_ARCH_PROTECT_SANITY_CHECK
+static sys_prot_t sys_arch_protect_nesting;
+#endif
+
+/* Initialize this module (see description in sys.h) */
+void
+sys_init(void)
+{
+#if SYS_LIGHTWEIGHT_PROT && LWIP_FREERTOS_SYS_ARCH_PROTECT_USES_MUTEX
+    /* initialize sys_arch_protect global mutex */
+    sys_arch_protect_mutex = xSemaphoreCreateRecursiveMutex();
+    LWIP_ASSERT("failed to create sys_arch_protect mutex",
+    sys_arch_protect_mutex != NULL);
+#endif /* SYS_LIGHTWEIGHT_PROT && LWIP_FREERTOS_SYS_ARCH_PROTECT_USES_MUTEX */
+}
+
+#if configUSE_16_BIT_TICKS == 1
+#error This port requires 32 bit ticks or timer overflow will fail
+#endif
+
+#if LWIP_FREERTOS_SYS_NOW_FROM_FREERTOS
+u32_t sys_now(void)
+{
+    return xTaskGetTickCount() * portTICK_PERIOD_MS;
+}
+#else
+u32_t sys_now(void)
+{
+    return 0;
+}
+#endif
+
+u32_t sys_jiffies(void)
+{
+    return xTaskGetTickCount();
+}
+
+#if SYS_LIGHTWEIGHT_PROT
+
+sys_prot_t sys_arch_protect(void)
+{
+#if LWIP_FREERTOS_SYS_ARCH_PROTECT_USES_MUTEX
+    BaseType_t ret;
+    LWIP_ASSERT("sys_arch_protect_mutex != NULL", sys_arch_protect_mutex != NULL);
+
+    ret = xSemaphoreTakeRecursive(sys_arch_protect_mutex, portMAX_DELAY);
+    LWIP_ASSERT("sys_arch_protect failed to take the mutex", ret == pdTRUE);
+    #else /* LWIP_FREERTOS_SYS_ARCH_PROTECT_USES_MUTEX */
+    taskENTER_CRITICAL();
+    #endif /* LWIP_FREERTOS_SYS_ARCH_PROTECT_USES_MUTEX */
+    #if LWIP_FREERTOS_SYS_ARCH_PROTECT_SANITY_CHECK
+    {
+        /* every nested call to sys_arch_protect() returns an increased number */
+        sys_prot_t ret = sys_arch_protect_nesting;
+        sys_arch_protect_nesting++;
+        LWIP_ASSERT("sys_arch_protect overflow", sys_arch_protect_nesting > ret);
+        return ret;
+    }
+#else
+    return 1;
+#endif
+}
+
+void sys_arch_unprotect(sys_prot_t pval)
+{
+#if LWIP_FREERTOS_SYS_ARCH_PROTECT_USES_MUTEX
+    BaseType_t ret;
+#endif
+#if LWIP_FREERTOS_SYS_ARCH_PROTECT_SANITY_CHECK
+    LWIP_ASSERT("unexpected sys_arch_protect_nesting", sys_arch_protect_nesting > 0);
+    sys_arch_protect_nesting--;
+    LWIP_ASSERT("unexpected sys_arch_protect_nesting", sys_arch_protect_nesting == pval);
+#endif
+
+#if LWIP_FREERTOS_SYS_ARCH_PROTECT_USES_MUTEX
+    LWIP_ASSERT("sys_arch_protect_mutex != NULL", sys_arch_protect_mutex != NULL);
+
+    ret = xSemaphoreGiveRecursive(sys_arch_protect_mutex);
+    LWIP_ASSERT("sys_arch_unprotect failed to give the mutex", ret == pdTRUE);
+#else /* LWIP_FREERTOS_SYS_ARCH_PROTECT_USES_MUTEX */
+    taskEXIT_CRITICAL();
+#endif /* LWIP_FREERTOS_SYS_ARCH_PROTECT_USES_MUTEX */
+    LWIP_UNUSED_ARG(pval);
+}
+
+#endif /* SYS_LIGHTWEIGHT_PROT */
+
+void sys_arch_msleep(u32_t delay_ms)
+{
+    TickType_t delay_ticks = delay_ms / portTICK_RATE_MS;
+    vTaskDelay(delay_ticks);
+}
+
+#if !LWIP_COMPAT_MUTEX
+
+/* Create a new mutex*/
+err_t sys_mutex_new(sys_mutex_t *mutex)
+{
+    LWIP_ASSERT("mutex != NULL", mutex != NULL);
+
+    mutex->mut = xSemaphoreCreateRecursiveMutex();
+    if (mutex->mut == NULL) {
+        SYS_STATS_INC(mutex.err);
+        return ERR_MEM;
+    }
+    SYS_STATS_INC_USED(mutex);
+    return ERR_OK;
+}
+
+void sys_mutex_lock(sys_mutex_t *mutex)
+{
+    BaseType_t ret;
+    LWIP_ASSERT("mutex != NULL", mutex != NULL);
+    LWIP_ASSERT("mutex->mut != NULL", mutex->mut != NULL); /* NOLINT */
+
+    ret = xSemaphoreTakeRecursive(mutex->mut, portMAX_DELAY);
+    LWIP_ASSERT("failed to take the mutex", ret == pdTRUE);
+}
+
+void sys_mutex_unlock(sys_mutex_t *mutex)
+{
+    BaseType_t ret;
+    LWIP_ASSERT("mutex != NULL", mutex != NULL);
+    LWIP_ASSERT("mutex->mut != NULL", mutex->mut != NULL); /* NOLINT */
+
+    ret = xSemaphoreGiveRecursive(mutex->mut);
+    LWIP_ASSERT("failed to give the mutex", ret == pdTRUE);
+}
+
+void sys_mutex_free(sys_mutex_t *mutex)
+{
+    LWIP_ASSERT("mutex != NULL", mutex != NULL);
+    LWIP_ASSERT("mutex->mut != NULL", mutex->mut != NULL); /* NOLINT */
+
+    SYS_STATS_DEC(mutex.used);
+    vSemaphoreDelete(mutex->mut);
+    mutex->mut = NULL;
+}
+
+#endif /* !LWIP_COMPAT_MUTEX */
+
+err_t sys_sem_new(sys_sem_t *sem, u8_t initial_count)
+{
+    LWIP_ASSERT("sem != NULL", sem != NULL);
+    LWIP_ASSERT("initial_count invalid (not 0 or 1)",
+        (initial_count == 0) || (initial_count == 1));
+
+    sem->sem = xSemaphoreCreateBinary();
+    if (sem->sem == NULL) {
+        SYS_STATS_INC(sem.err);
+        return ERR_MEM;
+    }
+    SYS_STATS_INC_USED(sem);
+
+    if (initial_count == 1) {
+        BaseType_t ret = xSemaphoreGive(sem->sem);
+        LWIP_ASSERT("sys_sem_new: initial give failed", ret == pdTRUE);
+    }
+    return ERR_OK;
+}
+
+void sys_sem_signal(sys_sem_t *sem)
+{
+    BaseType_t ret;
+    LWIP_ASSERT("sem != NULL", sem != NULL);
+    LWIP_ASSERT("sem->sem != NULL", sem->sem != NULL); /* NOLINT */
+
+    ret = xSemaphoreGive(sem->sem);
+    /* queue full is OK, this is a signal only... */
+    LWIP_ASSERT("sys_sem_signal: sane return value",
+        (ret == pdTRUE) || (ret == errQUEUE_FULL));
+}
+
+u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout_ms)
+{
+    BaseType_t ret;
+    LWIP_ASSERT("sem != NULL", sem != NULL);
+    LWIP_ASSERT("sem->sem != NULL", sem->sem != NULL); /* NOLINT */
+
+    if (!timeout_ms) {
+        /* wait infinite */
+        ret = xSemaphoreTake(sem->sem, portMAX_DELAY);
+        LWIP_ASSERT("taking semaphore failed", ret == pdTRUE);
+    } else {
+        TickType_t timeout_ticks = timeout_ms / portTICK_RATE_MS;
+        ret = xSemaphoreTake(sem->sem, timeout_ticks);
+        if (ret == errQUEUE_EMPTY) {
+        /* timed out */
+        return SYS_ARCH_TIMEOUT;
+        }
+        LWIP_ASSERT("taking semaphore failed", ret == pdTRUE);
+    }
+
+    /* Old versions of lwIP required us to return the time waited.
+        This is not the case any more. Just returning != SYS_ARCH_TIMEOUT
+        here is enough. */
+  return 1;
+}
+
+void sys_sem_free(sys_sem_t *sem)
+{
+    LWIP_ASSERT("sem != NULL", sem != NULL);
+    LWIP_ASSERT("sem->sem != NULL", sem->sem != NULL);  /* NOLINT */
+
+    SYS_STATS_DEC(sem.used);
+    vSemaphoreDelete(sem->sem);
+    sem->sem = NULL;
+}
+
+err_t sys_mbox_new(sys_mbox_t *mbox, int size)
+{
+    LWIP_ASSERT("mbox != NULL", mbox != NULL); /* NOLINT */
+    LWIP_ASSERT("size > 0", size > 0);
+
+    mbox->mbx = xQueueCreate((UBaseType_t)size, sizeof(void *));
+    if (mbox->mbx == NULL) {
+        SYS_STATS_INC(mbox.err);
+        return ERR_MEM;
+    }
+    SYS_STATS_INC_USED(mbox);
+    return ERR_OK;
+}
+
+void sys_mbox_post(sys_mbox_t *mbox, void *msg)
+{
+    BaseType_t ret;
+    LWIP_ASSERT("mbox != NULL", mbox != NULL);
+    LWIP_ASSERT("mbox->mbx != NULL", mbox->mbx != NULL);  /* NOLINT */
+
+    ret = xQueueSendToBack(mbox->mbx, &msg, portMAX_DELAY);
+    LWIP_ASSERT("mbox post failed", ret == pdTRUE);
+}
+
+err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg)
+{
+    BaseType_t ret;
+    LWIP_ASSERT("mbox != NULL", mbox != NULL);
+    LWIP_ASSERT("mbox->mbx != NULL", mbox->mbx != NULL); /* NOLINT */
+
+    ret = xQueueSendToBack(mbox->mbx, &msg, 0);
+    if (ret == pdTRUE) {
+        return ERR_OK;
+    } else {
+        LWIP_ASSERT("mbox trypost failed", ret == errQUEUE_FULL);
+        SYS_STATS_INC(mbox.err);
+        return ERR_MEM;
+    }
+}
+
+err_t sys_mbox_trypost_fromisr(sys_mbox_t *mbox, void *msg)
+{
+    BaseType_t ret;
+    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
+    LWIP_ASSERT("mbox != NULL", mbox != NULL);
+    LWIP_ASSERT("mbox->mbx != NULL", mbox->mbx != NULL); /* NOLINT */
+
+    ret = xQueueSendToBackFromISR(mbox->mbx, &msg, &xHigherPriorityTaskWoken);
+    if (ret == pdTRUE) {
+        if (xHigherPriorityTaskWoken == pdTRUE) {
+        return ERR_NEED_SCHED;
+        }
+        return ERR_OK;
+    } else {
+        LWIP_ASSERT("mbox trypost failed", ret == errQUEUE_FULL);
+        SYS_STATS_INC(mbox.err);
+        return ERR_MEM;
+    }
+}
+
+u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout_ms)
+{
+    BaseType_t ret;
+    void *msg_dummy;
+    LWIP_ASSERT("mbox != NULL", mbox != NULL);
+    LWIP_ASSERT("mbox->mbx != NULL", mbox->mbx != NULL); /* NOLINT */
+
+    if (!msg) {
+        msg = &msg_dummy;
+    }
+
+    if (!timeout_ms) {
+        /* wait infinite */
+        ret = xQueueReceive(mbox->mbx, &(*msg), portMAX_DELAY);
+        LWIP_ASSERT("mbox fetch failed", ret == pdTRUE);
+    } else {
+        TickType_t timeout_ticks = timeout_ms / portTICK_RATE_MS;
+        ret = xQueueReceive(mbox->mbx, &(*msg), timeout_ticks);
+        if (ret == errQUEUE_EMPTY) {
+        /* timed out */
+        *msg = NULL;
+        return SYS_ARCH_TIMEOUT;
+        }
+        LWIP_ASSERT("mbox fetch failed", ret == pdTRUE);
+    }
+
+    /* Old versions of lwIP required us to return the time waited.
+        This is not the case any more. Just returning != SYS_ARCH_TIMEOUT
+        here is enough. */
+    return 1;
+}
+
+u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg)
+{
+    BaseType_t ret;
+    void *msg_dummy;
+    LWIP_ASSERT("mbox != NULL", mbox != NULL);
+    LWIP_ASSERT("mbox->mbx != NULL", mbox->mbx != NULL); /* NOLINT */
+
+    if (!msg) {
+        msg = &msg_dummy;
+    }
+
+    ret = xQueueReceive(mbox->mbx, &(*msg), 0);
+    if (ret == errQUEUE_EMPTY) {
+        *msg = NULL;
+        return SYS_MBOX_EMPTY;
+    }
+    LWIP_ASSERT("mbox fetch failed", ret == pdTRUE);
+
+    /* Old versions of lwIP required us to return the time waited.
+        This is not the case any more. Just returning != SYS_ARCH_TIMEOUT
+        here is enough. */
+    return 1;
+}
+
+void sys_mbox_free(sys_mbox_t *mbox)
+{
+    LWIP_ASSERT("mbox != NULL", mbox != NULL);
+    LWIP_ASSERT("mbox->mbx != NULL", mbox->mbx != NULL); /* NOLINT */
+
+#if LWIP_FREERTOS_CHECK_QUEUE_EMPTY_ON_FREE
+    {
+        UBaseType_t msgs_waiting = uxQueueMessagesWaiting(mbox->mbx);
+        LWIP_ASSERT("mbox quence not empty", msgs_waiting == 0);
+
+        if (msgs_waiting != 0) {
+        SYS_STATS_INC(mbox.err);
+        }
+    }
+#endif
+
+    vQueueDelete(mbox->mbx);
+
+    SYS_STATS_DEC(mbox.used);
+}
+
+sys_thread_t sys_thread_new(const char *name, lwip_thread_fn thread, void *arg, int stacksize, int prio)
+{
+    TaskHandle_t rtos_task;
+    BaseType_t ret;
+    sys_thread_t lwip_thread;
+    size_t rtos_stacksize;
+
+    LWIP_ASSERT("invalid stacksize", stacksize > 0);
+#if LWIP_FREERTOS_THREAD_STACKSIZE_IS_STACKWORDS
+    rtos_stacksize = (size_t)stacksize;
+#else
+    rtos_stacksize = (size_t)stacksize / sizeof(StackType_t);
+#endif
+
+    /* lwIP's lwip_thread_fn matches FreeRTOS' TaskFunction_t, so we can pass the
+        thread function without adaption here. */
+    ret = xTaskCreate(thread, name, (configSTACK_DEPTH_TYPE)rtos_stacksize, arg, prio, &rtos_task);
+    LWIP_ASSERT("task creation failed", ret == pdTRUE);
+
+    lwip_thread.thread_handle = rtos_task;
+    return lwip_thread;
+}
+
+#if LWIP_NETCONN_SEM_PER_THREAD
+#if configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0
+
+sys_sem_t *sys_arch_netconn_sem_get(void)
+{
+    void *ret;
+    TaskHandle_t task = xTaskGetCurrentTaskHandle();
+    LWIP_ASSERT("task != NULL", task != NULL);
+
+    ret = pvTaskGetThreadLocalStoragePointer(task, 0);
+    return ret;
+}
+
+void sys_arch_netconn_sem_alloc(void)
+{
+    void *ret;
+    TaskHandle_t task = xTaskGetCurrentTaskHandle();
+    LWIP_ASSERT("task != NULL", task != NULL);
+
+    ret = pvTaskGetThreadLocalStoragePointer(task, 0);
+    if (ret == NULL) {
+        sys_sem_t *sem;
+        err_t err;
+        /* need to allocate the memory for this semaphore */
+        sem = mem_malloc(sizeof(sys_sem_t));
+        LWIP_ASSERT("sem != NULL", sem != NULL);
+        err = sys_sem_new(sem, 0);
+        LWIP_ASSERT("err == ERR_OK", err == ERR_OK);
+        LWIP_ASSERT("sem invalid", sys_sem_valid(sem));
+        vTaskSetThreadLocalStoragePointer(task, 0, sem);
+    }
+}
+
+void sys_arch_netconn_sem_free(void)
+{
+    void *ret;
+    TaskHandle_t task = xTaskGetCurrentTaskHandle();
+    LWIP_ASSERT("task != NULL", task != NULL);
+
+    ret = pvTaskGetThreadLocalStoragePointer(task, 0);
+    if (ret != NULL) {
+        sys_sem_t *sem = ret;
+        sys_sem_free(sem);
+        mem_free(sem);
+        vTaskSetThreadLocalStoragePointer(task, 0, NULL);
+    }
+}
+
+#else /* configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 */
+#error LWIP_NETCONN_SEM_PER_THREAD needs configNUM_THREAD_LOCAL_STORAGE_POINTERS
+#endif /* configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 */
+
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+#if LWIP_FREERTOS_CHECK_CORE_LOCKING
+#if LWIP_TCPIP_CORE_LOCKING
+
+/** Flag the core lock held. A counter for recursive locks. */
+static u8_t lwip_core_lock_count;
+static TaskHandle_t lwip_core_lock_holder_thread;
+
+void sys_lock_tcpip_core(void)
+{
+    sys_mutex_lock(&lock_tcpip_core);
+    if (lwip_core_lock_count == 0) {
+        lwip_core_lock_holder_thread = xTaskGetCurrentTaskHandle();
+    }
+    lwip_core_lock_count++;
+}
+
+void sys_unlock_tcpip_core(void)
+{
+    lwip_core_lock_count--;
+    if (lwip_core_lock_count == 0) {
+        lwip_core_lock_holder_thread = 0;
+    }
+    sys_mutex_unlock(&lock_tcpip_core);
+}
+
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+
+#if !NO_SYS
+static TaskHandle_t lwip_tcpip_thread;
+#endif
+
+void sys_mark_tcpip_thread(void)
+{
+#if !NO_SYS
+    lwip_tcpip_thread = xTaskGetCurrentTaskHandle();
+#endif
+}
+
+void sys_check_core_locking(void)
+{
+    /* Embedded systems should check we are NOT in an interrupt context here */
+    /* E.g. core Cortex-M3/M4 ports:
+            configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 );
+
+        Instead, we use more generic FreeRTOS functions here, which should fail from ISR: */
+    taskENTER_CRITICAL();
+    taskEXIT_CRITICAL();
+
+#if !NO_SYS
+    if (lwip_tcpip_thread != 0) {
+        TaskHandle_t current_thread = xTaskGetCurrentTaskHandle();
+
+#if LWIP_TCPIP_CORE_LOCKING
+        LWIP_ASSERT("Function called without core lock",
+                    current_thread == lwip_core_lock_holder_thread && lwip_core_lock_count > 0);
+#else /* LWIP_TCPIP_CORE_LOCKING */
+        LWIP_ASSERT("Function called from wrong thread", current_thread == lwip_tcpip_thread);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+    }
+#endif /* !NO_SYS */
+}
+
+#endif /* LWIP_FREERTOS_CHECK_CORE_LOCKING*/
+
+#else
+static uint32_t sys_tick = 0;
+
+void sys_timer_callback(void)
+{
+    sys_tick++;
+}
+
+u32_t sys_now(void)
+{
+    return (u32_t)sys_tick;
+}
+
+#endif

+ 108 - 0
demo/hpmicro/arch/sys_arch.h

@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2017 Simon Goldschmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmdit <goldsimon@gmx.de>
+ *
+ */
+
+/*
+ * Copyright (c) 2021-2025 HPMicro
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef LWIP_ARCH_SYS_ARCH_H
+#define LWIP_ARCH_SYS_ARCH_H
+
+#include "lwip/opt.h"
+#include "lwip/arch.h"
+
+
+#if !NO_SYS
+/** This is returned by _fromisr() sys functions to tell the outermost function
+ * that a higher priority task was woken and the scheduler needs to be invoked.
+ */
+#define ERR_NEED_SCHED 123
+
+/* This port includes FreeRTOS headers in sys_arch.c only.
+ *  FreeRTOS uses pointers as object types. We use wrapper structs instead of
+ * void pointers directly to get a tiny bit of type safety.
+ */
+
+void sys_arch_msleep(u32_t delay_ms);
+#define sys_msleep(ms) sys_arch_msleep(ms)
+
+#if SYS_LIGHTWEIGHT_PROT
+typedef u32_t sys_prot_t;
+#endif /* SYS_LIGHTWEIGHT_PROT */
+
+
+#if !LWIP_COMPAT_MUTEX
+struct _sys_mut {
+  void *mut;
+};
+typedef struct _sys_mut sys_mutex_t;
+#define sys_mutex_valid_val(mutex)   ((mutex).mut != NULL)
+#define sys_mutex_valid(mutex)       (((mutex) != NULL) && sys_mutex_valid_val(*(mutex)))
+#define sys_mutex_set_invalid(mutex) ((mutex)->mut = NULL)
+#endif /* !LWIP_COMPAT_MUTEX */
+
+struct _sys_sem {
+  void *sem;
+};
+typedef struct _sys_sem sys_sem_t;
+#define sys_sem_valid_val(sema)   ((sema).sem != NULL)
+#define sys_sem_valid(sema)       (((sema) != NULL) && sys_sem_valid_val(*(sema)))
+#define sys_sem_set_invalid(sema) ((sema)->sem = NULL)
+
+struct _sys_mbox {
+  void *mbx;
+};
+typedef struct _sys_mbox sys_mbox_t;
+#define sys_mbox_valid_val(mbox)   ((mbox).mbx != NULL)
+#define sys_mbox_valid(mbox)       (((mbox) != NULL) && sys_mbox_valid_val(*(mbox)))
+#define sys_mbox_set_invalid(mbox) ((mbox)->mbx = NULL)
+
+struct _sys_thread {
+  void *thread_handle;
+};
+typedef struct _sys_thread sys_thread_t;
+
+#if LWIP_NETCONN_SEM_PER_THREAD
+sys_sem_t *sys_arch_netconn_sem_get(void);
+void sys_arch_netconn_sem_alloc(void);
+void sys_arch_netconn_sem_free(void);
+#define LWIP_NETCONN_THREAD_SEM_GET()   sys_arch_netconn_sem_get()
+#define LWIP_NETCONN_THREAD_SEM_ALLOC() sys_arch_netconn_sem_alloc()
+#define LWIP_NETCONN_THREAD_SEM_FREE()  sys_arch_netconn_sem_free()
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+#else
+void sys_timer_callback(void);
+#endif /* !NO_SYS */
+#endif /* LWIP_ARCH_SYS_ARCH_H */

+ 1 - 0
demo/hpmicro/inc/FreeRTOSConfig.h

@@ -42,6 +42,7 @@
 #define configGENERATE_RUN_TIME_STATS           0
 
 #define configUSE_COUNTING_SEMAPHORES           1
+#define configUSE_RECURSIVE_MUTEXES             1
 #define configUSE_MUTEXES                       1
 
 /* Memory allocation definitions. */

+ 11 - 1
demo/hpmicro/inc/ec_config.h

@@ -42,7 +42,7 @@
 #endif
 
 #ifndef CONFIG_EC_SCAN_PRIO
-#define CONFIG_EC_SCAN_PRIO 1
+#define CONFIG_EC_SCAN_PRIO 10
 #endif
 
 #ifndef CONFIG_EC_SCAN_STACKSIZE
@@ -80,4 +80,14 @@
 
 // #define CONFIG_EC_FOE
 
+// #define CONFIG_EC_EOE
+
+#ifndef CONFIG_EC_EOE_PRIO
+#define CONFIG_EC_EOE_PRIO 1
+#endif
+
+#ifndef CONFIG_EC_EOE_STACKSIZE
+#define CONFIG_EC_EOE_STACKSIZE 4096
+#endif
+
 #endif

+ 194 - 0
demo/hpmicro/inc/lwipopts.h

@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * Copyright (c) 2023-2024 HPMicro
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt
+ *
+ */
+#ifndef __LWIPOPTS_H__
+#define __LWIPOPTS_H__
+
+#include <stdint.h>
+
+#define SYS_LIGHTWEIGHT_PROT            0
+#define LWIP_PROVIDE_ERRNO              1
+#define LWIP_RAND                       rand
+
+#define NO_SYS                          0
+#define MEM_ALIGNMENT                   4
+#define LWIP_DNS                        1
+#define LWIP_RAW                        1
+#define LWIP_NETCONN                    1
+#define LWIP_SOCKET                     1
+#define LWIP_DHCP                       0
+#define LWIP_ICMP                       1
+#define ICMP_TTL                        64
+#define LWIP_UDP                        1
+#define LWIP_TCP                        1
+#define TCP_TTL                         255
+#define LWIP_IPV4                       1
+#define LWIP_IPV6                       0
+#define ETH_PAD_SIZE                    0
+#define LWIP_IP_ACCEPT_UDP_PORT(p)      ((p) == PP_NTOHS(67))
+
+#define MEM_SIZE                        (11 * 1024)
+#define TCP_MSS                         (1500 /*mtu*/ - 20 /*iphdr*/ - 20 /*tcphhr*/)
+#define TCP_SND_BUF                     (5 * TCP_MSS)
+
+#define ETHARP_SUPPORT_STATIC_ENTRIES   1
+
+#define LWIP_HTTPD_CGI                  0
+#define LWIP_HTTPD_SSI                  0
+#define LWIP_HTTPD_SSI_INCLUDE_TAG      0
+
+
+/**
+ * MEMP_MEM_MALLOC==1: Use mem_malloc/mem_free instead of the lwip pool allocator.
+ */
+#define MEMP_MEM_MALLOC         1
+
+/* MEMP_NUM_PBUF: the number of memp struct pbufs. If the application
+ *   sends a lot of data out of ROM (or other static memory), this
+ *   should be set high.
+ */
+#define MEMP_NUM_PBUF           20
+
+/* MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One
+ * per active UDP "connection".
+ */
+#define MEMP_NUM_UDP_PCB        6
+
+/* MEMP_NUM_TCP_PCB: the number of simulatenously active TCP
+ *   connections.
+ */
+#define MEMP_NUM_TCP_PCB        10
+
+/* MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP
+ *  connections.
+ */
+#define MEMP_NUM_TCP_PCB_LISTEN 5
+
+/* MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP
+ *  segments.
+ */
+#define MEMP_NUM_TCP_SEG        20
+
+/* MEMP_NUM_SYS_TIMEOUT: the number of simulateously active
+ *  timeouts.
+ */
+#define MEMP_NUM_SYS_TIMEOUT    10
+
+
+/* ---------- Pbuf options ---------- */
+/* PBUF_POOL_SIZE: the number of buffers in the pbuf pool. */
+#define PBUF_POOL_SIZE          40
+
+/* PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. */
+#define PBUF_POOL_BUFSIZE       1600
+
+/* Controls if TCP should queue segments that arrive out of
+ * order. Define to 0 if your device is low on memory.
+ */
+#define TCP_QUEUE_OOSEQ         0
+
+/*  TCP_SND_QUEUELEN: TCP sender buffer space (pbufs). This must be at least
+ * as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work.
+ */
+#define TCP_SND_QUEUELEN        (4 * TCP_SND_BUF/TCP_MSS)
+
+/* TCP receive window. */
+#define TCP_WND                 (2 * TCP_MSS)
+
+/* move from message passing to mutual exclusion (lock netconns) */
+#define LWIP_TCPIP_CORE_LOCKING_INPUT   1
+#define LWIP_TCPIP_CORE_LOCKING         1
+
+/*
+ *  -----------------------------------
+ *  ---------- DEBUG options ----------
+ *  -----------------------------------
+ */
+
+#define LWIP_DEBUG                      1
+
+#ifdef LWIP_DEBUG
+
+#define LWIP_DBG_MIN_LEVEL         0
+
+#define PPP_DEBUG                  LWIP_DBG_OFF
+#define MEM_DEBUG                  LWIP_DBG_OFF
+#define MEMP_DEBUG                 LWIP_DBG_OFF
+#define PBUF_DEBUG                 LWIP_DBG_OFF
+#define API_LIB_DEBUG              LWIP_DBG_OFF
+#define API_MSG_DEBUG              LWIP_DBG_OFF
+#define TCPIP_DEBUG                LWIP_DBG_OFF
+#define NETIF_DEBUG                LWIP_DBG_OFF
+#define SOCKETS_DEBUG              LWIP_DBG_OFF
+#define DNS_DEBUG                  LWIP_DBG_OFF
+#define AUTOIP_DEBUG               LWIP_DBG_OFF
+#define DHCP_DEBUG                 LWIP_DBG_OFF
+#define IP_DEBUG                   LWIP_DBG_OFF
+#define IP_REASS_DEBUG             LWIP_DBG_OFF
+#define ICMP_DEBUG                 LWIP_DBG_OFF
+#define IGMP_DEBUG                 LWIP_DBG_OFF
+#define UDP_DEBUG                  LWIP_DBG_OFF
+#define TCP_DEBUG                  LWIP_DBG_OFF
+#define TCP_INPUT_DEBUG            LWIP_DBG_OFF
+#define TCP_OUTPUT_DEBUG           LWIP_DBG_OFF
+#define TCP_RTO_DEBUG              LWIP_DBG_OFF
+#define TCP_CWND_DEBUG             LWIP_DBG_OFF
+#define TCP_WND_DEBUG              LWIP_DBG_OFF
+#define TCP_FR_DEBUG               LWIP_DBG_OFF
+#define TCP_QLEN_DEBUG             LWIP_DBG_OFF
+#define TCP_RST_DEBUG              LWIP_DBG_OFF
+#define ETHARP_DEBUG               LWIP_DBG_OFF
+
+#endif
+
+#define DHCP_DOES_ARP_CHECK             0
+
+/*
+ *  ---------------------------------
+ *  ---------- OS options ----------
+ *  ---------------------------------
+ */
+
+#define TCPIP_THREAD_NAME              "TCP/IP"
+#define TCPIP_THREAD_STACKSIZE          1500
+#define TCPIP_MBOX_SIZE                 15
+#define DEFAULT_RAW_RECVMBOX_SIZE       1000
+#define DEFAULT_UDP_RECVMBOX_SIZE       100
+#define DEFAULT_TCP_RECVMBOX_SIZE       100
+#define DEFAULT_ACCEPTMBOX_SIZE         1500
+#define DEFAULT_THREAD_STACKSIZE        500
+#define TCPIP_THREAD_PRIO               3
+#define LWIP_SINGLE_NETIF               1
+#define LWIP_COMPAT_MUTEX               0
+
+
+#endif /* __LWIPOPTS_H__ */

+ 27 - 0
demo/hpmicro/inc/tcp_client.h

@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2024 HPMicro
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef TCP_CLIENT_H
+#define TCP_CLIENT_H
+
+#ifndef TCP_DEST_IP
+#define TCP_DEST_IP "192.168.100.10"
+#endif
+
+#ifndef TCP_DEST_PORT
+#define TCP_DEST_PORT (5001U)
+#endif
+
+#if defined(__cplusplus)
+extern "C" {
+#endif /* __cplusplus */
+
+void tcp_client_init(void *arg);
+#if defined(__cplusplus)
+}
+#endif /* __cplusplus */
+
+#endif /* TCP_CLIENT_H */

+ 29 - 6
demo/hpmicro/main.c

@@ -18,19 +18,26 @@
 #include "hpm_gptmr_drv.h"
 #include "cia402_def.h"
 #include "ec_master.h"
+#ifdef CONFIG_EC_EOE
+#include "lwip/tcpip.h"
+#endif
 
 SDK_DECLARE_EXT_ISR_M(BOARD_CONSOLE_UART_IRQ, shell_uart_isr)
 
 #define task_start_PRIORITY (configMAX_PRIORITIES - 2U)
 
-#define MOTOR_MODE_CSV_CSP  0
-#define MOTOR_MODE_CSP      1
-#define MOTOR_MODE_CSV      2
+#define MOTOR_MODE_CSV_CSP 0
+#define MOTOR_MODE_CSP     1
+#define MOTOR_MODE_CSV     2
 
 volatile uint8_t motor_mode = MOTOR_MODE_CSV;
 
 ec_master_t g_ec_master;
 
+#ifdef CONFIG_EC_EOE
+ec_eoe_t g_ec_eoe;
+#endif
+
 static void task_start(void *param);
 
 int main(void)
@@ -43,6 +50,10 @@ int main(void)
         };
     }
 
+#ifdef CONFIG_EC_EOE
+    /* Initialize the LwIP stack */
+    tcpip_init(NULL, NULL);
+#endif
     vTaskStartScheduler();
     printf("Unexpected scheduler exit!\r\n");
     while (1) {
@@ -91,6 +102,9 @@ static void task_start(void *param)
     printf("Enable shell uart interrupt\r\n");
 
     ec_master_cmd_init(&g_ec_master);
+#ifdef CONFIG_EC_EOE
+    ec_master_cmd_eoe_init(&g_ec_eoe);
+#endif
     ec_master_init(&g_ec_master, 0);
 
     printf("Exit start task\r\n");
@@ -192,13 +206,12 @@ int ec_start(int argc, const char **argv)
             case 0x00000001: // DIO
                 g_ec_master.slaves[i].config = &slave_dio_config;
                 break;
-            case 0x00000002: // FOE
-                break;
             case 0x00000003: // CIA402
                 g_ec_master.slaves[i].config = &slave_cia402_config;
                 break;
 
             default:
+                g_ec_master.slaves[i].config = &slave_dio_config;
                 break;
         }
     }
@@ -213,4 +226,14 @@ int ec_stop(int argc, const char **argv)
     ec_master_stop(&g_ec_master);
     return 0;
 }
-CSH_CMD_EXPORT(ec_stop, );
+CSH_CMD_EXPORT(ec_stop, );
+
+#ifdef CONFIG_EC_EOE
+#include "tcp_client.h"
+int tcp_client(int argc, const char **argv)
+{
+    tcp_client_init(&g_ec_eoe.netif);
+    return 0;
+}
+CSH_CMD_EXPORT(tcp_client, );
+#endif

+ 255 - 0
demo/hpmicro/ping.c

@@ -0,0 +1,255 @@
+/*
+ * netutils: ping implementation
+ */
+
+#include <FreeRTOS.h>
+
+#include <lwip/opt.h>
+#include <lwip/init.h>
+#include <lwip/mem.h>
+#include <lwip/icmp.h>
+#include <lwip/netif.h>
+#include <lwip/sys.h>
+#include <lwip/inet.h>
+#include <lwip/inet_chksum.h>
+#include <lwip/ip.h>
+#include <lwip/netdb.h>
+#include <lwip/sockets.h>
+
+/**
+ * PING_DEBUG: Enable debugging for PING.
+ */
+#ifndef PING_DEBUG
+#define PING_DEBUG LWIP_DBG_ON
+#endif
+
+/** ping receive timeout - in milliseconds */
+#define PING_RCV_TIMEO (2000 * portTICK_PERIOD_MS)
+/** ping delay - in milliseconds */
+#define PING_DELAY     (1000 * portTICK_PERIOD_MS)
+
+/** ping identifier - must fit on a u16_t */
+#ifndef PING_ID
+#define PING_ID 0xAFAF
+#endif
+
+/** ping additional data size to include in the packet */
+#ifndef PING_DATA_SIZE
+#define PING_DATA_SIZE 32
+#endif
+
+/* ping variables */
+static u16_t ping_seq_num;
+struct _ip_addr {
+    uint8_t addr0, addr1, addr2, addr3;
+};
+
+/** Prepare a echo ICMP request */
+static void ping_prepare_echo(struct icmp_echo_hdr *iecho, u16_t len)
+{
+    size_t i;
+    size_t data_len = len - sizeof(struct icmp_echo_hdr);
+
+    ICMPH_TYPE_SET(iecho, ICMP_ECHO);
+    ICMPH_CODE_SET(iecho, 0);
+    iecho->chksum = 0;
+    iecho->id = PING_ID;
+    iecho->seqno = htons(++ping_seq_num);
+
+    /* fill the additional data buffer with some data */
+    for (i = 0; i < data_len; i++) {
+        ((char *)iecho)[sizeof(struct icmp_echo_hdr) + i] = (char)i;
+    }
+
+    iecho->chksum = inet_chksum(iecho, len);
+}
+
+/* Ping using the socket ip */
+err_t lwip_ping_send(int s, ip_addr_t *addr, int size)
+{
+    int err;
+    struct icmp_echo_hdr *iecho;
+    struct sockaddr_in to;
+    int ping_size = sizeof(struct icmp_echo_hdr) + size;
+    LWIP_ASSERT("ping_size is too big", ping_size <= 0xffff);
+
+    iecho = mem_malloc(ping_size);
+    if (iecho == NULL) {
+        return ERR_MEM;
+    }
+
+    ping_prepare_echo(iecho, (u16_t)ping_size);
+
+    to.sin_len = sizeof(to);
+    to.sin_family = AF_INET;
+#if LWIP_IPV4 && LWIP_IPV6
+    to.sin_addr.s_addr = addr->u_addr.ip4.addr;
+#elif LWIP_IPV4
+    to.sin_addr.s_addr = addr->addr;
+#elif LWIP_IPV6
+#error Not supported IPv6.
+#endif
+
+    err = lwip_sendto(s, iecho, ping_size, 0, (struct sockaddr *)&to, sizeof(to));
+    mem_free(iecho);
+
+    return (err == ping_size ? ERR_OK : ERR_VAL);
+}
+
+int lwip_ping_recv(int s, int *ttl)
+{
+    char buf[64];
+    int fromlen = sizeof(struct sockaddr_in), len;
+    struct sockaddr_in from;
+    struct ip_hdr *iphdr;
+    struct icmp_echo_hdr *iecho;
+
+    while ((len = lwip_recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr *)&from, (socklen_t *)&fromlen)) > 0) {
+        if (len >= (int)(sizeof(struct ip_hdr) + sizeof(struct icmp_echo_hdr))) {
+            iphdr = (struct ip_hdr *)buf;
+            iecho = (struct icmp_echo_hdr *)(buf + (IPH_HL(iphdr) * 4));
+            if ((iecho->id == PING_ID) && (iecho->seqno == htons(ping_seq_num))) {
+                *ttl = iphdr->_ttl;
+                return len;
+            }
+        }
+    }
+
+    return len;
+}
+
+/* using the lwIP custom ping */
+uint32_t cmd_ping(char *target_name, uint16_t interval, uint16_t size, uint32_t count)
+{
+#if LWIP_VERSION_MAJOR >= 2U
+    struct timeval timeout = { PING_RCV_TIMEO / (1000 * portTICK_PERIOD_MS), PING_RCV_TIMEO % (1000 * portTICK_PERIOD_MS) };
+#else
+    int timeout = PING_RCV_TIMEO * 1000UL / (1000 * portTICK_PERIOD_MS);
+#endif
+
+    int s, ttl, recv_len;
+    ip_addr_t target_addr;
+    uint32_t send_times;
+    uint32_t recv_start_tick;
+    struct addrinfo hint, *res = NULL;
+    struct sockaddr_in *h = NULL;
+    struct in_addr ina;
+
+    send_times = 0;
+    ping_seq_num = 0;
+
+    if (size == 0) {
+        size = PING_DATA_SIZE;
+    }
+
+    memset(&hint, 0, sizeof(hint));
+    /* convert URL to IP */
+    if (lwip_getaddrinfo(target_name, NULL, &hint, &res) != 0) {
+        printf("ping: unknown host %s\n\r", target_name);
+        return -1;
+    }
+    memcpy(&h, &res->ai_addr, sizeof(struct sockaddr_in *));
+    memcpy(&ina, &h->sin_addr, sizeof(ina));
+    lwip_freeaddrinfo(res);
+    if (inet_aton(inet_ntoa(ina), &target_addr) == 0) {
+        printf("ping: unknown host %s\n\r", target_name);
+        return -1;
+    }
+    /* new a socket */
+    if ((s = lwip_socket(AF_INET, SOCK_RAW, IP_PROTO_ICMP)) < 0) {
+        printf("ping: create socket failed\n\r");
+        return -1;
+    }
+
+    lwip_setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
+
+    while (1) {
+        int elapsed_time;
+
+        if (lwip_ping_send(s, &target_addr, size) == ERR_OK) {
+            recv_start_tick = sys_now();
+            if ((recv_len = lwip_ping_recv(s, &ttl)) >= 0) {
+                elapsed_time = (sys_now() - recv_start_tick) * 1000UL / (1000 * portTICK_PERIOD_MS);
+                printf("%d bytes from %s icmp_seq=%d ttl=%d time=%d ms\n\r", recv_len, inet_ntoa(ina), send_times,
+                       ttl, elapsed_time);
+            } else {
+                printf("From %s icmp_seq=%d timeout\n\r", inet_ntoa(ina), send_times);
+            }
+        } else {
+            printf("Send %s - error\n\r", inet_ntoa(ina));
+        }
+
+        send_times++;
+        if (send_times >= count) {
+            /* send ping times reached, stop */
+            break;
+        }
+
+        vTaskDelay(interval); /* take a delay */
+    }
+
+    lwip_close(s);
+
+    return 0;
+}
+
+#include "shell.h"
+#include "utils_getopt.h"
+
+#define PING_USAGE \
+"ping [-c count] [-i interval] [-s size] [-h help] destination\r\n" \
+"\t\t-c count of ping requests. default is 4\r\n" \
+"\t\t-i interval in ms. default is 1000\r\n" \
+"\t\t-s ICMP payload size in bytes. default is 32\r\n" \
+"\t\t-h print this help\r\n"
+
+int ping(int argc, char **argv)
+{
+    int opt;
+    getopt_env_t getopt_env;
+    u16_t interval = PING_DELAY;
+    u16_t data_size = PING_DATA_SIZE;
+    u32_t total_count = 4;
+
+    if (argc == 1) {
+        goto usage;
+    } else {
+        utils_getopt_init(&getopt_env, 0);
+
+        while ((opt = utils_getopt(&getopt_env, argc, argv, ":i:s:c:W:h")) != -1) {
+            switch (opt) {
+                case 'i':
+                    interval = atoi(getopt_env.optarg);
+                    break;
+                case 's':
+                    data_size = atoi(getopt_env.optarg);
+                    break;
+                case 'c':
+                    total_count = atoi(getopt_env.optarg);
+                    break;
+                case 'h':
+                    goto usage;
+                case ':':
+                    printf("%s: %c requires an argument\r\n", *argv, getopt_env.optopt);
+                    goto usage;
+                case '?':
+                    printf("%s: unknown option %c\r\n", *argv, getopt_env.optopt);
+                    goto usage;
+            }
+        }
+
+        if (getopt_env.optind + 1 == argc) {
+            cmd_ping(argv[getopt_env.optind], interval, data_size, total_count);
+        } else {
+            printf("Need target address\r\n");
+            goto usage;
+        }
+    }
+
+    return 0;
+
+usage:
+    printf("%s", PING_USAGE);
+    return 0;
+}
+CSH_CMD_EXPORT(ping, );

+ 70 - 0
demo/hpmicro/tcp_client.c

@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2024 HPMicro
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#include "tcp_client.h"
+#include "lwip/sys.h"
+#include "lwip/api.h"
+#include "lwip/sockets.h"
+#include "FreeRTOS.h"
+#include "task.h"
+
+uint8_t send_buf[] = "This is a TCP Client test...\n";
+
+#ifndef TCP_CLIENT_DEBUG
+#define TCP_CLIENT_DEBUG  LWIP_DBG_OFF
+#endif
+
+static void tcp_client_thread(void *arg)
+{
+    int sock = -1;
+    ip4_addr_t ipaddr;
+    struct sockaddr_in client_addr;
+    struct netif *netif = (struct netif *)arg;
+
+    ip4addr_aton(TCP_DEST_IP, &ipaddr);
+
+    while (!netif_is_link_up(netif)) {
+        vTaskDelay(100);
+    }
+
+    while (1) {
+        sock = socket(AF_INET, SOCK_STREAM, 0);
+        if (sock < 0) {
+            printf("Socket error\n");
+            vTaskDelay(10);
+            continue;
+        }
+
+        client_addr.sin_family = AF_INET;
+        client_addr.sin_port = htons(TCP_DEST_PORT);
+        client_addr.sin_addr.s_addr = ipaddr.addr;
+        memset(&(client_addr.sin_zero), 0, sizeof(client_addr.sin_zero));
+
+        if (connect(sock, (struct sockaddr *)&client_addr, sizeof(struct sockaddr)) == -1) {
+            LWIP_DEBUGF(TCP_CLIENT_DEBUG, ("Connect error!\n"));
+            closesocket(sock);
+            vTaskDelay(10);
+            continue;
+        }
+
+        LWIP_DEBUGF(LWIP_DBG_ON | LWIP_DBG_TRACE, ("Connect to server successfully!\n"));
+
+        while (1) {
+            if (write(sock, send_buf, sizeof(send_buf)) < 0) {
+                LWIP_DEBUGF(TCP_CLIENT_DEBUG, ("Send error!\n"));
+                break;
+            }
+            vTaskDelay(1000);
+        }
+
+        closesocket(sock);
+    }
+}
+
+void tcp_client_init(void *arg)
+{
+    sys_thread_new("tcp_client_thread", tcp_client_thread, arg, 3*DEFAULT_THREAD_STACKSIZE, DEFAULT_THREAD_PRIO);
+}

+ 377 - 0
demo/hpmicro/utils_getopt.c

@@ -0,0 +1,377 @@
+/*
+ * This file is derived from musl v1.2.0.
+ * Modifications are applied.
+ * Copyright (C) Bouffalo Lab 2016-2020
+ */
+
+/*
+ * Copyright © 2005-2020 Rich Felker, et al.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <utils_getopt.h>
+
+int utils_getopt_init(getopt_env_t *env, int opterr)
+{
+    if (!env) {
+        return -1;
+    }
+    env->optarg = NULL;
+    env->optind = 1;
+    env->opterr = opterr;
+    env->optopt = 0;
+    env->__optpos = 0;
+    return 0;
+}
+
+#define NEWLINE "\r\n"
+
+int utils_getopt(getopt_env_t *env, int argc, char *const argv[], const char *optstring)
+{
+    int i;
+    char c, d;
+    char *optchar;
+
+    if (!env) {
+        return -1;
+    }
+
+    if (env->optind >= argc || !argv[env->optind])
+        return -1;
+
+    if (argv[env->optind][0] != '-') {
+        if (optstring[0] == '-') {
+            env->optarg = argv[env->optind++];
+            return 1;
+        }
+        return -1;
+    }
+
+    if (!argv[env->optind][1])
+        return -1;
+
+    if (argv[env->optind][1] == '-' && !argv[env->optind][2])
+        return env->optind++, -1;
+
+    if (!env->__optpos)
+        env->__optpos++;
+    c = argv[env->optind][env->__optpos];
+    optchar = argv[env->optind] + env->__optpos;
+    env->__optpos += !!c;
+
+    if (!argv[env->optind][env->__optpos]) {
+        env->optind++;
+        env->__optpos = 0;
+    }
+
+    if (optstring[0] == '-' || optstring[0] == '+')
+        optstring++;
+
+    i = 0;
+    do
+        d = optstring[i++];
+    while (d && d != c);
+
+    if (d != c || c == ':') {
+        env->optopt = c;
+        if (optstring[0] != ':' && env->opterr)
+            printf("%s: unrecognized option: %c" NEWLINE, argv[0], *optchar);
+        return '?';
+    }
+    if (optstring[i] == ':') {
+        env->optarg = 0;
+        if (optstring[i + 1] != ':' || env->__optpos) {
+            env->optarg = argv[env->optind++] + env->__optpos;
+            env->__optpos = 0;
+        }
+        if (env->optind > argc) {
+            env->optopt = c;
+            if (optstring[0] == ':')
+                return ':';
+            if (env->opterr) {
+                printf("%s: option requires an argument: %c" NEWLINE, argv[0], *optchar);
+            }
+            return '?';
+        }
+    }
+    return c;
+}
+
+static int params_filter(char **params, uint32_t *r)
+{
+    char *p;
+    uint32_t result = 0;
+    uint8_t base = 0;
+
+    p = *params;
+
+    if ((*p == '0') && ((*(p + 1) == 'x') || (*(p + 1) == 'X'))) {
+        p = p + 2;
+        base = 16;
+
+    } else {
+        base = 10;
+    }
+
+    while (*p) {
+        result *= base;
+        if (*p >= '0' && *p <= '9')
+            result += *p - '0';
+        else if (base == 10)
+            return -1;
+
+        if (base == 16) {
+            if (*p >= 'a' && *p <= 'f')
+                result += *p - 'a' + 10;
+            else if (*p >= 'A' && *p <= 'F')
+                result += *p - 'A' + 10;
+        }
+        p++;
+    }
+
+    *r = result;
+    return 0;
+}
+
+void get_bytearray_from_string(char **params, uint8_t *result, int array_size)
+{
+    int i = 0;
+    char rand[3];
+
+    for (i = 0; i < array_size; i++) {
+        memcpy(rand, *params, 2);
+        rand[2] = '\0';
+        result[i] = strtol(rand, NULL, 16);
+        *params = *params + 2;
+    }
+}
+
+void get_uint8_from_string(char **params, uint8_t *result)
+{
+    uint32_t p = 0;
+    int state = 0;
+
+    state = params_filter(params, &p);
+    if (!state) {
+        *result = p & 0xff;
+    } else
+        *result = 0;
+}
+
+void get_uint16_from_string(char **params, uint16_t *result)
+{
+    uint32_t p = 0;
+    int state = 0;
+
+    state = params_filter(params, &p);
+    if (!state) {
+        *result = p & 0xffff;
+    } else
+        *result = 0;
+}
+
+void get_uint32_from_string(char **params, uint32_t *result)
+{
+    uint32_t p = 0;
+    int state = 0;
+
+    state = params_filter(params, &p);
+    if (!state) {
+        *result = p;
+    } else
+        *result = 0;
+}
+
+void utils_parse_number(const char *str, char sep, uint8_t *buf, int buflen, int base)
+{
+    int i;
+    for (i = 0; i < buflen; i++) {
+        buf[i] = (uint8_t)strtol(str, NULL, base);
+        str = strchr(str, sep);
+        if (str == NULL || *str == '\0') {
+            break;
+        }
+        str++;
+    }
+}
+
+void utils_parse_number_adv(const char *str, char sep, uint8_t *buf, int buflen, int base, int *count)
+{
+    int i;
+
+    for (i = 0; i < buflen; i++) {
+        buf[i] = (uint8_t)strtol(str, NULL, base);
+        str = strchr(str, sep);
+        if (str == NULL || *str == '\0') {
+            break;
+        }
+        str++;
+    }
+    *count = (i + 1);
+}
+
+unsigned long long convert_arrayToU64(uint8_t *inputArray)
+{
+    unsigned long long result = 0;
+    for (uint8_t i = 0; i < 8; i++) {
+        result <<= 8;
+        result |= (unsigned long long)inputArray[7 - i];
+    }
+
+    return result;
+}
+
+void convert_u64ToArray(unsigned long long inputU64, uint8_t result[8])
+{
+    for (int i = 0; i < 8; i++) {
+        result[i] = inputU64 >> (i * 8);
+    }
+}
+
+void utils_memdrain8(void *src, size_t len)
+{
+    volatile uint8_t *s = (uint8_t *)src;
+    uint8_t tmp;
+
+    while (len--) {
+        tmp = *s++;
+    }
+
+    (void)tmp;
+}
+
+void utils_memdrain16(void *src, size_t len)
+{
+    volatile uint16_t *s = (uint16_t *)src;
+    uint16_t tmp;
+
+    len >>= 1; //convert to half words
+
+    while (len--) {
+        tmp = *s++;
+    }
+
+    (void)tmp;
+}
+
+void utils_memdrain32(void *src, size_t len)
+{
+    volatile uint32_t *s = (uint32_t *)src;
+    uint32_t tmp;
+
+    len >>= 2; //convert to words
+
+    while (len--) {
+        tmp = *s++;
+    }
+
+    (void)tmp;
+}
+
+void utils_memdrain64(void *src, size_t len)
+{
+    volatile uint64_t *s = (uint64_t *)src;
+    uint64_t tmp;
+
+    len >>= 3; //convert to two words
+
+    while (len--) {
+        tmp = *s++;
+    }
+
+    (void)tmp;
+}
+
+void *utils_memdrain8_with_check(void *src, size_t len, uint8_t seq)
+{
+    volatile uint8_t *s = (uint8_t *)src;
+    uint8_t tmp;
+
+    (void)tmp;
+
+    while (len--) {
+        tmp = *s++;
+        if ((seq++) != tmp) {
+            return (uint8_t *)s - 1;
+        }
+    }
+
+    return NULL;
+}
+
+void *utils_memdrain16_with_check(void *src, size_t len, uint16_t seq)
+{
+    volatile uint16_t *s = (uint16_t *)src;
+    uint16_t tmp;
+    (void)tmp;
+
+    len >>= 1; //convert to half words
+
+    while (len--) {
+        tmp = *s++;
+        if ((seq++) != tmp) {
+            return (uint16_t *)s - 1;
+        }
+    }
+
+    return NULL;
+}
+
+void *utils_memdrain32_with_check(void *src, size_t len, uint32_t seq)
+{
+    volatile uint32_t *s = (uint32_t *)src;
+    uint32_t tmp;
+    (void)tmp;
+
+    len >>= 2; //convert to words
+
+    while (len--) {
+        tmp = *s++;
+        if ((seq++) != tmp) {
+            return (uint32_t *)s - 1;
+        }
+    }
+
+    return NULL;
+}
+
+void *utils_memdrain64_with_check(void *src, size_t len, uint64_t seq)
+{
+    volatile uint64_t *s = (uint64_t *)src;
+    uint64_t tmp;
+    (void)tmp;
+
+    len >>= 3; //convert to two words
+
+    while (len--) {
+        tmp = *s++;
+        if ((seq++) != tmp) {
+            return (uint64_t *)s - 1;
+        }
+    }
+
+    return NULL;
+}

+ 107 - 0
demo/hpmicro/utils_getopt.h

@@ -0,0 +1,107 @@
+#ifndef __GETOPT_H__
+#define __GETOPT_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Parameters needed to parse the command line
+ *
+ */
+typedef struct getopt_env {
+    char *optarg;    /*!< if the option accepts parameters, then optarg point to the option parameter*/
+    int optind;      /*!< current index of argv*/
+    int opterr;      /*!< non-zero enable error message output, while 0,no error message output*/
+    int optopt;      /*!< contain unrecognized option character*/
+    int __optpos;
+} getopt_env_t;
+
+/**
+ * @brief Initialize struct getopt_env
+ *
+ * @param env pointer to struct getopt_env
+ * @param opterr set error message output method
+ *
+ * @return
+ *     -  0: success
+ *     - -1: fail
+ */
+int utils_getopt_init(getopt_env_t *env, int opterr);
+
+/**
+ * @brief Parses the command-line arguments
+ *
+ * @param env pointer to struct getopt_env
+ * @param argc the argument count
+ * @param argv the argument array
+ *
+ * @return
+ *     -  option character : an option was successfully found
+ *     - -1 : all command-line options have been parsed
+ *     - '?' : option character was not in optstring
+ *     - ':' or '?' : If utils_getopt() encounters an option with a missing argument, then the return value depends on the first character in optstring: if it is ':', then ':' is returned; otherwise '?' is returned
+ *
+ * @note Example
+ * @code
+ *
+ * #include <utils_getopt.h>
+ * #include <stdio.h>
+ *
+ * void cmd(char *buf, int len, int argc, char **argv)
+ * {
+ *     int opt;
+       getopt_env_t getopt_env;
+       utils_getopt_init(&getopt_env, 0);
+ *     //put ':' in the starting of the string so that program can distinguish between '?' and ':'
+ *     while ((opt = utils_getopt(&getopt_env, argc, argv, ":if:lr")) != -1) {
+ *         switch(opt)
+ *         {
+ *             case 'i':
+ *             case 'l':
+ *             case 'r':
+ *                 printf("option: %c\r\n", opt);
+ *                 break;
+ *             case 'f':
+ *                 printf("filename: %s\r\n", getopt_env.optarg);
+ *                 break;
+ *             case ':':
+                   printf("%s: %c requires an argument\r\n", *argv, getopt_env.optopt);
+ *                 break;
+ *             case '?':
+ *                 printf("unknow option: %c\r\n", getopt_env.optopt);
+ *                 break;
+ *          }
+ *      }
+ *      //optind is for the extra arguments which are not parsed
+ *      for(; getopt_env.optind < argc; getopt_env.optind++){
+ *          printf("extra arguments: %s\r\n", argv[getopt_env.optind]);
+ *      }
+ *
+ *  }
+ *  @endcode
+ */
+int utils_getopt(getopt_env_t *env, int argc, char *const argv[], const char *optstring);
+
+void get_bytearray_from_string(char **params, uint8_t *result, int array_size);
+void get_uint8_from_string(char **params, uint8_t *result);
+void get_uint16_from_string(char **params, uint16_t *result);
+void get_uint32_from_string(char **params, uint32_t *result);
+void utils_parse_number(const char *str, char sep, uint8_t *buf, int buflen, int base);
+void utils_parse_number_adv(const char *str, char sep, uint8_t *buf, int buflen, int base, int *count);
+unsigned long long convert_arrayToU64(uint8_t *inputArray);
+void convert_u64ToArray(unsigned long long inputU64, uint8_t result[8]);
+void utils_memdrain8(void *src, size_t len);
+void utils_memdrain16(void *src, size_t len);
+void utils_memdrain32(void *src, size_t len);
+void utils_memdrain64(void *src, size_t len);
+void *utils_memdrain8_with_check(void *src, size_t len, uint8_t seq);
+void *utils_memdrain16_with_check(void *src, size_t len, uint16_t seq);
+void *utils_memdrain32_with_check(void *src, size_t len, uint32_t seq);
+void *utils_memdrain64_with_check(void *src, size_t len, uint64_t seq);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GETOPT_H__ */

BIN
docs/assets/ethercat.png


+ 3 - 0
include/ec_cmd.h

@@ -9,6 +9,9 @@
 typedef struct ec_master ec_master_t;
 
 void ec_master_cmd_init(ec_master_t *master);
+#ifdef CONFIG_EC_EOE
+void ec_master_cmd_eoe_init(ec_eoe_t *master);
+#endif
 int ethercat(int argc, const char **argv);
 
 #endif

+ 2 - 1
include/ec_common.h

@@ -13,6 +13,7 @@ const char *ec_mbox_protocol_string(uint8_t prot);
 const char *ec_alstatus_string(uint16_t errorcode);
 const char *ec_mbox_error_string(uint16_t errorcode);
 const char *ec_sdo_abort_string(uint32_t errorcode);
-const char *foe_errorcode_string(uint16_t errorcode);
+const char *ec_foe_errorcode_string(uint16_t errorcode);
+const char *ec_eoe_errorcode_string(uint16_t errorcode);
 
 #endif

+ 32 - 4
include/ec_def.h

@@ -272,6 +272,11 @@ typedef struct ec_alstatus {
 #define EC_SII_SM_PROCESS_DATA_OUTPUT 0x0003
 #define EC_SII_SM_PROCESS_DATA_INPUT  0x0004
 
+#define EC_SM_INDEX_MBX_WRITE           0x0000
+#define EC_SM_INDEX_MBX_READ            0x0001
+#define EC_SM_INDEX_PROCESS_DATA_OUTPUT 0x0002
+#define EC_SM_INDEX_PROCESS_DATA_INPUT  0x0003
+
 typedef struct __PACKED ec_sii_base {
     uint16_t pdi_control;
     uint16_t pdi_config;
@@ -417,10 +422,15 @@ enum {
     EC_MBOX_TYPE_VOE = 0x0f,
 };
 
-#define EC_SM_INDEX_MBX_WRITE           0x0000
-#define EC_SM_INDEX_MBX_READ            0x0001
-#define EC_SM_INDEX_PROCESS_DATA_OUTPUT 0x0002
-#define EC_SM_INDEX_PROCESS_DATA_INPUT  0x0003
+#define EC_MBXERR_SYNTAX              0x01 /**< \brief Mailbox error "syntax"*/
+#define EC_MBXERR_UNSUPPORTEDPROTOCOL 0x02 /**< \brief Mailbox error "unsupported protocol"*/
+#define EC_MBXERR_INVALIDCHANNEL      0x03 /**< \brief Mailbox error "invalid channel"*/
+#define EC_MBXERR_SERVICENOTSUPPORTED 0x04 /**< \brief Mailbox error "service not supported"*/
+#define EC_MBXERR_INVALIDHEADER       0x05 /**< \brief Mailbox error "invalid header"*/
+#define EC_MBXERR_SIZETOOSHORT        0x06 /**< \brief Mailbox error "Size too short"*/
+#define EC_MBXERR_NOMOREMEMORY        0x07 /**< \brief Mailbox error "No memory"*/
+#define EC_MBXERR_INVALIDSIZE         0x08 /**< \brief Mailbox error "Invalid size"*/
+#define EC_MBXERR_SERVICEINWORK       0x09 /**< \brief Mailbox error "Service in work"*/
 
 typedef struct __PACKED ec_coe_header {
     uint16_t number   : 9;
@@ -507,6 +517,24 @@ typedef struct __PACKED {
 #define EC_FOE_ERRCODE_NO_FILE_HEADER   0x8010 /**< \brief Missing file header of error in file header*/
 #define EC_FOE_ERRCODE_FLASH_ERROR      0x8011 /**< \brief Flash cannot be accessed*/
 
+#define EC_EOE_TYPE_FRAGMENT                0x00
+#define EC_EOE_TYPE_TIMESTAMP               0x01
+#define EC_EOE_TYPE_SET_IP_REQUEST          0x02
+#define EC_EOE_TYPE_SET_IP_RESPONSE         0x03
+#define EC_EOE_TYPE_MACFILTER_REQUEST       0x04
+#define EC_EOE_TYPE_MACFILTER_RESPONSE      0x05
+#define EC_EOE_TYPE_GET_IP_REQUEST          0x06
+#define EC_EOE_TYPE_GET_IP_RESPONSE         0x07
+#define EC_EOE_TYPE_GET_MAC_FILTER_REQUEST  0x08
+#define EC_EOE_TYPE_GET_MAC_FILTER_RESPONSE 0x09
+
+#define EC_EOE_RESULT_NOERROR                  0x0000 /**< \brief No Error*/
+#define EC_EOE_RESULT_UNSPECIFIED_ERROR        0x0001 /**< \brief Unspecified error*/
+#define EC_EOE_RESULT_UNSUPPORTED_TYPE         0x0002 /**< \brief unsupported type*/
+#define EC_EOE_RESULT_NO_IP_SUPPORT            0x0201 /**< \brief No IP supported*/
+#define EC_EOE_RESULT_NO_DHCP_SUPPORT          0x0202 /**< \brief No DHCP supported*/
+#define EC_EOE_RESULT_NO_MACFILTERMASK_SUPPORT 0x0401 /**< \brief No mac filter supported*/
+
 typedef enum {
     EC_DIR_OUTPUT, /**< Values written by the master. */
     EC_DIR_INPUT,  /**< Values read by the master. */

+ 49 - 0
include/ec_eoe.h

@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef EC_EOE_H
+#define EC_EOE_H
+
+#ifdef CONFIG_EC_EOE
+#include "netif/etharp.h"
+#include "lwip/netif.h"
+#include "lwip/pbuf.h"
+#include "lwip/tcpip.h"
+#include "lwip/ip_addr.h"
+
+struct ec_eoe_ip_param {
+    uint8_t hostname[EC_MAX_HOSTNAME_SIZE];
+    const char *ipv4_addr_str;
+    const char *ipv4_mask_str;
+    const char *ipv4_gw_str;
+    const char *dns_server_str;
+    uint8_t mac_addr[6];
+};
+
+typedef struct ec_eoe {
+    ec_master_t *master;
+    uint16_t slave_index;
+    ec_datagram_t tx_datagram;
+    ec_datagram_t rx_datagram;
+    uint8_t tx_buffer[1536];
+    uint8_t rx_buffer[1536];
+
+    struct ec_eoe_ip_param master_ip_param;
+    struct ec_eoe_ip_param slave_ip_param;
+
+    bool initialized;
+    ec_osal_thread_t rx_thread;
+    ec_osal_mutex_t txrx_mutex;
+    struct netif netif;
+} ec_eoe_t;
+
+int ec_eoe_start(ec_eoe_t *eoe,
+                 ec_master_t *master,
+                 uint16_t slave_index,
+                 struct ec_eoe_ip_param *master_ip_param,
+                 struct ec_eoe_ip_param *slave_ip_param);
+
+#endif
+#endif

+ 14 - 10
include/ec_errno.h

@@ -15,16 +15,20 @@
 #define EC_ERR_ALERR       6  /**< AL status error */
 #define EC_ERR_SII         7  /**< SII error */
 #define EC_ERR_MBOX        8  /**< mailbox error */
-#define EC_ERR_COE_TYPE    9  /**< COE type error */
-#define EC_ERR_COE_SIZE    10 /**< COE size error */
-#define EC_ERR_COE_REQUEST 11 /**< COE request & index & subindex error */
-#define EC_ERR_COE_TOGGLE  12 /**< COE toggle error */
-#define EC_ERR_COE_ABORT   13 /**< COE abort error */
-#define EC_ERR_FOE_TYPE    14 /**< FOE type error */
-#define EC_ERR_FOE_SIZE    15 /**< FOE size error */
-#define EC_ERR_FOE_OPCODE  16 /**< FOE opcode error */
-#define EC_ERR_FOE_PACKNO  17 /**< FOE packet number error */
+#define EC_ERR_MBOX_EMPTY  9  /**< mailbox is empty */
+#define EC_ERR_COE_TYPE    10 /**< COE type error */
+#define EC_ERR_COE_SIZE    11 /**< COE size error */
+#define EC_ERR_COE_REQUEST 12 /**< COE request & index & subindex error */
+#define EC_ERR_COE_TOGGLE  13 /**< COE toggle error */
+#define EC_ERR_COE_ABORT   14 /**< COE abort error */
+#define EC_ERR_FOE_TYPE    15 /**< FOE type error */
+#define EC_ERR_FOE_SIZE    16 /**< FOE size error */
+#define EC_ERR_FOE_OPCODE  17 /**< FOE opcode error */
+#define EC_ERR_FOE_PACKNO  18 /**< FOE packet number error */
+#define EC_ERR_EOE_TYPE    19 /**< EOE type error */
+#define EC_ERR_EOE_SIZE    20 /**< EOE size error */
+#define EC_ERR_EOE_FRAME   21 /**< EOE frametype error */
 
-#define EC_ERR_UNKNOWN     255
+#define EC_ERR_UNKNOWN 255
 
 #endif

+ 5 - 4
include/ec_master.h

@@ -20,17 +20,18 @@
 #include "ec_def.h"
 #include "ec_osal.h"
 #include "ec_port.h"
+#include "ec_perf.h"
+#include "ec_timestamp.h"
+#include "ec_version.h"
 #include "ec_datagram.h"
 #include "ec_common.h"
 #include "ec_sii.h"
 #include "ec_slave.h"
 #include "ec_mailbox.h"
 #include "ec_coe.h"
-#include "ec_cmd.h"
-#include "ec_perf.h"
-#include "ec_timestamp.h"
-#include "ec_version.h"
 #include "ec_foe.h"
+#include "ec_eoe.h"
+#include "ec_cmd.h"
 
 /** Netdev statistics.
  */

+ 63 - 22
src/ec_cmd.c

@@ -85,11 +85,22 @@ typedef struct {
 
 static ec_master_t *global_cmd_master = NULL;
 
+#ifdef CONFIG_EC_EOE
+static ec_eoe_t *global_cmd_eoe = NULL;
+#endif
+
 void ec_master_cmd_init(ec_master_t *master)
 {
     global_cmd_master = master;
 }
 
+#ifdef CONFIG_EC_EOE
+void ec_master_cmd_eoe_init(ec_eoe_t *master)
+{
+    global_cmd_eoe = master;
+}
+#endif
+
 static void ec_master_cmd_show_help(void)
 {
     EC_LOG_RAW("CherryECAT " CHERRYECAT_VERSION_STR " Command Line Tool\n\n");
@@ -106,15 +117,19 @@ static void ec_master_cmd_show_help(void)
     EC_LOG_RAW("  states -p <idx> <state>                        Request state for slave <idx> (hex)\n");
     EC_LOG_RAW("  coe_read -p [idx] [index] [subindex]           Read SDO via CoE\n");
     EC_LOG_RAW("  coe_write -p [idx] [index] [subindex] [data]   Write SDO via CoE\n");
-    EC_LOG_RAW("  pdo_read                                       Read process data\n");
-    EC_LOG_RAW("  pdo_read -p [idx]                              Read slave <idx> process data\n");
-    EC_LOG_RAW("  pdo_write [offset] [hex low...high]            Write hexarray with offset to pdo\n");
-    EC_LOG_RAW("  pdo_write -p [idx] [offset] [hex low...high]   Write slave <idx> hexarray with offset to pdo\n");
 #ifdef CONFIG_EC_FOE
     EC_LOG_RAW("  foe_write -p [idx] [filename] [pwd] [hexdata]  Read hexarray via FoE\n");
     EC_LOG_RAW("  foe_read -p [idx] [filename] [pwd]             Write hexarray via FoE\n");
 #endif
+#ifdef CONFIG_EC_EOE
+    EC_LOG_RAW("  eoe_start -p [slave_idx]                       Start EoE on slave <idx>\n");
+#endif
+    EC_LOG_RAW("  pdo_read                                       Read process data\n");
+    EC_LOG_RAW("  pdo_read -p [idx]                              Read slave <idx> process data\n");
+    EC_LOG_RAW("  pdo_write [offset] [hex low...high]            Write hexarray with offset to pdo\n");
+    EC_LOG_RAW("  pdo_write -p [idx] [offset] [hex low...high]   Write slave <idx> hexarray with offset to pdo\n");
     EC_LOG_RAW("  sii_read -p [idx]                              Read SII\n");
+    EC_LOG_RAW("  sii_write -p [idx]                             Write SII\n");
     EC_LOG_RAW("  wc                                             Show master working counter\n");
 #ifdef CONFIG_EC_PERF_ENABLE
     EC_LOG_RAW("  perf -s <time>                                 Start performance test\n");
@@ -667,6 +682,8 @@ static int parse_hex_string(const char *hex_str, uint8_t *output, uint32_t max_l
 
 int ethercat(int argc, const char **argv)
 {
+    int ret;
+
     if (global_cmd_master == NULL) {
         EC_LOG_RAW("No master configured\n");
         return -1;
@@ -767,11 +784,10 @@ int ethercat(int argc, const char **argv)
         static ec_datagram_t datagram;
         static uint8_t output_buffer[512];
         uint32_t actual_size;
-        int ret;
 
         uint32_t slave_idx = atoi(argv[3]);
 
-        ec_datagram_init(&datagram, 512);
+        ec_datagram_init(&datagram, 1024);
         ret = ec_coe_upload(global_cmd_master,
                             slave_idx,
                             &datagram,
@@ -794,7 +810,6 @@ int ethercat(int argc, const char **argv)
         static ec_datagram_t datagram;
         uint32_t u32data;
         uint32_t size;
-        int ret;
 
         uint32_t slave_idx = atoi(argv[3]);
         u32data = strtoul(argv[6], NULL, 16);
@@ -806,7 +821,7 @@ int ethercat(int argc, const char **argv)
         else
             size = 4;
 
-        ec_datagram_init(&datagram, 512);
+        ec_datagram_init(&datagram, 1024);
         ret = ec_coe_download(global_cmd_master,
                               slave_idx,
                               &datagram,
@@ -836,11 +851,10 @@ int ethercat(int argc, const char **argv)
         return 0;
     } else if (argc >= 5 && strcmp(argv[1], "sii_write") == 0) {
         // ethercat sii_write -p [slave_idx]
-        int ret;
         static ec_datagram_t datagram;
         extern unsigned char cherryecat_eepromdata[2048];
 
-        ec_datagram_init(&datagram, 4096);
+        ec_datagram_init(&datagram, 1024);
 
         uint32_t slave_idx = atoi(argv[3]);
 
@@ -859,17 +873,15 @@ int ethercat(int argc, const char **argv)
     } else if (argc >= 2 && strcmp(argv[1], "pdo_read") == 0) {
         // ethercat pdo_read
         if (argc == 2) {
-            for (uint32_t count = 0; count < 10; count++) {
+            for (uint32_t slave_idx = 0; slave_idx < global_cmd_master->slave_count; slave_idx++) {
+                uint8_t *buffer = ec_master_get_slave_domain_input(global_cmd_master, slave_idx);
+                uint32_t data_size = ec_master_get_slave_domain_isize(global_cmd_master, slave_idx);
                 EC_LOG_RAW("\r");
-                for (uint32_t i = 0; i < global_cmd_master->actual_pdo_size; i++) {
-                    EC_LOG_RAW("%02x ", global_cmd_master->pdo_buffer[EC_NETDEV_MAIN][i]);
-                }
-                fflush(stdout);
-                if (count < 9) {
-                    ec_osal_msleep(1000);
+                for (uint32_t i = 0; i < data_size; i++) {
+                    EC_LOG_RAW("%02x ", buffer[i]);
                 }
+                EC_LOG_RAW("\n");
             }
-            EC_LOG_RAW("\n");
             return 0;
         } else if (argc == 4 && strcmp(argv[2], "-p") == 0) {
             // ethercat pdo_read -p [slave_idx]
@@ -947,7 +959,6 @@ int ethercat(int argc, const char **argv)
         // ethercat foe_write -p [slave_idx] [filename] [password] [hexdata]
         uint8_t hexdata[256];
         uint32_t size;
-        int ret;
         uint32_t slave_idx = atoi(argv[3]);
         const char *filename = argv[4];
         uint32_t password = strtoul(argv[5], NULL, 16);
@@ -958,7 +969,7 @@ int ethercat(int argc, const char **argv)
         }
         static ec_datagram_t datagram;
 
-        ec_datagram_init(&datagram, 4096);
+        ec_datagram_init(&datagram, 1024);
 
         EC_SLAVE_LOG_INFO("Slave %u foe write file %s, password: 0x%08x, size %u\n", slave_idx, filename, password, size);
 
@@ -978,7 +989,6 @@ int ethercat(int argc, const char **argv)
         // ethercat foe_read -p [slave_idx] [filename] [password]
         uint8_t hexdata[256];
         uint32_t size;
-        int ret;
         uint32_t slave_idx = atoi(argv[3]);
         const char *filename = argv[4];
         uint32_t password = strtoul(argv[5], NULL, 16);
@@ -987,7 +997,7 @@ int ethercat(int argc, const char **argv)
 
         static ec_datagram_t datagram;
 
-        ec_datagram_init(&datagram, 4096);
+        ec_datagram_init(&datagram, 1024);
 
         ec_osal_mutex_take(global_cmd_master->scan_lock);
         ret = ec_foe_read(global_cmd_master, slave_idx, &datagram, filename, password, hexdata, sizeof(hexdata), &size);
@@ -1002,6 +1012,37 @@ int ethercat(int argc, const char **argv)
         ec_datagram_clear(&datagram);
         return 0;
     }
+#endif
+#ifdef CONFIG_EC_EOE
+    else if (argc >= 4 && strcmp(argv[1], "eoe_start") == 0) {
+        // ethercat eoe_start -p [slave_idx]
+        struct ec_eoe_ip_param slave_ip_param = { 0 };
+        struct ec_eoe_ip_param master_ip_param = { 0 };
+
+        slave_ip_param.ipv4_addr_str = "192.168.100.10";
+        slave_ip_param.ipv4_mask_str = "255.255.255.0";
+        slave_ip_param.ipv4_gw_str = "192.168.100.1";
+        slave_ip_param.dns_server_str = "192.168.2.1";
+
+        for (uint8_t i = 0; i < 6; i++) {
+            slave_ip_param.mac_addr[i] = i + 0xf0;
+        }
+
+        master_ip_param.ipv4_addr_str = "192.168.100.8";
+        master_ip_param.ipv4_mask_str = "255.255.255.0";
+        master_ip_param.ipv4_gw_str = "192.168.100.1";
+
+        for (uint8_t i = 0; i < 6; i++) {
+            master_ip_param.mac_addr[i] = i;
+        }
+        ret = ec_eoe_start(global_cmd_eoe, global_cmd_master, atoi(argv[3]), &master_ip_param, &slave_ip_param);
+        if (ret < 0) {
+            EC_LOG_RAW("eoe start failed: %d\n", ret);
+        } else {
+            EC_LOG_RAW("eoe start success\n");
+        }
+        return 0;
+    }
 #endif
     else if (argc >= 3 && strcmp(argv[1], "timediff") == 0) {
         if (strcmp(argv[2], "-s") == 0) {

+ 22 - 1
src/ec_common.c

@@ -370,7 +370,7 @@ const ec_code_msg_t foe_errcode_messages[] = {
 
 };
 
-const char *foe_errorcode_string(uint16_t errorcode)
+const char *ec_foe_errorcode_string(uint16_t errorcode)
 {
     for (uint32_t i = 0; foe_errcode_messages[i].code != 0xffffffff; i++) {
         if (foe_errcode_messages[i].code == errorcode) {
@@ -379,3 +379,24 @@ const char *foe_errorcode_string(uint16_t errorcode)
     }
     return "Unknown errorcode";
 }
+
+const ec_code_msg_t eoe_errcode_messages[] = {
+    { EC_EOE_RESULT_NOERROR, "No Error" },
+    { EC_EOE_RESULT_UNSPECIFIED_ERROR, "Unspecified error" },
+    { EC_EOE_RESULT_UNSUPPORTED_TYPE, "Unsupported type" },
+    { EC_EOE_RESULT_NO_IP_SUPPORT, "No IP support" },
+    { EC_EOE_RESULT_NO_DHCP_SUPPORT, "No DHCP support" },
+    { EC_EOE_RESULT_NO_MACFILTERMASK_SUPPORT, "No MAC filter/mask support" },
+
+    { 0xffffffff }
+};
+
+const char *ec_eoe_errorcode_string(uint16_t errorcode)
+{
+    for (uint32_t i = 0; eoe_errcode_messages[i].code != 0xffffffff; i++) {
+        if (eoe_errcode_messages[i].code == errorcode) {
+            return eoe_errcode_messages[i].message;
+        }
+    }
+    return "Unknown errorcode";
+}

+ 1 - 1
src/ec_datagram.c

@@ -99,7 +99,7 @@ void ec_datagram_unqueue(ec_datagram_t *datagram)
 
 EC_FAST_CODE_SECTION void ec_datagram_zero(ec_datagram_t *datagram)
 {
-    memset(datagram->data, 0x00, datagram->data_size);
+    ec_memset(datagram->data, 0x00, datagram->data_size);
 }
 
 void ec_datagram_fill(ec_datagram_t *datagram,

+ 0 - 0
src/ec_eoe.c


+ 5 - 2
src/ec_mailbox.c

@@ -15,10 +15,11 @@ uint8_t *ec_mailbox_fill_send(ec_master_t *master,
 {
     ec_slave_t *slave;
 
-    EC_ASSERT_MSG(slave_index >= master->slave_count, "Invalid slave index");
+    EC_ASSERT_MSG(slave_index < master->slave_count, "Invalid slave index");
 
     slave = &master->slaves[slave_index];
 
+    EC_ASSERT_MSG(datagram->mem_size >= slave->configured_rx_mailbox_size, "Datagram size too small for RX mailbox");
     EC_ASSERT_MSG((EC_MBOX_HEADER_SIZE + size) <= slave->configured_rx_mailbox_size, "RX Mailbox size overflow");
 
     EC_WRITE_U16(datagram->data, size);                       // mailbox service data length
@@ -74,7 +75,7 @@ check_again:
 
     if (!(EC_READ_U8(datagram->data + 5) & ESC_SYNCM_STATUS_MBX_MODE_MASK)) {
         if ((jiffies - start_time) > timeout_us) {
-            return -EC_ERR_TIMEOUT;
+            return -EC_ERR_MBOX_EMPTY;
         }
         goto check_again;
     }
@@ -106,6 +107,8 @@ int ec_mailbox_receive(ec_master_t *master,
         return ret;
     }
 
+    EC_ASSERT_MSG(datagram->mem_size >= slave->configured_tx_mailbox_size, "Datagram size too small for TX mailbox");
+
     ec_datagram_fprd(datagram, slave->station_address, slave->configured_tx_mailbox_offset, slave->configured_tx_mailbox_size);
     ec_datagram_zero(datagram);
     datagram->netdev_idx = slave->netdev_idx;