/**************************************************************************** * This file is part of the project AqHome. * AqHome (c) by 2023 Martin Preuss, all rights reserved. * * The license for this file can be found in the file COPYING which you * should have received along with this file. ****************************************************************************/ #ifdef HAVE_CONFIG_H # include #endif #include "./flash.h" #include "../utils.h" #include "aqhome/ipc/endpoint_ipc.h" #include "aqhome/ipc/nodes/msg_ipc_setaccmsggrps.h" #include "aqhome/ipc/nodes/msg_ipc_forward.h" #include "aqhome/msg/msg_node.h" #include "aqhome/msg/msg_flashready.h" #include "aqhome/msg/msg_flashstart.h" #include "aqhome/msg/msg_flashresponse.h" #include "aqhome/msg/msg_flashdata.h" #include "aqhome/msg/msg_flashend.h" #include "aqhome/msg/msg_reboot.h" #include "aqhome/hexfile/hexfile.h" #include "aqhome/hexfile/flashrecord.h" #include #include #include #include #include #include #define I18S(msg) msg #define I18N(msg) GWEN_I18N_Translate(PACKAGE, msg) #define FLASH_TOOL_MAX_REPEAT 16 /*#define DEBUG_FLASH*/ static int _doFlash(GWEN_DB_NODE *dbArgs); static AQH_FLASHRECORD_LIST *_readHexfileIntoFlashRecordList(const char *hexFilename); static int _rebootNode(GWEN_MSG_ENDPOINT *epTcp, unsigned int uid, int timeoutInSeconds); static int _performFlashProcedure(GWEN_MSG_ENDPOINT *epTcp, unsigned int uid, const AQH_FLASHRECORD_LIST *flashRecordList, int pageSize, int timeoutInSeconds); static int _flashStart(GWEN_MSG_ENDPOINT *epTcp, unsigned int uid, int timeoutInSeconds); static GWEN_MSG *_waitForFlashReadyMessageForUid(GWEN_MSG_ENDPOINT *epTcp, unsigned int uid, int timeoutInSeconds); static int _sendRebootRequest(GWEN_MSG_ENDPOINT *epTcp, unsigned int uid); static int _sendFlashStart(GWEN_MSG_ENDPOINT *epTcp, unsigned int uid); static int _waitForFlashResponseMessage(GWEN_MSG_ENDPOINT *epTcp, int timeoutInSeconds); static int _waitForRebootResponseMessage(GWEN_MSG_ENDPOINT *epTcp, int timeoutInSeconds); static int _sendFlashRecord(GWEN_MSG_ENDPOINT *epTcp, const AQH_FLASHRECORD *flashRecord, uint16_t pageSize, int timeoutInSeconds); static int _sendFlashEnd(GWEN_MSG_ENDPOINT *epTcp, int reason); int AQH_Tool_Flash(GWEN_DB_NODE *dbGlobalArgs, int argc, char **argv) { GWEN_DB_NODE *dbLocalArgs; int rv; const GWEN_ARGS args[]= { { GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ GWEN_ArgsType_Char, /* type */ "tcpAddress", /* name */ 0, /* minnum */ 1, /* maxnum */ "t", /* short option */ "tcpaddress", /* long option */ I18S("Specify TCP address to connect to (defaults to 127.0.0.1)"), I18S("Specify TCP address to connect to (defaults to 127.0.0.1)") }, { GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ GWEN_ArgsType_Int, /* type */ "tcpPort", /* name */ 0, /* minnum */ 1, /* maxnum */ "P", /* short option */ "tcpport", /* long option */ I18S("Specify the TCP port to listen on"), I18S("Specify the TCP port to listen on") }, { GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ GWEN_ArgsType_Char, /* type */ "uid", /* name */ 1, /* minnum */ 1, /* maxnum */ "u", /* short option */ "uid", /* long option */ I18S("Specify UID of the node to flash"), I18S("Specify UID of the node to flash)") }, { GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ GWEN_ArgsType_Char, /* type */ "hexFilename", /* name */ 1, /* minnum */ 1, /* maxnum */ "H", /* short option */ NULL, /* long option */ I18S("Specify hexfile to flash"), I18S("Specify hexfile to flash)") }, { GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ GWEN_ArgsType_Int, /* type */ "timeout", /* name */ 0, /* minnum */ 1, /* maxnum */ "T", /* short option */ NULL, /* long option */ I18S("Specify timeout in seconds for PONG response"), I18S("Specify timeout in seconds for PONG response") }, { 0, /* flags */ GWEN_ArgsType_Int, /* type */ "reboot", /* name */ 0, /* minnum */ 1, /* maxnum */ "R", /* short option */ NULL, /* long option */ I18S("Request node reboot before trying to flash"), I18S("Request node reboot before trying to flash") }, { GWEN_ARGS_FLAGS_HELP | GWEN_ARGS_FLAGS_LAST, /* flags */ GWEN_ArgsType_Int, /* type */ "help", /* name */ 0, /* minnum */ 0, /* maxnum */ "h", /* short option */ "help", /* long option */ "Show this help screen", /* short description */ "Show this help screen" /* long description */ } }; dbLocalArgs=GWEN_DB_GetGroup(dbGlobalArgs, GWEN_DB_FLAGS_DEFAULT, "local"); rv=GWEN_Args_Check(argc, argv, 1, GWEN_ARGS_MODE_ALLOW_FREEPARAM, args, dbLocalArgs); if (rv==GWEN_ARGS_RESULT_ERROR) { fprintf(stderr, "ERROR: Could not parse arguments\n"); return 1; } else if (rv==GWEN_ARGS_RESULT_HELP) { GWEN_BUFFER *ubuf; ubuf=GWEN_Buffer_new(0, 1024, 0, 1); if (GWEN_Args_Usage(args, ubuf, GWEN_ArgsOutType_Txt)) { fprintf(stderr, "ERROR: Could not create help string\n"); return 1; } fprintf(stderr, "%s\n", GWEN_Buffer_GetStart(ubuf)); GWEN_Buffer_free(ubuf); return 0; } return _doFlash(dbLocalArgs); } int _doFlash(GWEN_DB_NODE *dbArgs) { GWEN_MSG_ENDPOINT *epTcp; int rv; int timeoutInSeconds; int doReboot; const char *s; unsigned int uid; const char *hexFilename; AQH_FLASHRECORD_LIST *flashRecordList=NULL; GWEN_MSG *msg; uint16_t pageSize; /* read data */ timeoutInSeconds=GWEN_DB_GetIntValue(dbArgs, "timeout", 0, 30); doReboot=GWEN_DB_GetIntValue(dbArgs, "reboot", 0, 0); s=GWEN_DB_GetCharValue(dbArgs, "uid", 0, NULL); if (1!=sscanf(s, "%x", &uid)) { DBG_ERROR(NULL, "Bad uid \"%s\"", s); return 1; } hexFilename=GWEN_DB_GetCharValue(dbArgs, "hexFilename", 0, NULL); flashRecordList=_readHexfileIntoFlashRecordList(hexFilename); if (flashRecordList==NULL) { DBG_ERROR(NULL, "Error reading flash record list from hexfile"); return 2; } /* setup client connection */ epTcp=Utils_SetupNodesClientEndpoint(dbArgs); if (epTcp==NULL) { DBG_ERROR(NULL, "ERROR creating TCP connection"); return 3; } /* declare accepted message type groups */ rv=Utils_SendAcceptedMsgGroups(epTcp, AQH_MSG_TYPEGROUP_FLASH); if (rv<0) { DBG_INFO(NULL, "here (%d)", rv); GWEN_MsgEndpoint_free(epTcp); return 3; } if (doReboot) { fprintf(stdout, "Sending REBOOT request\n"); rv=_rebootNode(epTcp, uid, timeoutInSeconds); if (rv<0) { DBG_INFO(NULL, "here (%d)", rv); GWEN_MsgEndpoint_free(epTcp); return 3; } fprintf(stdout, "Reboot in progress\n"); } /* wait for FLASH_READY message */ fprintf(stdout, "Waiting for node to become ready for flashing\n"); msg=_waitForFlashReadyMessageForUid(epTcp, uid, timeoutInSeconds); if (msg==NULL) { DBG_INFO(NULL, "No FLASH_READY message received."); GWEN_MsgEndpoint_free(epTcp); return 3; } DBG_INFO(NULL, "FLASH_READY message received"); pageSize=AQH_FlashReadyMsg_GetPagesize(msg); fprintf(stdout, "Node is ready for flashing (pagesize=%d bytes)\n", pageSize); GWEN_Msg_free(msg); /* perform flash */ rv=_performFlashProcedure(epTcp, uid, flashRecordList, pageSize, timeoutInSeconds); if (rv<0) { if (rv<0) { DBG_INFO(NULL, "here (%d)", rv); GWEN_MsgEndpoint_free(epTcp); AQH_FlashRecord_List_free(flashRecordList); return 4; } } AQH_FlashRecord_List_free(flashRecordList); GWEN_MsgEndpoint_free(epTcp); return 0; } AQH_FLASHRECORD_LIST *_readHexfileIntoFlashRecordList(const char *hexFilename) { AQH_HEXFILE *hexfile; AQH_FLASHRECORD_LIST *flashRecordList; fprintf(stdout, "Reading hexfile \"%s\"\n", hexFilename); /* read hexfile */ hexfile=AQH_Hexfile_fromFile(hexFilename); if (hexfile==NULL) { DBG_ERROR(NULL, "Error loading hexfile \"%s\"", hexFilename); return NULL; } flashRecordList=AQH_FlashRecord_fromHexfileRecords(AQH_Hexfile_GetRecordList(hexfile)); AQH_Hexfile_free(hexfile); if (flashRecordList==NULL) { DBG_ERROR(NULL, "Error reading flash record list from hexfile"); return NULL; } return flashRecordList; } int _rebootNode(GWEN_MSG_ENDPOINT *epTcp, unsigned int uid, int timeoutInSeconds) { int rv; /* send REBOOT_REQUEST message */ DBG_INFO(NULL, "Sending REBOOT REQUEST message"); rv=_sendRebootRequest(epTcp, uid); if (rv<0) { DBG_INFO(NULL, "here (%d)", rv); return rv; } rv=_waitForRebootResponseMessage(epTcp, timeoutInSeconds); if (rv!=0) { DBG_INFO(NULL, "Bad or no reboot response received (%d).", rv); return rv; } DBG_INFO(NULL, "REBOOT_RESPONSE message received"); return 0; } int _performFlashProcedure(GWEN_MSG_ENDPOINT *epTcp, unsigned int uid, const AQH_FLASHRECORD_LIST *flashRecordList, int pageSize, int timeoutInSeconds) { int rv; const AQH_FLASHRECORD *flashRecord; fprintf(stdout, "Sending FLASH_START\n"); rv=_flashStart(epTcp, uid, timeoutInSeconds); if (rv<0) { DBG_INFO(NULL, "here (%d)", rv); return rv; } /* send FLASH_DATA requests and retrieve FLASH_RSP responses */ flashRecord=AQH_FlashRecord_List_First(flashRecordList); while(flashRecord) { DBG_ERROR(NULL, "Sending flash record at %08x", AQH_FlashRecord_GetAddress(flashRecord)); rv=_sendFlashRecord(epTcp, flashRecord, pageSize, timeoutInSeconds); if (rv!=0) { DBG_ERROR(NULL, "Error sending flash data (%d)", rv); return rv; } flashRecord=AQH_FlashRecord_List_Next(flashRecord); } rv=_sendFlashEnd(epTcp, 0); if (rv<0) { DBG_INFO(NULL, "here (%d)", rv); return rv; } rv=Utils_FlushOutMessageQueue(epTcp, timeoutInSeconds); if (rv<0) { DBG_INFO(NULL, "here (%d)", rv); return rv; } return 0; } int _flashStart(GWEN_MSG_ENDPOINT *epTcp, unsigned int uid, int timeoutInSeconds) { int rv; int i; for (i=0; ipageSize)?pageSize:len; for (i=0;i=FLASH_TOOL_MAX_REPEAT) { DBG_ERROR(NULL, "Too many errors (tried %d times), giving up", i); /* TODO: send flash end message */ return GWEN_ERROR_IO; } ptr+=sendLen; address+=sendLen; len-=sendLen; } /* while */ return 0; }