Sfoglia il codice sorgente

feat(foe): add foe code

Signed-off-by: sakumisu <1203593632@qq.com>
sakumisu 3 mesi fa
parent
commit
bd069a84a6

+ 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_foe.c
         ${CMAKE_CURRENT_LIST_DIR}/src/ec_mailbox.c
         ${CMAKE_CURRENT_LIST_DIR}/src/ec_master.c
         ${CMAKE_CURRENT_LIST_DIR}/src/ec_netdev.c

+ 1 - 0
README.md

@@ -55,6 +55,7 @@ CherryECAT is a tiny and beautiful, high real-time and low-jitter EtherCAT maste
 ![ethercat](docs/assets/ethercat5.png)
 ![ethercat](docs/assets/ethercat6.png)
 ![ethercat](docs/assets/ethercat7.png)
+![ethercat](docs/assets/ethercat8.png)
 
 ## Tool
 

+ 1 - 0
README_zh.md

@@ -55,6 +55,7 @@ CherryECAT 是一个小而美的、高实时性、低抖动的 EtherCAT 主机
 ![ethercat](docs/assets/ethercat5.png)
 ![ethercat](docs/assets/ethercat6.png)
 ![ethercat](docs/assets/ethercat7.png)
+![ethercat](docs/assets/ethercat8.png)
 
 ## 工具
 

+ 2 - 0
cherryec_config_template.h

@@ -76,4 +76,6 @@
 #define CONFIG_EC_MAX_ENET_RXBUF_COUNT 10
 #endif
 
+//#define CONFIG_EC_FOE
+
 #endif

+ 2 - 0
demo/hpmicro/inc/ec_config.h

@@ -76,4 +76,6 @@
 #define CONFIG_EC_MAX_ENET_RXBUF_COUNT 10
 #endif
 
+//#define CONFIG_EC_FOE
+
 #endif

BIN
docs/assets/ethercat.png


BIN
docs/assets/ethercat2.png


BIN
docs/assets/ethercat8.png


+ 1 - 0
include/ec_common.h

@@ -13,5 +13,6 @@ 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);
 
 #endif

+ 113 - 79
include/ec_def.h

@@ -10,19 +10,19 @@
  *	IEEE 802.3 Ethernet magic constants.  The frame sizes omit the preamble
  *	and FCS/CRC (frame check sequence).
  */
-#define ETH_ALEN                     6    /* Octets in one ethernet addr	 */
-#define ETH_TLEN                     2    /* Octets in ethernet type field */
-#define ETH_HLEN                     14   /* Total octets in header.	 */
-#define ETH_ZLEN                     60   /* Min. octets in frame sans FCS */
-#define ETH_DATA_LEN                 1500 /* Max. octets in payload	 */
-#define ETH_FRAME_LEN                1514 /* Max. octets in frame sans FCS */
-#define ETH_FCS_LEN                  4    /* Octets in the FCS		 */
+#define ETH_ALEN      6    /* Octets in one ethernet addr	 */
+#define ETH_TLEN      2    /* Octets in ethernet type field */
+#define ETH_HLEN      14   /* Total octets in header.	 */
+#define ETH_ZLEN      60   /* Min. octets in frame sans FCS */
+#define ETH_DATA_LEN  1500 /* Max. octets in payload	 */
+#define ETH_FRAME_LEN 1514 /* Max. octets in frame sans FCS */
+#define ETH_FCS_LEN   4    /* Octets in the FCS		 */
 
-#define ETH_MIN_MTU                  68      /* Min IPv4 MTU per RFC791	*/
-#define ETH_MAX_MTU                  0xFFFFU /* 65535, same as IP_MAX_MTU	*/
+#define ETH_MIN_MTU 68      /* Min IPv4 MTU per RFC791	*/
+#define ETH_MAX_MTU 0xFFFFU /* 65535, same as IP_MAX_MTU	*/
 
 /** Datagram timeout in microseconds. */
-#define EC_IO_TIMEOUT                500
+#define EC_IO_TIMEOUT 500
 
 /** Time to send a byte in nanoseconds.
  *
@@ -31,82 +31,82 @@
 #define EC_BYTE_TRANSMISSION_TIME_NS 80
 
 /** Minimum size of a buffer used with ec_state_string(). */
-#define EC_STATE_STRING_SIZE         32
+#define EC_STATE_STRING_SIZE 32
 
 /** Maximum SII size in words, to avoid infinite reading. */
-#define EC_MAX_SII_SIZE              4096
+#define EC_MAX_SII_SIZE 4096
 
 /** Number of statistic rate intervals to maintain. */
-#define EC_RATE_COUNT                3
+#define EC_RATE_COUNT 3
 
 /*****************************************************************************
  * EtherCAT protocol
  ****************************************************************************/
 
 /** Size of an EtherCAT frame header. */
-#define EC_FRAME_HEADER_SIZE         2
+#define EC_FRAME_HEADER_SIZE 2
 
 /** Size of an EtherCAT datagram header. */
-#define EC_DATAGRAM_HEADER_SIZE      10
+#define EC_DATAGRAM_HEADER_SIZE 10
 
 /** Size of an EtherCAT datagram workcounter. */
-#define EC_DATAGRAM_WC_SIZE          2
+#define EC_DATAGRAM_WC_SIZE 2
 
 /** Size of the EtherCAT address field. */
-#define EC_ADDR_LEN                  4
+#define EC_ADDR_LEN 4
 
 /** Resulting maximum data size of a single datagram in a frame. */
-#define EC_MAX_DATA_SIZE             (ETH_DATA_LEN - EC_FRAME_HEADER_SIZE - EC_DATAGRAM_HEADER_SIZE - EC_DATAGRAM_WC_SIZE)
+#define EC_MAX_DATA_SIZE (ETH_DATA_LEN - EC_FRAME_HEADER_SIZE - EC_DATAGRAM_HEADER_SIZE - EC_DATAGRAM_WC_SIZE)
 
 /** Mailbox header size.  */
-#define EC_MBOX_HEADER_SIZE          6
+#define EC_MBOX_HEADER_SIZE 6
 
 /** Word offset of first SII category. */
 #define EC_FIRST_SII_CATEGORY_OFFSET 0x40
 
 /** Size of a sync manager configuration page. */
-#define EC_SYNC_PAGE_SIZE            8
+#define EC_SYNC_PAGE_SIZE 8
 
 /** Maximum number of FMMUs per slave. */
-#define EC_MAX_FMMUS                 16
+#define EC_MAX_FMMUS 16
 
 /** Size of an FMMU configuration page. */
-#define EC_FMMU_PAGE_SIZE            16
+#define EC_FMMU_PAGE_SIZE 16
 
 /** Number of DC sync signals. */
-#define EC_SYNC_SIGNAL_COUNT         2
+#define EC_SYNC_SIGNAL_COUNT 2
 
 /** Size of the datagram decription string.
  *
  * This is also used as the maximum lenth of EoE device names.
  **/
-#define EC_DATAGRAM_NAME_SIZE        20
+#define EC_DATAGRAM_NAME_SIZE 20
 
 /** Maximum hostname size.
  *
  * Used inside the EoE set IP parameter request.
  */
-#define EC_MAX_HOSTNAME_SIZE         32
+#define EC_MAX_HOSTNAME_SIZE 32
 
 /** Maximum number of sync managers per slave.
  */
-#define EC_MAX_SYNC_MANAGERS         16
+#define EC_MAX_SYNC_MANAGERS 16
 
 /** Maximum string length.
  *
  * Used in ec_slave_info_t.
  */
-#define EC_MAX_STRING_LENGTH         64
+#define EC_MAX_STRING_LENGTH 64
 
 /** Maximum number of slave ports. */
-#define EC_MAX_PORTS                 4
+#define EC_MAX_PORTS 4
 
 /** Slave state mask.
  *
  * Apply this mask to a slave state byte to get the slave state without
  * the error flag.
  */
-#define EC_SLAVE_STATE_MASK          0x0F
+#define EC_SLAVE_STATE_MASK 0x0F
 
 /** State of an EtherCAT slave.
  */
@@ -236,41 +236,41 @@ typedef struct ec_alstatus {
 #define EC_ALSTATUSCODE_TEMPERATURE_TOO_LOW        0x0082 /**< The slave temperature is too low*/
 #define EC_ALSTATUSCODE_TEMPERATURE_TOO_HIGH       0x0083 /**< The slave temperature is too high*/
 
-#define EC_SII_ADDRESS_MANUF                       (0x0008)
-#define EC_SII_ADDRESS_PRODUCTCODE                 (0x000a)
-#define EC_SII_ADDRESS_REVISION                    (0x000c)
-#define EC_SII_ADDRESS_SN                          (0x000E)
-#define EC_SII_ADDRESS_BOOTRXMBX                   (0x0014)
-#define EC_SII_ADDRESS_BOOTTXMBX                   (0x0016)
-#define EC_SII_ADDRESS_MBXSIZE                     (0x0019)
-#define EC_SII_ADDRESS_TXMBXADR                    (0x001a)
-#define EC_SII_ADDRESS_RXMBXADR                    (0x0018)
-#define EC_SII_ADDRESS_MBXPROTO                    (0x001c)
-#define EC_SII_ADDRESS_ADDITIONAL_INFO             (0x0040)
-
-#define EC_SII_TYPE_NOP                            0x0000
-#define EC_SII_TYPE_STRINGS                        0x000A
-#define EC_SII_TYPE_DATATYPES                      0x0014
-#define EC_SII_TYPE_GENERAL                        0x001E
-#define EC_SII_TYPE_FMMU                           0x0028
-#define EC_SII_TYPE_SM                             0x0029
-#define EC_SII_TYPE_FMMUX                          0x002A
-#define EC_SII_TYPE_SYNCUNIT                       0x002B
-#define EC_SII_TYPE_TXPDO                          0x0032
-#define EC_SII_TYPE_RXPDO                          0x0033
-#define EC_SII_TYPE_DC                             0x003C
-#define EC_SII_TYPE_END                            0xFFFF
-
-#define EC_SII_FMMU_NONE                           0x0000
-#define EC_SII_FMMU_READ                           0x0001
-#define EC_SII_FMMU_WRITE                          0x0002
-#define EC_SII_FMMU_SM_STATUS                      0x0003
-
-#define EC_SII_SM_UNKNOWN                          0x0000
-#define EC_SII_SM_MBX_OUT                          0x0001
-#define EC_SII_SM_MBX_IN                           0x0002
-#define EC_SII_SM_PROCESS_DATA_OUTPUT              0x0003
-#define EC_SII_SM_PROCESS_DATA_INPUT               0x0004
+#define EC_SII_ADDRESS_MANUF           (0x0008)
+#define EC_SII_ADDRESS_PRODUCTCODE     (0x000a)
+#define EC_SII_ADDRESS_REVISION        (0x000c)
+#define EC_SII_ADDRESS_SN              (0x000E)
+#define EC_SII_ADDRESS_BOOTRXMBX       (0x0014)
+#define EC_SII_ADDRESS_BOOTTXMBX       (0x0016)
+#define EC_SII_ADDRESS_MBXSIZE         (0x0019)
+#define EC_SII_ADDRESS_TXMBXADR        (0x001a)
+#define EC_SII_ADDRESS_RXMBXADR        (0x0018)
+#define EC_SII_ADDRESS_MBXPROTO        (0x001c)
+#define EC_SII_ADDRESS_ADDITIONAL_INFO (0x0040)
+
+#define EC_SII_TYPE_NOP       0x0000
+#define EC_SII_TYPE_STRINGS   0x000A
+#define EC_SII_TYPE_DATATYPES 0x0014
+#define EC_SII_TYPE_GENERAL   0x001E
+#define EC_SII_TYPE_FMMU      0x0028
+#define EC_SII_TYPE_SM        0x0029
+#define EC_SII_TYPE_FMMUX     0x002A
+#define EC_SII_TYPE_SYNCUNIT  0x002B
+#define EC_SII_TYPE_TXPDO     0x0032
+#define EC_SII_TYPE_RXPDO     0x0033
+#define EC_SII_TYPE_DC        0x003C
+#define EC_SII_TYPE_END       0xFFFF
+
+#define EC_SII_FMMU_NONE      0x0000
+#define EC_SII_FMMU_READ      0x0001
+#define EC_SII_FMMU_WRITE     0x0002
+#define EC_SII_FMMU_SM_STATUS 0x0003
+
+#define EC_SII_SM_UNKNOWN             0x0000
+#define EC_SII_SM_MBX_OUT             0x0001
+#define EC_SII_SM_MBX_IN              0x0002
+#define EC_SII_SM_PROCESS_DATA_OUTPUT 0x0003
+#define EC_SII_SM_PROCESS_DATA_INPUT  0x0004
 
 typedef struct __PACKED ec_sii_base {
     uint16_t pdi_control;
@@ -398,12 +398,12 @@ typedef struct __PACKED ec_mailbox_header {
  */
 #define EC_MBOX_HEADER_SIZE 6
 
-#define EC_MBXPROT_AOE      0x0001
-#define EC_MBXPROT_EOE      0x0002
-#define EC_MBXPROT_COE      0x0004
-#define EC_MBXPROT_FOE      0x0008
-#define EC_MBXPROT_SOE      0x0010
-#define EC_MBXPROT_VOE      0x0020
+#define EC_MBXPROT_AOE 0x0001
+#define EC_MBXPROT_EOE 0x0002
+#define EC_MBXPROT_COE 0x0004
+#define EC_MBXPROT_FOE 0x0008
+#define EC_MBXPROT_SOE 0x0010
+#define EC_MBXPROT_VOE 0x0020
 
 /** Mailbox types.
  *
@@ -462,16 +462,50 @@ typedef struct __PACKED ec_sdo_header {
 #define EC_COE_SERVICE_RXPDO_REMOTE_REQUEST 0x07
 #define EC_COE_SERVICE_SDOINFO              0x08
 
-#define EC_COE_REQUEST_SEGMENT_DOWNLOAD     0x00
-#define EC_COE_REQUEST_DOWNLOAD             0x01
-#define EC_COE_REQUEST_UPLOAD               0x02
-#define EC_COE_REQUEST_SEGMENT_UPLOAD       0x03
-#define EC_COE_REQUEST_ABORT                0x04
+#define EC_COE_REQUEST_SEGMENT_DOWNLOAD 0x00
+#define EC_COE_REQUEST_DOWNLOAD         0x01
+#define EC_COE_REQUEST_UPLOAD           0x02
+#define EC_COE_REQUEST_SEGMENT_UPLOAD   0x03
+#define EC_COE_REQUEST_ABORT            0x04
+
+#define EC_COE_RESPONSE_SEGMENT_UPLOAD   0x00
+#define EC_COE_RESPONSE_SEGMENT_DOWNLOAD 0x01
+#define EC_COE_RESPONSE_UPLOAD           0x02
+#define EC_COE_RESPONSE_DOWNLOAD         0x03
 
-#define EC_COE_RESPONSE_SEGMENT_UPLOAD      0x00
-#define EC_COE_RESPONSE_SEGMENT_DOWNLOAD    0x01
-#define EC_COE_RESPONSE_UPLOAD              0x02
-#define EC_COE_RESPONSE_DOWNLOAD            0x03
+typedef struct __PACKED {
+    uint16_t opcode;
+    union {
+        uint32_t password;
+        uint32_t packet_number;
+        uint32_t error_code;
+    };
+} ec_foe_header_t;
+
+#define EC_FOE_OPCODE_READ  0x0001
+#define EC_FOE_OPCODE_WRITE 0x0002
+#define EC_FOE_OPCODE_DATA  0x0003
+#define EC_FOE_OPCODE_ACK   0x0004
+#define EC_FOE_OPCODE_ERROR 0x0005
+#define EC_FOE_OPCODE_BUSY  0x0006
+
+#define EC_FOE_ERRCODE_NOTDEFINED       0x8000 /**< \brief Not defined*/
+#define EC_FOE_ERRCODE_NOTFOUND         0x8001 /**< \brief The file requested by an FoE upload service could not be found on the server*/
+#define EC_FOE_ERRCODE_ACCESS           0x8002 /**< \brief Read or write access to this file not allowed (e.g. due to local control).*/
+#define EC_FOE_ERRCODE_DISKFULL         0x8003 /**< \brief Disk to store file is full or memory allocation exceeded*/
+#define EC_FOE_ERRCODE_ILLEGAL          0x8004 /**< \brief Illegal FoE operation, e.g. service identifier invalid*/
+#define EC_FOE_ERRCODE_PACKENO          0x8005 /**< \brief FoE packet number invalid*/
+#define EC_FOE_ERRCODE_EXISTS           0x8006 /**< \brief The file which is requested to be downloaded does already exist*/
+#define EC_FOE_ERRCODE_NOUSER           0x8007 /**< \brief No User*/
+#define EC_FOE_ERRCODE_BOOTSTRAPONLY    0x8008 /**< \brief FoE only supported in Bootstrap*/
+#define EC_FOE_ERRCODE_NOTINBOOTSTRAP   0x8009 /**< \brief This file may not be accessed in BOOTSTRAP state*/
+#define EC_FOE_ERRCODE_NORIGHTS         0x800A /**< \brief Password invalid*/
+#define EC_FOE_ERRCODE_PROGERROR        0x800B /**< \brief Generic programming error. Should only be returned if  error reason cannot be distinguished*/
+#define EC_FOE_ERRCODE_INVALID_CHECKSUM 0x800C /**< \brief checksum included in the file is invalid*/
+#define EC_FOE_ERRCODE_INVALID_FIRMWARE 0x800D /**< \brief The hardware does not support the downloaded firmware*/
+#define EC_FOE_ERRCODE_NO_FILE          0x800F /**< \brief Do not use (identical with 0x8001)*/
+#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*/
 
 typedef enum {
     EC_DIR_OUTPUT, /**< Values written by the master. */

+ 5 - 1
include/ec_errno.h

@@ -16,10 +16,14 @@
 #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_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_UNKNOWN     255
 

+ 23 - 0
include/ec_foe.h

@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef EC_FOE_H
+#define EC_FOE_H
+
+int ec_foe_write(ec_slave_t *slave,
+                 ec_datagram_t *datagram,
+                 const char *filename,
+                 uint32_t password,
+                 const void *buf,
+                 uint32_t size);
+
+int ec_foe_read(ec_slave_t *slave,
+                ec_datagram_t *datagram,
+                const char *filename,
+                uint32_t password,
+                void *buf,
+                uint32_t maxsize,
+                uint32_t *size);
+#endif

+ 1 - 0
include/ec_master.h

@@ -29,6 +29,7 @@
 #include "ec_cmd.h"
 #include "ec_perf.h"
 #include "ec_version.h"
+#include "ec_foe.h"
 
 #define jiffies ec_htimer_get_time_us()
 

+ 84 - 17
src/ec_cmd.c

@@ -110,6 +110,10 @@ static void ec_master_cmd_show_help(void)
     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
     EC_LOG_RAW("  sii_read -p [idx]                              Read SII\n");
     EC_LOG_RAW("  wc                                             Show master working counter\n");
 #ifdef CONFIG_EC_PERF_ENABLE
@@ -722,13 +726,6 @@ int ethercat(int argc, const char **argv)
         } else if (argc == 4 && strcmp(argv[2], "-p") == 0) {
             // ethercat pdos -p [x]
             ec_cmd_show_slave_pdos(global_cmd_master, atoi(argv[3]));
-            return 0;
-        } else if (argc == 3 && strcmp(argv[2], "-v") == 0) {
-            // ethercat slaves -v
-            for (uint32_t i = 0; i < global_cmd_master->slave_count; i++) {
-                ec_cmd_show_slave_detail(global_cmd_master, i);
-            }
-
             return 0;
         } else {
         }
@@ -737,12 +734,12 @@ int ethercat(int argc, const char **argv)
         if (argc == 3) {
             // ethercat states [state]
             for (uint32_t i = 0; i < global_cmd_master->slave_count; i++) {
-                ec_cmd_slave_state_request(global_cmd_master, i, strtol(argv[4], NULL, 16));
+                ec_cmd_slave_state_request(global_cmd_master, i, strtoul(argv[4], NULL, 16));
             }
             return 0;
         } else if (argc == 5 && strcmp(argv[2], "-p") == 0) {
             // ethercat states -p [x] [state]
-            ec_cmd_slave_state_request(global_cmd_master, atoi(argv[3]), strtol(argv[4], NULL, 16));
+            ec_cmd_slave_state_request(global_cmd_master, atoi(argv[3]), strtoul(argv[4], NULL, 16));
             return 0;
         } else {
         }
@@ -772,8 +769,8 @@ int ethercat(int argc, const char **argv)
         ec_datagram_init(&datagram, 512);
         ret = ec_coe_upload(&global_cmd_master->slaves[slave_idx],
                             &datagram,
-                            strtol(argv[4], NULL, 16),
-                            argc >= 6 ? strtol(argv[5], NULL, 16) : 0x00,
+                            strtoul(argv[4], NULL, 16),
+                            argc >= 6 ? strtoul(argv[5], NULL, 16) : 0x00,
                             output_buffer,
                             sizeof(output_buffer),
                             &actual_size,
@@ -798,7 +795,7 @@ int ethercat(int argc, const char **argv)
             EC_LOG_RAW("No slaves found\n");
             return -1;
         }
-        u32data = strtol(argv[6], NULL, 16);
+        u32data = strtoul(argv[6], NULL, 16);
 
         if (u32data < 0xff)
             size = 1;
@@ -810,8 +807,8 @@ int ethercat(int argc, const char **argv)
         ec_datagram_init(&datagram, 512);
         ret = ec_coe_download(&global_cmd_master->slaves[slave_idx],
                               &datagram,
-                              strtol(argv[4], NULL, 16),
-                              strtol(argv[5], NULL, 16),
+                              strtoul(argv[4], NULL, 16),
+                              strtoul(argv[5], NULL, 16),
                               &u32data,
                               size,
                               false);
@@ -905,7 +902,7 @@ int ethercat(int argc, const char **argv)
                 return -1;
             }
 
-            offset = strtol(argv[4], NULL, 16);
+            offset = strtoul(argv[4], NULL, 16);
 
             size = parse_hex_string(argv[5], hexdata, sizeof(hexdata));
             if (size < 0) {
@@ -920,7 +917,7 @@ int ethercat(int argc, const char **argv)
             }
             return 0;
         } else {
-            offset = strtol(argv[2], NULL, 16);
+            offset = strtoul(argv[2], NULL, 16);
 
             size = parse_hex_string(argv[3], hexdata, sizeof(hexdata));
             if (size < 0) {
@@ -938,7 +935,77 @@ int ethercat(int argc, const char **argv)
             }
             return 0;
         }
-    } else if (argc >= 3 && strcmp(argv[1], "timediff") == 0) {
+    }
+#ifdef CONFIG_EC_FOE
+    else if (argc >= 7 && strcmp(argv[1], "foe_write") == 0) {
+        // 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]);
+        if (slave_idx >= global_cmd_master->slave_count) {
+            EC_LOG_RAW("No slaves found\n");
+            return -1;
+        }
+        const char *filename = argv[4];
+        uint32_t password = strtoul(argv[5], NULL, 16);
+
+        size = parse_hex_string(argv[6], hexdata, sizeof(hexdata));
+        if (size < 0) {
+            return -1;
+        }
+        static ec_datagram_t datagram;
+
+        ec_datagram_init(&datagram, 4096);
+
+        EC_SLAVE_LOG_INFO("Slave %u foe write file %s, password: 0x%08x, size %u\n", slave_idx, filename, password, size);
+
+        ec_osal_mutex_take(global_cmd_master->scan_lock);
+        ret = ec_foe_write(&global_cmd_master->slaves[slave_idx], &datagram, filename, password, hexdata, size);
+        ec_osal_mutex_give(global_cmd_master->scan_lock);
+
+        if (ret < 0) {
+            EC_SLAVE_LOG_ERR("Slave %u foe_write failed: %d\n", slave_idx, ret);
+        } else {
+            EC_LOG_RAW("Slave %u foe write success\n", slave_idx);
+        }
+
+        ec_datagram_clear(&datagram);
+        return 0;
+    } else if (argc >= 6 && strcmp(argv[1], "foe_read") == 0) {
+        // ethercat foe_read -p [slave_idx] [filename] [password]
+        uint8_t hexdata[256];
+        uint32_t size;
+        int ret;
+        uint32_t slave_idx = atoi(argv[3]);
+        if (slave_idx >= global_cmd_master->slave_count) {
+            EC_LOG_RAW("No slaves found\n");
+            return -1;
+        }
+        const char *filename = argv[4];
+        uint32_t password = strtoul(argv[5], NULL, 16);
+
+        EC_SLAVE_LOG_INFO("Slave %u foe read file %s, password: 0x%08x\n", slave_idx, filename, password);
+
+        static ec_datagram_t datagram;
+
+        ec_datagram_init(&datagram, 4096);
+
+        ec_osal_mutex_take(global_cmd_master->scan_lock);
+        ret = ec_foe_read(&global_cmd_master->slaves[slave_idx], &datagram, filename, password, hexdata, sizeof(hexdata), &size);
+        ec_osal_mutex_give(global_cmd_master->scan_lock);
+
+        if (ret < 0) {
+            EC_SLAVE_LOG_ERR("Slave %u foe_read failed: %d\n", slave_idx, ret);
+        } else {
+            ec_hexdump(hexdata, size);
+        }
+
+        ec_datagram_clear(&datagram);
+        return 0;
+    }
+#endif
+    else if (argc >= 3 && strcmp(argv[1], "timediff") == 0) {
         if (strcmp(argv[2], "-s") == 0) {
             uintptr_t flags;
 

+ 37 - 5
src/ec_common.c

@@ -224,8 +224,8 @@ const ec_code_msg_t al_status_messages[] = {
     { 0x0012, "Unknown requested state" },
     { 0x0013, "Bootstrap not supported" },
     { 0x0014, "No valid firmware" },
-    { 0x0015, "Invalid mailbox configuration" },
-    { 0x0016, "Invalid mailbox configuration" },
+    { 0x0015, "Invalid mailbox configuration (BOOT state)" },
+    { 0x0016, "Invalid mailbox configuration (PreOP state)" },
     { 0x0017, "Invalid sync manager configuration" },
     { 0x0018, "No valid inputs available" },
     { 0x0019, "No valid outputs" },
@@ -335,15 +335,47 @@ const ec_code_msg_t sdo_abort_messages[] = {
                   " because of the present device state" },
     { 0x08000023, "Object dictionary dynamic generation fails or no object"
                   " dictionary is present" },
-    {}
+    { 0xffffffff }
 };
 
 const char *ec_sdo_abort_string(uint32_t errorcode)
 {
-    for (uint32_t i = 0; sdo_abort_messages[i].code != 0; i++) {
+    for (uint32_t i = 0; sdo_abort_messages[i].code != 0xffffffff; i++) {
         if (sdo_abort_messages[i].code == errorcode) {
             return sdo_abort_messages[i].message;
         }
     }
     return "Unknown errorcode";
-}
+}
+
+const ec_code_msg_t foe_errcode_messages[] = {
+    { EC_FOE_ERRCODE_NOTDEFINED, "Not defined" },
+    { EC_FOE_ERRCODE_NOTFOUND, "The file requested by an FoE upload service could not be found on the server" },
+    { EC_FOE_ERRCODE_ACCESS, "Read or write access to this file not allowed (e.g. due to local control)" },
+    { EC_FOE_ERRCODE_DISKFULL, "Disk to store file is full or memory allocation exceeded" },
+    { EC_FOE_ERRCODE_ILLEGAL, "Illegal FoE operation, e.g. service identifier invalid" },
+    { EC_FOE_ERRCODE_PACKENO, "FoE packet number invalid" },
+    { EC_FOE_ERRCODE_EXISTS, "The file which is requested to be downloaded does already exist" },
+    { EC_FOE_ERRCODE_NOUSER, "No User" },
+    { EC_FOE_ERRCODE_BOOTSTRAPONLY, "FoE only supported in Bootstrap" },
+    { EC_FOE_ERRCODE_NOTINBOOTSTRAP, "This file may not be accessed in BOOTSTRAP state" },
+    { EC_FOE_ERRCODE_NORIGHTS, "Password invalid" },
+    { EC_FOE_ERRCODE_PROGERROR, "Generic programming error. Should only be returned if  error reason cannot be distinguished" },
+    { EC_FOE_ERRCODE_INVALID_CHECKSUM, "checksum included in the file is invalid" },
+    { EC_FOE_ERRCODE_INVALID_FIRMWARE, "The hardware does not support the downloaded firmware" },
+    { EC_FOE_ERRCODE_NO_FILE, "Do not use (identical with 0x8001)" },
+    { EC_FOE_ERRCODE_NO_FILE_HEADER, "Missing file header of error in file header" },
+    { EC_FOE_ERRCODE_FLASH_ERROR, "Flash cannot be accessed" },
+    { 0xffffffff }
+
+};
+
+const char *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) {
+            return foe_errcode_messages[i].message;
+        }
+    }
+    return "Unknown errorcode";
+}

+ 0 - 0
src/ec_foe.c


+ 3 - 3
src/ec_slave.c

@@ -516,18 +516,18 @@ static int ec_slave_config(ec_master_t *master, ec_slave_t *slave)
         sm_info[0].physical_start_address = slave->sii.boot_rx_mailbox_offset;
         sm_info[0].control = 0x26;
         sm_info[0].length = slave->sii.boot_rx_mailbox_size;
-        sm_info[0].enable = 0x0001;
+        sm_info[0].enable = 0x01;
 
         sm_info[1].physical_start_address = slave->sii.boot_tx_mailbox_offset;
         sm_info[1].control = 0x22;
         sm_info[1].length = slave->sii.boot_tx_mailbox_size;
-        sm_info[1].enable = 0x0001;
+        sm_info[1].enable = 0x01;
 
         // Config mailbox sm
         ec_datagram_fpwr(datagram, slave->station_address, ESCREG_OF(ESCREG->SYNCM[0]), EC_SYNC_PAGE_SIZE * 2);
         ec_datagram_zero(datagram);
         for (uint8_t i = 0; i < 2; i++) {
-            ec_slave_sm_config(sm_info, datagram->data + EC_SYNC_PAGE_SIZE * i);
+            ec_slave_sm_config(&sm_info[i], datagram->data + EC_SYNC_PAGE_SIZE * i);
         }
         datagram->netdev_idx = slave->netdev_idx;
         ret = ec_master_queue_ext_datagram(master, datagram, true, true);