From 2f2bcb7d09b0fb6ea9a261b14f9c3bda75943b85 Mon Sep 17 00:00:00 2001 From: Sebastian Block Date: Fri, 26 Aug 2016 15:15:01 +0200 Subject: [PATCH 1/4] fix segfault if device mailbox is bigger than our buffer --- soem/ethercatbase.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/soem/ethercatbase.c b/soem/ethercatbase.c index f5b7c4a..fefe37d 100644 --- a/soem/ethercatbase.c +++ b/soem/ethercatbase.c @@ -361,7 +361,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); @@ -670,4 +677,4 @@ int ec_LRWDC(uint32 LogAdr, uint16 length, void *data, uint16 DCrs, int64 *DCtim { return ecx_LRWDC(&ecx_port, LogAdr, length, data, DCrs, DCtime, timeout); } -#endif \ No newline at end of file +#endif From b5aa8c4374b5f20068da3a285e60baa298b671d4 Mon Sep 17 00:00:00 2001 From: Sebastian Block Date: Fri, 26 Aug 2016 15:17:55 +0200 Subject: [PATCH 2/4] increase possible foe data size limit to max mailbox length (some slave have large mailbox in bootstrap) --- soem/ethercatfoe.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/soem/ethercatfoe.c b/soem/ethercatfoe.c index 26d6fb4..658f02a 100644 --- a/soem/ethercatfoe.c +++ b/soem/ethercatfoe.c @@ -57,8 +57,6 @@ #include "ethercatmain.h" #include "ethercatfoe.h" -#define EC_MAXFOEDATA 512 - /** FOE structure. * Used for Read, Write, Data, Ack and Error mailbox packets. */ @@ -76,9 +74,9 @@ typedef struct PACKED }; union { - char FileName[EC_MAXFOEDATA]; - uint8 Data[EC_MAXFOEDATA]; - char ErrorText[EC_MAXFOEDATA]; + char FileName[EC_MAXMBX]; + uint8 Data[EC_MAXMBX]; + char ErrorText[EC_MAXMBX]; }; } ec_FOEt; PACKED_END From 8f1cc8dad0b0b72d46702b6e2c922d22236194fe Mon Sep 17 00:00:00 2001 From: Sebastian Block Date: Fri, 26 Aug 2016 15:19:41 +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 658f02a..9816995 100644 --- a/soem/ethercatfoe.c +++ b/soem/ethercatfoe.c @@ -345,13 +345,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 f7dce28f6803b1c90397b6dd9a88a1ffd2ae7d13 Mon Sep 17 00:00:00 2001 From: Sebastian Block Date: Fri, 26 Aug 2016 15:21:31 +0200 Subject: [PATCH 4/4] improve firmware update example (can handle write while busy and wait for slaves which update in BOOT2INIT transition --- test/linux/firm_update/CMakeLists.txt | 14 +++ test/linux/firm_update/firm_update.c | 133 ++++++++++++++++++++++---- 2 files changed, 128 insertions(+), 19 deletions(-) create mode 100644 test/linux/firm_update/CMakeLists.txt diff --git a/test/linux/firm_update/CMakeLists.txt b/test/linux/firm_update/CMakeLists.txt new file mode 100644 index 0000000..b298281 --- /dev/null +++ b/test/linux/firm_update/CMakeLists.txt @@ -0,0 +1,14 @@ + +set(SOURCES firm_update.c) +add_executable(firm_update ${SOURCES}) +target_link_libraries(firm_update soem) + +if(WIN32) + target_link_libraries(firm_update wpcap.lib Packet.lib Ws2_32.lib Winmm.lib) +elseif(UNIX) + target_link_libraries(firm_update pthread rt) +elseif(${CMAKE_SYSTEM_NAME} MATCHES "rt-kernel") + target_link_libraries(firm_update "-Wl,--start-group -l${BSP} -l${ARCH} -lkern -ldev -lsio -lblock -lfs -lusb -llwip -leth -li2c -lrtc -lcan -lnand -lspi -lnor -lpwm -ladc -ltrace -lc -lm -Wl,--end-group") +endif() + +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 0cd511e..cb01ae0 100644 --- a/test/linux/firm_update/firm_update.c +++ b/test/linux/firm_update/firm_update.c @@ -20,13 +20,13 @@ #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; @@ -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,15 +62,47 @@ 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); + 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); + + 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 */ - ec_statecheck(slave, EC_STATE_INIT, EC_TIMEOUTSTATE * 4); - printf("Slave %d state to INIT.\n", slave); + 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); + + /* 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); @@ -98,9 +131,24 @@ 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) @@ -109,22 +157,65 @@ 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); - } - else - printf("File not read OK.\n"); - } + 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"); + 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 */ @@ -133,17 +224,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 { @@ -155,5 +249,6 @@ int main(int argc, char *argv[]) } printf("End program\n"); - return (0); + return ret; } +