From 6f5ab3b0b8d376c7b7714d0dfe82799a10f5b304 Mon Sep 17 00:00:00 2001 From: Martin Preuss Date: Thu, 20 Apr 2023 00:43:35 +0200 Subject: [PATCH] aqhome-tool: started working on flash module --- apps/aqhome-tool/0BUILD | 2 + apps/aqhome-tool/flash.c | 387 +++++++++++++++++++++++++++++++++++++++ apps/aqhome-tool/flash.h | 20 ++ apps/aqhome-tool/main.c | 2 + apps/aqhome-tool/utils.c | 3 +- 5 files changed, 412 insertions(+), 2 deletions(-) create mode 100644 apps/aqhome-tool/flash.c create mode 100644 apps/aqhome-tool/flash.h diff --git a/apps/aqhome-tool/0BUILD b/apps/aqhome-tool/0BUILD index 32b31e1..151a413 100644 --- a/apps/aqhome-tool/0BUILD +++ b/apps/aqhome-tool/0BUILD @@ -34,6 +34,7 @@ ping.h + flash.h utils.h @@ -41,6 +42,7 @@ $(local/typefiles) main.c ping.c + flash.c utils.c diff --git a/apps/aqhome-tool/flash.c b/apps/aqhome-tool/flash.c new file mode 100644 index 0000000..87eea0f --- /dev/null +++ b/apps/aqhome-tool/flash.c @@ -0,0 +1,387 @@ +/**************************************************************************** + * 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/msg_ipc_setaccmsggrps.h" +#include "aqhome/ipc/msg_ipc_forward.h" +#include "aqhome/ipc/endpoint_ipc_tcpc.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/hexfile/hexfile.h" +#include "aqhome/hexfile/flashrecord.h" + +#include +#include +#include +#include +#include + +#include +#include + + +#define I18S(msg) msg +#define I18N(msg) GWEN_I18N_Translate(PACKAGE, msg) + +#define FLASH_TOOL_MAX_REPEAT 16 + + +static int _doFlash(GWEN_DB_NODE *dbArgs); +static GWEN_MSG *_waitForFlashReadyMessageForUid(GWEN_MSG_ENDPOINT_MGR *emgr, GWEN_MSG_ENDPOINT *epTcp, + unsigned int uid, int timeoutInSeconds); +static int _sendFlashStart(GWEN_MSG_ENDPOINT *epTcp, unsigned int uid); +static int _waitForFlashResponseMessage(GWEN_MSG_ENDPOINT_MGR *emgr, GWEN_MSG_ENDPOINT *epTcp, int timeoutInSeconds); +static int _sendFlashData(GWEN_MSG_ENDPOINT_MGR *emgr, + GWEN_MSG_ENDPOINT *epTcp, + const AQH_FLASHRECORD *flashRecord, + uint16_t pageSize, + int timeoutInSeconds); + + + +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") + }, + { + 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_MGR *emgr; + GWEN_MSG_ENDPOINT *epTcp; + int rv; + int timeoutInSeconds; + const char *s; + unsigned int uid; + const char *hexFilename; + AQH_FLASHRECORD_LIST *flashRecordList=NULL; + AQH_FLASHRECORD *flashRecord; + GWEN_MSG *msg; + uint16_t pageSize; + + /* read data */ + timeoutInSeconds=GWEN_DB_GetIntValue(dbArgs, "timeout", 0, 30); + 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); + if (!(hexFilename && *hexFilename)) { + DBG_ERROR(NULL, "Missing or invalid hexfilename"); + return 1; + } + else { + AQH_HEXFILE *hexfile; + + /* read hexfile */ + hexfile=AQH_Hexfile_fromFile(hexFilename); + if (hexfile==NULL) { + DBG_ERROR(NULL, "Error loading hexfile \"%s\"", hexFilename); + return 2; + } + 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 2; + } + } + + /* setup client connection */ + emgr=GWEN_MsgEndpointMgr_new(); + + epTcp=Utils_SetupIpcEndpoint(emgr, dbArgs); + if (epTcp==NULL) { + DBG_ERROR(NULL, "ERROR creating TCP connection"); + return 3; + } + GWEN_MsgEndpointMgr_AddEndpoint(emgr, epTcp); + + /* declare accepted message type groups */ + rv=Utils_SendAcceptedMsgGroups(epTcp, AQH_MSG_TYPEGROUP_FLASH); + if (rv<0) { + DBG_INFO(NULL, "here (%d)", rv); + return 3; + } + + /* wait for FLASH_READY message */ + msg=_waitForFlashReadyMessageForUid(emgr, epTcp, uid, timeoutInSeconds); + if (msg==NULL) { + DBG_INFO(NULL, "No FLASH_READY message received."); + GWEN_MsgEndpointMgr_free(emgr); + return 3; + } + DBG_INFO(NULL, "FLASH_READY message received"); + pageSize=AQH_FlashReadyMsg_GetPagesize(msg); + GWEN_Msg_free(msg); + + /* send FLASH_START message */ + DBG_INFO(NULL, "Sending FLASH START message"); + rv=_sendFlashStart(epTcp, uid); + if (rv<0) { + DBG_INFO(NULL, "here (%d)", rv); + GWEN_MsgEndpointMgr_free(emgr); + return 3; + } + + /* wait for response */ + rv=_waitForFlashResponseMessage(emgr, epTcp, timeoutInSeconds); + if (rv!=0) { + DBG_INFO(NULL, "Bad or no response received (%d).", rv); + GWEN_MsgEndpointMgr_free(emgr); + return 3; + } + DBG_INFO(NULL, "FLASH_RESPONSE message received"); + + flashRecord=AQH_FlashRecord_List_First(flashRecordList); + while(flashRecord) { + int rv; + + rv=_sendFlashData(emgr, epTcp, flashRecord, pageSize, timeoutInSeconds); + if (rv!=0) { + DBG_ERROR(NULL, "Error sending flash data (%d)", rv); + AQH_FlashRecord_List_free(flashRecordList); + GWEN_MsgEndpointMgr_free(emgr); + return 3; + } + flashRecord=AQH_FlashRecord_List_Next(flashRecord); + } + + GWEN_MsgEndpointMgr_free(emgr); + return 0; +} + + + +GWEN_MSG *_waitForFlashReadyMessageForUid(GWEN_MSG_ENDPOINT_MGR *emgr, GWEN_MSG_ENDPOINT *epTcp, + unsigned int uid, int timeoutInSeconds) +{ + int i; + + for (i=0;i<16;i++) { + GWEN_MSG *msg; + unsigned int receivedUid; + + msg=Utils_WaitForSpecificNodeMessage(emgr, epTcp, AQH_MSG_TYPE_FLASH_READY, 0, timeoutInSeconds); + if (msg==NULL) { + DBG_INFO(NULL, "No FLASH_READY message received."); + return NULL; + } + + receivedUid=AQH_FlashReadyMsg_GetUid(msg); + if (receivedUid==uid) { + DBG_INFO(NULL, "Received FLASH_READY message for given uid"); + return msg; + } + else { + DBG_INFO(NULL, "Received FLASH_READY message but not for given uid (%08x)", receivedUid); + } + GWEN_Msg_free(msg); + } + return NULL; +} + + + +int _waitForFlashResponseMessage(GWEN_MSG_ENDPOINT_MGR *emgr, GWEN_MSG_ENDPOINT *epTcp, int timeoutInSeconds) +{ + GWEN_MSG *msg; + int responseCode; + + msg=Utils_WaitForSpecificNodeMessage(emgr, epTcp, AQH_MSG_TYPE_FLASH_RSP, 0, timeoutInSeconds); + if (msg==NULL) { + DBG_INFO(NULL, "No FLASH_RSP message received."); + return GWEN_ERROR_IO; + } + responseCode=AQH_FlashResponseMsg_GetCode(msg); + GWEN_Msg_free(msg); + return responseCode; +} + + + +int _sendFlashStart(GWEN_MSG_ENDPOINT *epTcp, unsigned int uid) +{ + GWEN_MSG *msgNode; + GWEN_MSG *msgOut; + + msgNode=AQH_FlashStartMsg_new(0, 0xc1, AQH_MSG_TYPE_FLASH_START, uid, 0, 0); + if (msgNode==NULL) { + DBG_ERROR(NULL, "Error creating message"); + return GWEN_ERROR_GENERIC; + } + + msgOut=AQH_ForwardIpcMsg_new(AQH_MSGTYPE_IPC_FORWARD, GWEN_Msg_GetConstBuffer(msgNode), GWEN_Msg_GetBytesInBuffer(msgNode)); + GWEN_MsgEndpoint_AddSendMessage(epTcp, msgOut); + GWEN_Msg_free(msgNode); + return 0; +} + + + +int _sendFlashData(GWEN_MSG_ENDPOINT_MGR *emgr, + GWEN_MSG_ENDPOINT *epTcp, + const AQH_FLASHRECORD *flashRecord, + uint16_t pageSize, + int timeoutInSeconds) +{ + const uint8_t *ptr; + uint32_t len; + uint32_t address; + + ptr=AQH_FlashRecord_GetDataPointer(flashRecord); + len=AQH_FlashRecord_GetDataLength(flashRecord); + address=AQH_FlashRecord_GetAddress(flashRecord); + while(ptr && len) { + uint32_t sendLen; + int i; + + sendLen=(len>pageSize)?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; +} + + + + + + diff --git a/apps/aqhome-tool/flash.h b/apps/aqhome-tool/flash.h new file mode 100644 index 0000000..65b778a --- /dev/null +++ b/apps/aqhome-tool/flash.h @@ -0,0 +1,20 @@ +/**************************************************************************** + * 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. + ****************************************************************************/ + +#ifndef AQHOME_TOOL_FLASH_H +#define AQHOME_TOOL_FLASH_H + + +#include + + +int AQH_Tool_Flash(GWEN_DB_NODE *dbArgs, int argc, char **argv); + + +#endif + diff --git a/apps/aqhome-tool/main.c b/apps/aqhome-tool/main.c index 8852533..fb04834 100644 --- a/apps/aqhome-tool/main.c +++ b/apps/aqhome-tool/main.c @@ -11,6 +11,7 @@ #endif #include "./ping.h" +#include "./flash.h" #include #include @@ -65,6 +66,7 @@ int main(int argc, char **argv) }; const GWEN_FUNCS cmdDefArray[]= { GWEN_FE_DAH("ping", AQH_Tool_Ping, I18N("Ping a given node on the network")), + GWEN_FE_DAH("flash", AQH_Tool_Flash, I18N("Flash a given node on the network")), GWEN_FE_END(), }; const GWEN_FUNCS *func; diff --git a/apps/aqhome-tool/utils.c b/apps/aqhome-tool/utils.c index 183329a..04f60cb 100644 --- a/apps/aqhome-tool/utils.c +++ b/apps/aqhome-tool/utils.c @@ -73,8 +73,7 @@ GWEN_MSG *Utils_WaitForSpecificNodeMessage(GWEN_MSG_ENDPOINT_MGR *emgr, GWEN_MSG "Received node msg from %d (%d)", AQH_NodeMsg_GetSourceAddress(nodeMsg), AQH_NodeMsg_GetMsgType(nodeMsg)); - if (AQH_NodeMsg_GetMsgType(nodeMsg)==msgCode && - AQH_NodeMsg_GetSourceAddress(nodeMsg)==nodeAddr) { + if (AQH_NodeMsg_GetMsgType(nodeMsg)==msgCode && (nodeAddr==0 || AQH_NodeMsg_GetSourceAddress(nodeMsg)==nodeAddr)) { GWEN_Msg_free(msg); return nodeMsg; }