From c499b93f39bf3508abf344afbea835e4af55e4d1 Mon Sep 17 00:00:00 2001 From: Sebastian Block Date: Sat, 9 Jul 2022 14:17:20 +0200 Subject: [PATCH 1/4] set FOE data size limit to max mailbox length (slave may have larger mailbox in bootstrap) --- soem/ethercatfoe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/soem/ethercatfoe.c b/soem/ethercatfoe.c index 573cfc0c..6bf3615a 100644 --- a/soem/ethercatfoe.c +++ b/soem/ethercatfoe.c @@ -19,7 +19,7 @@ #include "ethercatmain.h" #include "ethercatfoe.h" -#define EC_MAXFOEDATA 512 +#define EC_MAXFOEDATA EC_MAXMBX /** FOE structure. * Used for Read, Write, Data, Ack and Error mailbox packets. From ebbe5da29d969ed0847a1c312d20e8ab345e8859 Mon Sep 17 00:00:00 2001 From: Sebastian Block Date: Sat, 9 Jul 2022 14:56:45 +0200 Subject: [PATCH 2/4] fix segfault if device mailbox is bigger than our buffer --- soem/ethercatbase.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/soem/ethercatbase.c b/soem/ethercatbase.c index ba925639..2cc0bafe 100644 --- a/soem/ethercatbase.c +++ b/soem/ethercatbase.c @@ -325,7 +325,14 @@ int ecx_FPRD(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, wkc = ecx_srconfirm(port, idx, timeout); if (wkc > 0) { - memcpy(data, &(port->rxbuf[idx][EC_HEADERSIZE]), length); + if (length <= EC_MAXMBX) + { + memcpy(data, &(port->rxbuf[idx][EC_HEADERSIZE]), length); + } + else + { + wkc = EC_ERROR; + } } ecx_setbufstat(port, idx, EC_BUF_EMPTY); From 7430e61948514909fc914e4daa694b6d51d0b7d2 Mon Sep 17 00:00:00 2001 From: Sebastian Block Date: Sat, 9 Jul 2022 15:15:53 +0200 Subject: [PATCH 3/4] FoE: resend packet on BUSY answer while writing --- soem/ethercatfoe.c | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/soem/ethercatfoe.c b/soem/ethercatfoe.c index 6bf3615a..98fab120 100644 --- a/soem/ethercatfoe.c +++ b/soem/ethercatfoe.c @@ -309,13 +309,45 @@ int ecx_FOEwrite(ecx_contextt *context, uint16 slave, char *filename, uint32 pas /* otherwise ignore */ if (sendpacket) { - if (!psize) - { - dofinalzero = TRUE; - } psize += segmentdata; p = (uint8 *)p - segmentdata; --sendpacket; + tsize = psize; + if (tsize > maxdata) + { + tsize = maxdata; + } + if(tsize || dofinalzero) + { + worktodo = TRUE; + dofinalzero = FALSE; + segmentdata = tsize; + psize -= segmentdata; + /* if last packet was full size, add a zero size packet as final */ + /* EOF is defined as packetsize < full packetsize */ + if (!psize && (segmentdata == maxdata)) + { + dofinalzero = TRUE; + } + FOEp->MbxHeader.length = htoes(0x0006 + segmentdata); + FOEp->MbxHeader.address = htoes(0x0000); + FOEp->MbxHeader.priority = 0x00; + /* get new mailbox count value */ + cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt); + context->slavelist[slave].mbx_cnt = cnt; + FOEp->MbxHeader.mbxtype = ECT_MBXT_FOE + (cnt << 4); /* FoE */ + FOEp->OpCode = ECT_FOE_DATA; + sendpacket++; + FOEp->PacketNumber = htoel(sendpacket); + memcpy(&FOEp->Data[0], p, segmentdata); + p = (uint8 *)p + segmentdata; + /* send FoE data to slave */ + wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM); + if (wkc <= 0) + { + worktodo = FALSE; + } + } } break; } From 7058a0b6abd6b690bdd291a603c2d81eee74e736 Mon Sep 17 00:00:00 2001 From: Sebastian Block Date: Sat, 9 Jul 2022 15:45:44 +0200 Subject: [PATCH 4/4] improve firmware update example It can handle write while busy and wait for slaves which update in BOOT2INIT transition. --- CMakeLists.txt | 3 +- test/linux/firm_update/CMakeLists.txt | 5 + test/linux/firm_update/firm_update.c | 145 +++++++++++++++++++++----- 3 files changed, 126 insertions(+), 27 deletions(-) create mode 100644 test/linux/firm_update/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index baf26bd5..06d30723 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -105,9 +105,10 @@ install(FILES ${OSHW_HEADERS} DESTINATION ${SOEM_INCLUDE_INSTALL_DIR}) -if(BUILD_TESTS) +if(BUILD_TESTS) add_subdirectory(test/simple_ng) add_subdirectory(test/linux/slaveinfo) add_subdirectory(test/linux/eepromtool) add_subdirectory(test/linux/simple_test) + add_subdirectory(test/linux/firm_update) endif() diff --git a/test/linux/firm_update/CMakeLists.txt b/test/linux/firm_update/CMakeLists.txt new file mode 100644 index 00000000..55815cbb --- /dev/null +++ b/test/linux/firm_update/CMakeLists.txt @@ -0,0 +1,5 @@ + +set(SOURCES firm_update.c) +add_executable(firm_update ${SOURCES}) +target_link_libraries(firm_update soem) +install(TARGETS firm_update DESTINATION bin) diff --git a/test/linux/firm_update/firm_update.c b/test/linux/firm_update/firm_update.c index b59374f6..2686d5e5 100644 --- a/test/linux/firm_update/firm_update.c +++ b/test/linux/firm_update/firm_update.c @@ -20,26 +20,26 @@ #include "ethercat.h" -#define FWBUFSIZE (8 * 1024 * 1024) +#define FWBUFSIZE (20 * 1024 * 1024) uint8 ob; uint16 ow; uint32 data; char filename[256]; -char filebuffer[FWBUFSIZE]; // 8MB buffer +char filebuffer[FWBUFSIZE]; // 20MB buffer int filesize; int j; uint16 argslave; int input_bin(char *fname, int *length) { - FILE *fp; + FILE *fp; int cc = 0, c; - fp = fopen(fname, "rb"); - if(fp == NULL) - return 0; + fp = fopen(fname, "rb"); + if(fp == NULL) + return 0; while (((c = fgetc(fp)) != EOF) && (cc < FWBUFSIZE)) filebuffer[cc++] = (uint8)c; *length = cc; @@ -48,8 +48,9 @@ int input_bin(char *fname, int *length) } -void boottest(char *ifname, uint16 slave, char *filename) +int boottest(char *ifname, uint16 slave, char *filename) { + int retval = 0; printf("Starting firmware update example\n"); /* initialise SOEM, bind socket to ifname */ @@ -61,18 +62,48 @@ void boottest(char *ifname, uint16 slave, char *filename) if ( ec_config_init(FALSE) > 0 ) { + int counter = 3; + char sm_zero[sizeof(ec_smt)*2]; + char fmmu_zero[sizeof(ec_fmmut)]; + unsigned char rd_stat = 0xff; + + memset(sm_zero, 0, sizeof(sm_zero)); + memset(fmmu_zero, 0, sizeof(fmmu_zero)); + printf("%d slaves found and configured.\n",ec_slavecount); - /* wait for all slaves to reach PRE_OP state */ - ec_statecheck(0, EC_STATE_PRE_OP, EC_TIMEOUTSTATE * 4); + counter = 3; + do { + /* wait for slave to reach PREOP state */ + if (ec_statecheck(slave, EC_STATE_PRE_OP, EC_TIMEOUTSTATE * 4) == EC_STATE_PRE_OP) { + printf("Slave is in PREOP state\n"); + counter = 0; + } + } + while (counter-- > 0); - printf("Request init state for slave %d\n", slave); - ec_slave[slave].state = EC_STATE_INIT; - ec_writestate(slave); + counter = 3; + do { + printf("Request init state for slave %d\n", slave); + ec_slave[slave].state = EC_STATE_INIT; + ec_writestate(slave); + + /* wait for slave to reach INIT state */ + if (ec_statecheck(slave, EC_STATE_INIT, EC_TIMEOUTSTATE * 4) == EC_STATE_INIT) { + counter = 0; + } + } + while (counter-- > 0); + printf("Slave %d state to INIT. counter: %d\n", slave, counter); - /* wait for slave to reach INIT state */ - ec_statecheck(slave, EC_STATE_INIT, EC_TIMEOUTSTATE * 4); - printf("Slave %d state to INIT.\n", slave); + /* clean FMMUs */ + ec_FPWR(ec_slave[slave].configadr, ECT_REG_FMMU0, sizeof(fmmu_zero), fmmu_zero, EC_TIMEOUTRET3); + ec_FPWR(ec_slave[slave].configadr, ECT_REG_FMMU1, sizeof(fmmu_zero), fmmu_zero, EC_TIMEOUTRET3); + + /* clean all SM0 / SM1 to set new bootstrap values later */ + ec_FPWR(ec_slave[slave].configadr, ECT_REG_SM0, sizeof(ec_smt) * 2, sm_zero, EC_TIMEOUTRET3); + ec_FPRD(ec_slave[slave].configadr, ECT_REG_SM1STAT, 1, &rd_stat, EC_TIMEOUTRET3); + printf("Read back Status of SM1: %02x\n", rd_stat); /* read BOOT mailbox data, master -> slave */ data = ec_readeeprom(slave, ECT_SII_BOOTRXMBX, EC_TIMEOUTEEP); @@ -101,9 +132,23 @@ void boottest(char *ifname, uint16 slave, char *filename) /* program SM1 mailbox out for slave */ ec_FPWR (ec_slave[slave].configadr, ECT_REG_SM1, sizeof(ec_smt), &ec_slave[slave].SM[1], EC_TIMEOUTRET); - printf("Request BOOT state for slave %d\n", slave); - ec_slave[slave].state = EC_STATE_BOOT; - ec_writestate(slave); + /* give EEPROM control to PDI */ + rd_stat = 1; + ec_FPWR (ec_slave[slave].configadr, ECT_REG_EEPCFG, 1, &rd_stat, EC_TIMEOUTRET); + + counter = 3; + do { + uint16 ret; + printf("Request BOOT state for slave %d\n", slave); + ec_slave[slave].state = EC_STATE_BOOT; + printf("ec_writestate returned: %d\n", ec_writestate(slave)); + ret = ec_statecheck(slave, EC_STATE_BOOT, EC_TIMEOUTSTATE ); + printf("ec_statecheck returned: %d\n", ret); + if (ret == EC_STATE_BOOT) + counter = 0; + } while(counter-- > 0); + + printf("check BOOT state again\n"); /* wait for slave to reach BOOT state */ if (ec_statecheck(slave, EC_STATE_BOOT, EC_TIMEOUTSTATE * 10) == EC_STATE_BOOT) @@ -112,22 +157,67 @@ void boottest(char *ifname, uint16 slave, char *filename) if (input_bin(filename, &filesize)) { + char *short_filename = strrchr(filename, '/'); /* search for last / */ + if (short_filename) + short_filename++; /* jump over the \ */ + else + short_filename = filename; /* no \ found -> must be a short filename */ printf("File read OK, %d bytes.\n",filesize); - printf("FoE write...."); - j = ec_FOEwrite(slave, filename, 0, filesize , &filebuffer, EC_TIMEOUTSTATE); + printf("FoE write %s....\n", short_filename); + j = ec_FOEwrite(slave, short_filename, 0, filesize , &filebuffer, EC_TIMEOUTSTATE); printf("result %d.\n",j); - printf("Request init state for slave %d\n", slave); - ec_slave[slave].state = EC_STATE_INIT; - ec_writestate(slave); + if (j == 0) retval = 5; + else if (j != 1) retval = j; + + if (retval == 0) { + /* wait max 600s => 10 minutes for firmware update to complete */ + counter = 60; + retval = 6; /* only success switch to init state afterwards completes firmware update */ + do { + uint16 ret; + printf("Request init state for slave %d (counter: %d)\n", slave, counter); + ec_slave[slave].state = EC_STATE_INIT; + ec_writestate(slave); + ret = ec_statecheck(slave, EC_STATE_INIT, 10000000); + if (ret == 0) { + int count; + if ((count = ec_config_init(FALSE)) >= slave) { + ec_slave[slave].state = EC_STATE_INIT; + ec_writestate(slave); + ret = ec_statecheck(slave, EC_STATE_INIT, EC_TIMEOUTSTATE * 10); + } + else { + printf("Slaves found: %d Needed: %d .. will try it again\n", count, slave); +#ifndef WIN32 + sleep(10); +#endif + ret = 25555; + } + } + if (ret == EC_STATE_INIT) { + counter = 0; + printf("State change to init success\n"); + retval = 0; + } + } while (counter-- > 0); + } } else - printf("File not read OK.\n"); + { + printf("File not read OK.\n"); + retval = 4; + } + } + else { + printf("Failed to enter boot mode\n"); + retval = 3; } } else { printf("No slaves found!\n"); + retval = 2; } printf("End firmware update example, close socket\n"); /* stop SOEM, close socket */ @@ -136,17 +226,20 @@ void boottest(char *ifname, uint16 slave, char *filename) else { printf("No socket connection on %s\nExcecute as root\n",ifname); + retval = 1; } + return retval; } int main(int argc, char *argv[]) { + int ret = 0; printf("SOEM (Simple Open EtherCAT Master)\nFirmware update example\n"); if (argc > 3) { argslave = atoi(argv[2]); - boottest(argv[1], argslave, argv[3]); + ret = boottest(argv[1], argslave, argv[3]); } else { @@ -158,5 +251,5 @@ int main(int argc, char *argv[]) } printf("End program\n"); - return (0); + return ret; }