Pārlūkot izejas kodu

Erster Release. v1.0

Rind 5 gadi atpakaļ
vecāks
revīzija
99481dd999
7 mainītis faili ar 1439 papildinājumiem un 1382 dzēšanām
  1. 1021 1066
      cmdopt.c
  2. 13 15
      cmdopt.h
  3. 2 0
      error.c
  4. 1 0
      error.h
  5. 192 261
      main.c
  6. 203 38
      modbmst.c
  7. 7 2
      modbmst.h

+ 1021 - 1066
cmdopt.c

@@ -1,1066 +1,1021 @@
-#include <stdio.h>
-#include <stdint.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <limits.h>
-#include <string.h>
-#include <ctype.h>
-#include <unistd.h>
-#include <stddef.h>
-#include <time.h>
-#include <errno.h>
-#include <getopt.h>
-#include <gfaserial.h>
-#include <gfabootlmast.h>
-#include "main.h"
-#include "cmdopt.h"
-#include "error.h"
-#include "output.h"
-
-/////////////////////////////////////////////////////////////////////////////
-
-#define _IS_POWER_OF_2(x)						(!!(x) && !((x) & ((x) - 1)))
-#define _IS_VALID_STATION_NUMBER(s)				(((s) > 0) && ((s) <= 99))
-#define _IS_VALID_BLOCK_SIZE(s)					(((s) >= GFA_BOOTLOADER_MIN_SEND_DATA_BLOCK_SIZE) && ((s) <= GFA_BOOTLOADER_MAX_SEND_DATA_BLOCK_SIZE) && !((s) & 0x03))
-
-/////////////////////////////////////////////////////////////////////////////
-
-#define _OV_HAS_UPLOAD_IMG						((uint32_t)0x00000001)
-#define _OV_HAS_SHOW_FILE_IMG_INFO				((uint32_t)0x00000002)
-#define _OV_HAS_SHOW_DEV_IMG_INFO				((uint32_t)0x00000004)
-#define _OV_HAS_VALIDATE_IMG					((uint32_t)0x00000008)
-#define _OV_HAS_SHOW_MAT_SER					((uint32_t)0x00000010)
-#define _OV_HAS_SET_MAT_SER						((uint32_t)0x00000020)
-#define _OV_HAS_BOOT_PING						((uint32_t)0x00000040)
-#define _OV_HAS_START_BOOT						((uint32_t)0x00000080)
-#define _OV_HAS_RESET_BOOT						((uint32_t)0x00000100)
-#define _OV_HAS_RESCUE_BOOT						((uint32_t)0x00000200)
-#define _OV_HAS_MODBUS_START_BOOT				((uint32_t)0x00000400)
-
-#define _OV_HAS_IMG_FILE						((uint32_t)0x00001000)
-#define _OV_HAS_ITF_NAME						((uint32_t)0x00002000)
-#define _OV_HAS_BLOCK_SIZE						((uint32_t)0x00004000)
-#define _OV_HAS_APP_ADDR						((uint32_t)0x00008000)
-#define _OV_HAS_X_BAUD_RATE						((uint32_t)0x00010000)
-#define _OV_HAS_STATION_NUMBER					((uint32_t)0x00020000)
-#define _OV_HAS_NODE_ADDR						((uint32_t)0x00040000)
-#define _OV_HAS_SLAVE_ADDR						((uint32_t)0x00080000)
-#define _OV_HAS_MATERIAL						((uint32_t)0x00100000)
-#define _OV_HAS_SERIAL							((uint32_t)0x00200000)
-#define _OV_HAS_PING_INTERVAL					((uint32_t)0x00400000)
-#define _OV_HAS_NO_SHOW_PROGRESS				((uint32_t)0x00800000)
-#define _OV_HAS_PAGE_ERASE_TIME					((uint32_t)0x01000000)
-#define _OV_HAS_MODBUS_SLAVE_ID					((uint32_t)0x02000000)
-#define _OV_HAS_MODBUS_CTRL_REG					((uint32_t)0x04000000)
-//#define _OV_HAS_INIT_BAUD_RATE					((uint32_t)0x08000000)
-//#define _OV_HAS_MODBUS_BAUD_RATE				((uint32_t)0x10000000)
-//#define _OV_HAS_MODBUS_PARITY					((uint32_t)0x20000000)
-
-/////////////////////////////////////////////////////////////////////////////
-
-#define _OV_CMD_MASK							((uint32_t)0x00000FFF)
-#define _OV_OPT_MASK							(~_OV_CMD_MASK)
-
-/////////////////////////////////////////////////////////////////////////////
-
-#define _REQ_CONNECTION_OPTS					(_OV_HAS_ITF_NAME | _OV_HAS_SLAVE_ADDR)
-
-#define _REQ_OPTS_UPLOAD_IMG					(_OV_HAS_IMG_FILE | _REQ_CONNECTION_OPTS)
-#define _REQ_OPTS_SHOW_IMG_INFO_OFFLINE			_OV_HAS_IMG_FILE
-#define _REQ_OPTS_SHOW_IMG_INFO_ONLINE			_REQ_CONNECTION_OPTS
-#define _REQ_OPTS_VALIDATE_IMG					(_OV_HAS_IMG_FILE | _REQ_CONNECTION_OPTS)
-#define _REQ_OPTS_SHOW_MAT_SER					_REQ_CONNECTION_OPTS
-#define _REQ_OPTS_SET_MAT_SER					(_REQ_CONNECTION_OPTS | _OV_HAS_MATERIAL | _OV_HAS_SERIAL)
-#define _REQ_OPTS_BOOT_PING						_REQ_CONNECTION_OPTS
-#define _REQ_OPTS_START_BOOT					_REQ_CONNECTION_OPTS
-#define _REQ_OPTS_RESET_BOOT					_REQ_CONNECTION_OPTS
-#define _REQ_OPTS_RESCUE_BOOT					_REQ_CONNECTION_OPTS
-#define _REQ_OPTS_MODBUS_START_BOOT				(_OV_HAS_ITF_NAME | _OV_HAS_MODBUS_SLAVE_ID)
-
-/////////////////////////////////////////////////////////////////////////////
-
-#define _OPT_CONNECTION_OPTS					(/*_OV_HAS_INIT_BAUD_RATE | */_OV_HAS_X_BAUD_RATE | _OV_HAS_STATION_NUMBER | _OV_HAS_NODE_ADDR | _OV_HAS_MODBUS_SLAVE_ID)
-
-#define _OPT_OPTS_UPLOAD_IMG					(_OV_HAS_IMG_FILE | _OV_HAS_APP_ADDR | _OPT_CONNECTION_OPTS | _OV_HAS_NO_SHOW_PROGRESS | _OV_HAS_PAGE_ERASE_TIME)
-#define _OPT_OPTS_SHOW_IMG_INFO_OFFLINE			_OV_HAS_APP_ADDR
-#define _OPT_OPTS_SHOW_IMG_INFO_ONLINE			(_OPT_CONNECTION_OPTS | _OV_HAS_APP_ADDR)
-#define _OPT_OPTS_VALIDATE_IMG					(_OV_HAS_STATION_NUMBER | _OV_HAS_NODE_ADDR | _OV_HAS_APP_ADDR/* | _OV_HAS_INIT_BAUD_RATE*/)
-#define _OPT_OPTS_SHOW_MAT_SER					(_OV_HAS_STATION_NUMBER | _OV_HAS_NODE_ADDR/* | _OV_HAS_INIT_BAUD_RATE*/)
-#define _OPT_OPTS_SET_MAT_SER					(_OV_HAS_STATION_NUMBER | _OV_HAS_NODE_ADDR/* | _OV_HAS_INIT_BAUD_RATE*/)
-#define _OPT_OPTS_BOOT_PING						(_OV_HAS_STATION_NUMBER | _OV_HAS_NODE_ADDR | _OV_HAS_PING_INTERVAL/* | _OV_HAS_INIT_BAUD_RATE*/)
-#define _OPT_OPTS_START_BOOT					(_OV_HAS_STATION_NUMBER | _OV_HAS_NODE_ADDR/* | _OV_HAS_INIT_BAUD_RATE*/)
-#define _OPT_OPTS_RESET_BOOT					(_OV_HAS_STATION_NUMBER | _OV_HAS_NODE_ADDR/* | _OV_HAS_INIT_BAUD_RATE*/)
-#define _OPT_OPTS_RESCUE_BOOT					(_OV_HAS_STATION_NUMBER | _OV_HAS_NODE_ADDR/* | _OV_HAS_INIT_BAUD_RATE*/)
-#define _OPT_OPTS_MODBUS_START_BOOT				(_OV_HAS_MODBUS_CTRL_REG/* | _OV_HAS_MODBUS_BAUD_RATE | _OV_HAS_MODBUS_PARITY*/)
-
-/////////////////////////////////////////////////////////////////////////////
-
-#define _GET_OPTS(m)							((m) & _OV_OPT_MASK)
-#define _GET_CMD(m)								((m) & _OV_CMD_MASK)
-#define _HAS_CMD(m)								(!!_GET_CMD(m))
-#define _HAS_VALID_CMD(m)						(_HAS_CMD(m) && _IS_POWER_OF_2(_GET_CMD(m)))
-#define _IS_OFFLINE_CMD(m)						(_GET_CMD(m) == _OV_HAS_SHOW_FILE_IMG_INFO)
-#define _HAS_REQUIRED_OPTIONS(m, r)				((_GET_OPTS(m) & (r)) == (r))
-#define _GET_MISSING_OPTS(m, r)					((_GET_OPTS(m) ^ (r)) & (r))
-#define _GET_UNUSED_OPTS(m, r, o)				((_GET_OPTS(m) ^ (r)) & ~(o))
-
-/////////////////////////////////////////////////////////////////////////////
-
-#define _OPT_STRING_UPLOAD_IMG					"upload-img"
-#define _OPT_STRING_VALIDATE_IMG				"validate-img"
-#define _OPT_STRING_SHOW_FILE_IMG_INFO			"show-file-img-info"
-#define _OPT_STRING_SHOW_DEV_IMG_INFO			"show-dev-img-info"
-#define _OPT_STRING_SHOW_MAT_SER				"show-mat-ser"
-#define _OPT_STRING_SET_MAT_SER					"set-mat-ser"
-#define _OPT_STRING_BOOT_PING					"ping-target"
-#define _OPT_STRING_START_BOOT					"start-boot"
-#define _OPT_STRING_RESET_BOOT					"reset-boot"
-#define _OPT_STRING_RESCUE_BOOT					"revive-boot"
-#define _OPT_STRING_MODBUS_START_BOOT			"mb-start-boot"
-#define _OPT_STRING_ITF_NAME					"itf-name"
-//#define _OPT_STRING_INIT_BAUD_RATE				"init-baud-rate"
-#define _OPT_STRING_X_BAUD_RATE					"x-baud-rate"
-//#define _OPT_STRING_MODBUS_BAUD_RATE			"mb-baud-rate"
-//#define _OPT_STRING_MODBUS_PARITY				"mb-parity"
-#define _OPT_STRING_STATION_NUMBER				"stat-num"
-#define _OPT_STRING_NODE_ADDR					"node-addr"
-#define _OPT_STRING_MATERIAL					"material"
-#define _OPT_STRING_SERIAL						"serial"
-#define _OPT_STRING_SLAVE_ADDR					"stat-num or node-addr"
-#define _OPT_STRING_APP_BASE_ADDR				"app-addr"
-#define _OPT_STRING_BLOCK_SIZE					"block-size"
-#define _OPT_STRING_PING_INTERVAL				"ping-int"
-#define _OPT_STRING_PAGE_ERASE_TIME				"page-erase-time"
-#define _OPT_STRING_NO_SHOW_PROGRESS			"no-progress"
-#define _OPT_STRING_MODBUS_SLAVE_ID				"mb-slave-id"
-#define _OPT_STRING_MODBUS_CTRL_REG				"mb-bl-ctrl-reg"
-#define _OPT_STRING_VERBOSITY					"verbosity"
-#define _OPT_STRING_HELP						"help"
-#define _OPT_STRING_IMG_FILE					"<IMG FILE>"
-
-#define _MAX_CMD_LENGTH							18
-#define _MAX_OPT_LENGTH							15
-
-/////////////////////////////////////////////////////////////////////////////
-
-int g_nVerbosity = CMD_OPT_DEFAULT_VERBOSITY;
-
-/////////////////////////////////////////////////////////////////////////////
-
-static const uint8_t g_nodeTable[] =
-{
-	// 1 - 99
-	0x11,	0x12,	0x13,	0x14,	0x15,	0x16,	0x17,	0x18,	0x19,	0x1A,
-	0x21,	0x22,	0x23,	0x24,	0x25,	0x26,	0x27,	0x28,	0x29,	0x2A,
-	0x31,	0x32,	0x33,	0x34,	0x35,	0x36,	0x37,	0x38,	0x39,	0x3A,
-	0x41,	0x42,	0x43,	0x44,	0x45,	0x46,	0x47,	0x48,	0x49,	0x4A,
-	0x51,	0x52,	0x53,	0x54,	0x55,	0x56,	0x57,	0x58,	0x59,	0x5A,
-	0x61,	0x62,	0x63,	0x64,	0x65,	0x66,	0x67,	0x68,	0x69,	0x6A,
-	0x71,	0x72,	0x73,	0x74,	0x75,	0x76,	0x77,	0x78,	0x79,	0x7A,
-	0x81,	0x82,	0x83,	0x84,	0x85,	0x86,	0x87,	0x88,	0x89,	0x8A,
-	0x91,	0x92,	0x93,	0x94,	0x95,	0x96,	0x97,	0x98,	0x99,	0x9A,
-	0xA1,	0xA2,	0xA3,	0xA4,	0xA5,	0xA6,	0xA7,	0xA8,	0xA9
-};
-
-/////////////////////////////////////////////////////////////////////////////
-
-static const char* _GetOptName(uint32_t nOpt)
-{
-	switch(nOpt)
-	{
-	case _OV_HAS_UPLOAD_IMG:
-		return _OPT_STRING_UPLOAD_IMG;
-	case _OV_HAS_VALIDATE_IMG:
-		return _OPT_STRING_VALIDATE_IMG;
-	case _OV_HAS_SHOW_FILE_IMG_INFO:
-		return _OPT_STRING_SHOW_FILE_IMG_INFO;
-	case _OV_HAS_SHOW_DEV_IMG_INFO:
-		return _OPT_STRING_SHOW_DEV_IMG_INFO;
-	case _OV_HAS_SHOW_MAT_SER:
-		return _OPT_STRING_SHOW_MAT_SER;
-	case _OV_HAS_BOOT_PING:
-		return _OPT_STRING_BOOT_PING;
-	case _OV_HAS_START_BOOT:
-		return _OPT_STRING_START_BOOT;
-	case _OV_HAS_RESET_BOOT:
-		return _OPT_STRING_RESET_BOOT;
-	case _OV_HAS_RESCUE_BOOT:
-		return _OPT_STRING_RESCUE_BOOT;
-	case _OV_HAS_MODBUS_START_BOOT:
-		return _OPT_STRING_MODBUS_START_BOOT;
-	case _OV_HAS_PING_INTERVAL:
-		return _OPT_STRING_PING_INTERVAL;
-	case _OV_HAS_NO_SHOW_PROGRESS:
-		return _OPT_STRING_NO_SHOW_PROGRESS;
-	case _OV_HAS_ITF_NAME:
-		return _OPT_STRING_ITF_NAME;
-	case _OV_HAS_BLOCK_SIZE:
-		return _OPT_STRING_BLOCK_SIZE;
-	case _OV_HAS_APP_ADDR:
-		return _OPT_STRING_APP_BASE_ADDR;
-//	case _OV_HAS_INIT_BAUD_RATE:
-//		return _OPT_STRING_INIT_BAUD_RATE;
-	case _OV_HAS_X_BAUD_RATE:
-		return _OPT_STRING_X_BAUD_RATE;
-//	case _OV_HAS_MODBUS_BAUD_RATE:
-//		return _OPT_STRING_MODBUS_BAUD_RATE;
-//	case _OV_HAS_MODBUS_PARITY:
-//		return _OPT_STRING_MODBUS_PARITY;
-	case _OV_HAS_PAGE_ERASE_TIME:
-		return _OPT_STRING_PAGE_ERASE_TIME;
-	case _OV_HAS_STATION_NUMBER:
-		return _OPT_STRING_STATION_NUMBER;
-	case _OV_HAS_NODE_ADDR:
-		return _OPT_STRING_NODE_ADDR;
-	case _OV_HAS_SLAVE_ADDR:
-		return _OPT_STRING_SLAVE_ADDR;
-	case _OV_HAS_MATERIAL:
-		return _OPT_STRING_MATERIAL;
-	case _OV_HAS_SERIAL:
-		return _OPT_STRING_SERIAL;
-	case _OV_HAS_SET_MAT_SER:
-		return _OPT_STRING_SET_MAT_SER;
-	case _OV_HAS_IMG_FILE:
-		return _OPT_STRING_IMG_FILE;
-	case _OV_HAS_MODBUS_SLAVE_ID:
-		return _OPT_STRING_MODBUS_SLAVE_ID;
-	case _OV_HAS_MODBUS_CTRL_REG:
-		return _OPT_STRING_MODBUS_CTRL_REG;
-	default:
-		return NULL;
-	}
-}
-
-static const char* _GetOptValue(uint32_t nOpt, LPCMD_LINE_ARGS pcla)
-{
-	static char szValue[32];
-
-	switch(nOpt)
-	{
-	case _OV_HAS_UPLOAD_IMG:
-		return _OPT_STRING_UPLOAD_IMG;
-	case _OV_HAS_VALIDATE_IMG:
-		return _OPT_STRING_VALIDATE_IMG;
-	case _OV_HAS_SHOW_FILE_IMG_INFO:
-		return _OPT_STRING_SHOW_FILE_IMG_INFO;
-	case _OV_HAS_SHOW_DEV_IMG_INFO:
-		return _OPT_STRING_SHOW_DEV_IMG_INFO;
-	case _OV_HAS_SHOW_MAT_SER:
-		return _OPT_STRING_SHOW_MAT_SER;
-	case _OV_HAS_BOOT_PING:
-		return _OPT_STRING_BOOT_PING;
-	case _OV_HAS_START_BOOT:
-		return _OPT_STRING_START_BOOT;
-	case _OV_HAS_RESET_BOOT:
-		return _OPT_STRING_RESET_BOOT;
-	case _OV_HAS_RESCUE_BOOT:
-		return _OPT_STRING_RESCUE_BOOT;
-	case _OV_HAS_MODBUS_START_BOOT:
-		return _OPT_STRING_MODBUS_START_BOOT;
-
-	case _OV_HAS_PING_INTERVAL:
-		snprintf(szValue, sizeof(szValue) - 1, "%d", pcla->nPingIntervalSec);
-		break;
-	case _OV_HAS_ITF_NAME:
-		return pcla->pszDevName ? pcla->pszDevName : "empty";
-	case _OV_HAS_BLOCK_SIZE:
-		snprintf(szValue, sizeof(szValue) - 1, "%u", pcla->nBlockSize);
-		break;
-	case _OV_HAS_APP_ADDR:
-		snprintf(szValue, sizeof(szValue) - 1, "0x%X", pcla->nStartAddr);
-		break;
-//	case _OV_HAS_INIT_BAUD_RATE:
-//		snprintf(szValue, sizeof(szValue) - 1, "%u", pcla->nInitBaudrate);
-//		break;
-	case _OV_HAS_X_BAUD_RATE:
-		snprintf(szValue, sizeof(szValue) - 1, "%u", pcla->nElevBaudrate);
-		break;
-//	case _OV_HAS_MODBUS_BAUD_RATE:
-//		snprintf(szValue, sizeof(szValue) - 1, "%u", pcla->nModbBaudrate);
-//		break;
-//	case _OV_HAS_MODBUS_PARITY:
-//		snprintf(szValue, sizeof(szValue) - 1, "%c", pcla->modbParity);
-//		break;
-	case _OV_HAS_PAGE_ERASE_TIME:
-		snprintf(szValue, sizeof(szValue) - 1, "%u", pcla->nPageErsaeTime);
-		break;
-	case _OV_HAS_STATION_NUMBER:
-		snprintf(szValue, sizeof(szValue) - 1, "%hhu", pcla->nStationNr);
-		break;
-	case _OV_HAS_NODE_ADDR:
-		snprintf(szValue, sizeof(szValue) - 1, "0x%02hhX", pcla->nNodeAddr);
-		break;
-	case _OV_HAS_SLAVE_ADDR:
-		return _OPT_STRING_SLAVE_ADDR;
-	case _OV_HAS_MATERIAL:
-		return pcla->pszMaterial ? pcla->pszMaterial : "empty";
-	case _OV_HAS_SERIAL:
-		return pcla->pszSerial ? pcla->pszSerial : "empty";
-	case _OV_HAS_IMG_FILE:
-		return pcla->pszImgFile ? pcla->pszImgFile : "empty";
-	case _OV_HAS_NO_SHOW_PROGRESS:
-		return pcla->bNoProgressBlock ? "true" : "false";
-	case _OV_HAS_MODBUS_SLAVE_ID:
-		snprintf(szValue, sizeof(szValue) - 1, "%hhu", pcla->nModbusSlvID);
-		break;
-	case _OV_HAS_MODBUS_CTRL_REG:
-		snprintf(szValue, sizeof(szValue) - 1, "%hu", pcla->nModbusCtrlReg);
-		break;
-	default:
-		return "empty";
-	}
-
-	szValue[31] = '\0';
-	return szValue;
-}
-
-/////////////////////////////////////////////////////////////////////////////
-
-const char* GfaTfuCmdOpt2String(uint32_t nOpts)
-{
-	static char szOptStr[600];
-
-	if(nOpts)
-	{
-		int nCount = 0;
-		uint32_t nMask = 1;
-		memset(szOptStr, '\0', sizeof(szOptStr));
-
-		while(nMask)
-		{
-			if(nOpts & nMask)
-			{
-				const char *pszOptName = _GetOptName(nMask);
-				if(pszOptName)
-				{
-					if(nCount++)
-						strcat(szOptStr, ", ");
-					strcat(szOptStr, pszOptName);
-				}
-			}
-
-			nMask <<= 1;
-		}
-
-		return szOptStr;
-	}
-
-	return "";
-}
-
-/////////////////////////////////////////////////////////////////////////////
-
-static const struct option g_lo[] =
-{
-	{
-		_OPT_STRING_UPLOAD_IMG,
-		no_argument,
-		NULL,
-		ov_upload_img
-	},
-	{
-		_OPT_STRING_VALIDATE_IMG,
-		no_argument,
-		NULL,
-		ov_validate_img
-	},
-	{
-		_OPT_STRING_SHOW_FILE_IMG_INFO,
-		no_argument,
-		NULL,
-		ov_show_file_img_info
-	},
-	{
-		_OPT_STRING_SHOW_DEV_IMG_INFO,
-		no_argument,
-		NULL,
-		ov_show_dev_img_info
-	},
-	{
-		_OPT_STRING_SHOW_MAT_SER,
-		no_argument,
-		NULL,
-		ov_show_mat_ser
-	},
-	{
-		_OPT_STRING_SET_MAT_SER,
-		no_argument,
-		NULL,
-		ov_set_mat_ser
-	},
-	{
-		_OPT_STRING_MATERIAL,
-		required_argument,
-		NULL,
-		ov_material
-	},
-	{
-		_OPT_STRING_SERIAL,
-		required_argument,
-		NULL,
-		ov_serial
-	},
-	{
-		_OPT_STRING_NO_SHOW_PROGRESS,
-		no_argument,
-		NULL,
-		ov_no_progress
-	},
-	{
-		_OPT_STRING_BOOT_PING,
-		no_argument,
-		NULL,
-		ov_boot_ping
-	},
-	{
-		_OPT_STRING_START_BOOT,
-		no_argument,
-		NULL,
-		ov_start_boot
-	},
-	{
-		_OPT_STRING_RESET_BOOT,
-		no_argument,
-		NULL,
-		ov_reset_boot
-	},
-	{
-		_OPT_STRING_RESCUE_BOOT,
-		no_argument,
-		NULL,
-		ov_rescue_boot
-	},
-	{
-		_OPT_STRING_MODBUS_START_BOOT,
-		no_argument,
-		NULL,
-		ov_mb_start_boot
-	},
-	{
-		_OPT_STRING_PING_INTERVAL,
-		required_argument,
-		NULL,
-		ov_ping_interval
-	},
-	{
-		_OPT_STRING_ITF_NAME,
-		required_argument,
-		NULL,
-		ov_itf_name
-	},
-/*	{
-		_OPT_STRING_INIT_BAUD_RATE,
-		required_argument,
-		NULL,
-		ov_init_baud_rate
-	},*/
-	{
-		_OPT_STRING_X_BAUD_RATE,
-		required_argument,
-		NULL,
-		ov_x_baud_rate
-	},
-/*	{
-		_OPT_STRING_MODBUS_BAUD_RATE,
-		required_argument,
-		NULL,
-		ov_mb_baud_rate
-	},*/
-/*	{
-		_OPT_STRING_MODBUS_PARITY,
-		required_argument,
-		NULL,
-		ov_mb_parity
-	},*/
-	{
-		_OPT_STRING_STATION_NUMBER,
-		required_argument,
-		NULL,
-		ov_station_number
-	},
-	{
-		_OPT_STRING_NODE_ADDR,
-		required_argument,
-		NULL,
-		ov_node_addr
-	},
-	{
-		_OPT_STRING_PAGE_ERASE_TIME,
-		required_argument,
-		NULL,
-		ov_page_erase_time
-	},
-	{
-		_OPT_STRING_APP_BASE_ADDR,
-		required_argument,
-		NULL,
-		ov_app_addr
-	},
-	{
-		_OPT_STRING_BLOCK_SIZE,
-		required_argument,
-		NULL,
-		ov_block_size
-	},
-	{
-		_OPT_STRING_MODBUS_SLAVE_ID,
-		required_argument,
-		NULL,
-		ov_mb_slave_id
-	},
-	{
-		_OPT_STRING_MODBUS_CTRL_REG,
-		required_argument,
-		NULL,
-		ov_mb_ctrl_reg
-	},
-	{
-		_OPT_STRING_VERBOSITY,
-		required_argument,
-		NULL,
-		ov_verbosity
-	},
-	{
-		_OPT_STRING_HELP,
-		no_argument,
-		NULL,
-		ov_help
-	},
-	{
-		NULL,
-		0,
-		NULL,
-		0
-	}
-};
-
-/////////////////////////////////////////////////////////////////////////////
-
-static int64_t _ArgStr2Num(const char *pszNum, int64_t nDefault)
-{
-	if(pszNum && *pszNum)
-	{
-		char *pszEnd = NULL;
-		int64_t n = strtoll(pszNum, &pszEnd, 0);
-		if((((n == LLONG_MIN) || (n == LLONG_MAX)) && (errno == ERANGE)) || *pszEnd)
-			return nDefault;
-		return n;
-	}
-
-	return nDefault;
-}
-
-/////////////////////////////////////////////////////////////////////////////
-/////////////////////////////////////////////////////////////////////////////
-/////////////////////////////////////////////////////////////////////////////
-
-void GfaTfuCmdOptInitOpts(LPCMD_LINE_ARGS pcla)
-{
-	memset(pcla, 0, sizeof(CMD_LINE_ARGS));
-	pcla->nBootBaudrate		= CMD_OPT_DEFAULT_BAUDRATE;
-	pcla->nInitBaudrate		= CMD_OPT_DEFAULT_BAUDRATE;
-	pcla->nModbBaudrate		= CMD_OPT_DEFAULT_BAUDRATE;
-	pcla->modbParity		= CMD_OPT_DEFAULT_MODBUS_PARITY;
-	pcla->nBlockSize		= GFA_BOOTLOADER_DEF_SEND_DATA_BLOCK_SIZE;
-	pcla->nStartAddr		= CMD_OPT_DEFAULT_APP_BASE_ADDRESS;
-	pcla->nPageErsaeTime	= CMD_OPT_DEFAULT_PAGE_ERASE_TIME;
-	pcla->nVerbosity		= CMD_OPT_DEFAULT_VERBOSITY;
-	pcla->nModbusCtrlReg	= CMD_OPT_DEFAULT_MODBUS_START_REGISTER;
-}
-
-/////////////////////////////////////////////////////////////////////////////
-
-int GfaTfuCmdOptParse(int argc, char* argv[], LPCMD_LINE_ARGS pcla)
-{
-	int opt, nCount = 0;
-	pcla->nOptFlags = 0;
-
-	while((opt = getopt_long(argc, argv, "-a:d:e:i:n:p:s:v:x:?", g_lo, NULL)) != -1)
-	{
-		const char *arg = optarg;
-
-		switch(opt)
-		{
-		case ov_img_file:
-			if(arg)
-			{
-				const char *pszFileBase = strrchr(arg, '/');
-				pcla->pszImgFile = arg;
-				if(pszFileBase)
-					pcla->pszImgFileBase = ++pszFileBase;
-				pcla->nOptFlags |= _OV_HAS_IMG_FILE;
-			}
-			break;
-		case ov_itf_name:
-			if(arg)
-			{
-				pcla->pszDevName = arg;
-				pcla->nOptFlags |= _OV_HAS_ITF_NAME;
-			}
-			break;
-		case ov_show_dev_img_info:
-			pcla->bShowDevImgInfo = true;
-			pcla->nOptFlags |= _OV_HAS_SHOW_DEV_IMG_INFO;
-			break;
-		case ov_show_file_img_info:
-			pcla->bShowFileImgInfo = true;
-			pcla->nOptFlags |= _OV_HAS_SHOW_FILE_IMG_INFO;
-			break;
-		case ov_show_mat_ser:
-			pcla->bShowMatSer = true;
-			pcla->nOptFlags |= _OV_HAS_SHOW_MAT_SER;
-			break;
-		case ov_set_mat_ser:
-			pcla->bSetMatSer = true;
-			pcla->nOptFlags |= _OV_HAS_SET_MAT_SER;
-			break;
-		case ov_validate_img:
-			pcla->bValidateImg = true;
-			pcla->nOptFlags |= _OV_HAS_VALIDATE_IMG;
-			break;
-		case ov_upload_img:
-			pcla->bUploadImg = true;
-			pcla->nOptFlags |= _OV_HAS_UPLOAD_IMG;
-			break;
-		case ov_boot_ping:
-			pcla->bPing = true;
-			pcla->nOptFlags |= _OV_HAS_BOOT_PING;
-			break;
-		case ov_start_boot:
-			pcla->bStartBoot = true;
-			pcla->nOptFlags |= _OV_HAS_START_BOOT;
-			break;
-		case ov_reset_boot:
-			pcla->bResetBoot = true;
-			pcla->nOptFlags |= _OV_HAS_RESET_BOOT;
-			break;
-		case ov_rescue_boot:
-			pcla->bReviveBoot = true;
-			pcla->nOptFlags |= _OV_HAS_RESCUE_BOOT;
-			break;
-		case ov_mb_start_boot:
-			pcla->bModbusStartBoot = true;
-			pcla->nOptFlags |= _OV_HAS_MODBUS_START_BOOT;
-			break;
-		case ov_ping_interval:
-			pcla->nPingIntervalSec = (int32_t)_ArgStr2Num(arg, 0);
-			if(pcla->nPingIntervalSec > 0)
-				pcla->nOptFlags |= _OV_HAS_PING_INTERVAL;
-			break;
-		case ov_material:
-			if(arg && *arg)
-			{
-				pcla->pszMaterial = arg;
-				pcla->nOptFlags |= _OV_HAS_MATERIAL;
-			}
-			break;
-		case ov_serial:
-			if(arg && *arg)
-			{
-				pcla->pszSerial = arg;
-				pcla->nOptFlags |= _OV_HAS_SERIAL;
-			}
-			break;
-		case ov_no_progress:
-			pcla->bNoProgressBlock = true;
-			pcla->nOptFlags |= _OV_HAS_NO_SHOW_PROGRESS;
-			break;
-		case ov_block_size:
-			pcla->nBlockSize = (uint32_t)_ArgStr2Num(arg, GFA_BOOTLOADER_DEF_SEND_DATA_BLOCK_SIZE);
-			pcla->nOptFlags |= _OV_HAS_BLOCK_SIZE;
-			break;
-		case ov_app_addr:
-			pcla->nStartAddr = (uint32_t)_ArgStr2Num(arg, CMD_OPT_DEFAULT_APP_BASE_ADDRESS);
-			pcla->nOptFlags |= _OV_HAS_APP_ADDR;
-			break;
-/*		case ov_init_baud_rate:
-			pcla->nInitBaudrate = (uint32_t)_ArgStr2Num(arg, CMD_OPT_DEFAULT_BAUDRATE);
-			pcla->nOptFlags |= _OV_HAS_INIT_BAUD_RATE;
-			break;*/
-		case ov_x_baud_rate:
-			pcla->nElevBaudrate = (uint32_t)_ArgStr2Num(arg, 0);
-			pcla->nOptFlags |= _OV_HAS_X_BAUD_RATE;
-			break;
-/*		case ov_mb_baud_rate:
-			pcla->nModbBaudrate = (uint32_t)_ArgStr2Num(arg, CMD_OPT_DEFAULT_BAUDRATE);
-			pcla->nOptFlags |= _OV_HAS_MODBUS_BAUD_RATE;
-			break;*/
-/*		case ov_mb_parity:
-			pcla->modbParity = toupper(*arg);
-			pcla->nOptFlags |= _OV_HAS_MODBUS_PARITY;
-			break;*/
-		case ov_page_erase_time:
-			pcla->nPageErsaeTime = (uint32_t)_ArgStr2Num(arg, CMD_OPT_DEFAULT_PAGE_ERASE_TIME);
-			pcla->nOptFlags |= _OV_HAS_PAGE_ERASE_TIME;
-			break;
-		case ov_station_number:
-			pcla->nStationNr = (uint8_t)_ArgStr2Num(arg, 0);
-			pcla->nOptFlags |= _OV_HAS_STATION_NUMBER;
-			pcla->nOptFlags |= _OV_HAS_SLAVE_ADDR;
-			break;
-		case ov_node_addr:
-			pcla->nNodeAddr = (uint8_t)_ArgStr2Num(arg, 0);
-			pcla->nOptFlags |= _OV_HAS_NODE_ADDR;
-			pcla->nOptFlags |= _OV_HAS_SLAVE_ADDR;
-			break;
-		case ov_mb_slave_id:
-			pcla->nModbusSlvID = (uint8_t)_ArgStr2Num(arg, 0xFF);
-			pcla->nOptFlags |= _OV_HAS_MODBUS_SLAVE_ID;
-			break;
-		case ov_mb_ctrl_reg:
-			pcla->nModbusCtrlReg = (uint16_t)_ArgStr2Num(arg, CMD_OPT_DEFAULT_MODBUS_START_REGISTER);
-			pcla->nOptFlags |= _OV_HAS_MODBUS_CTRL_REG;
-			break;
-		case ov_verbosity:
-			pcla->nVerbosity = (int)_ArgStr2Num(arg, CMD_OPT_DEFAULT_VERBOSITY);
-			break;
-		case ov_help:
-			pcla->bShowHelp = true;
-			break;
-		}
-
-		++nCount;
-	}
-
-	if(nCount == 0)
-		pcla->bShowHelp = true;
-
-	return nCount;
-}
-
-/////////////////////////////////////////////////////////////////////////////
-
-int GfaTfuCmdOptProcess(LPCMD_LINE_ARGS pcla)
-{
-	/////////////////////////////////////////////////////////////////////////
-	// If help is requested, do nothing else
-
-	if(pcla->bShowHelp)
-		return 1;
-
-	/////////////////////////////////////////////////////////////////////////
-	// adjust verbosity
-
-	if(pcla->nVerbosity < CMD_OPT_MIN_VERBOSITY)
-		pcla->nVerbosity = CMD_OPT_MIN_VERBOSITY;
-	else if(pcla->nVerbosity > CMD_OPT_MAX_VERBOSITY)
-		pcla->nVerbosity = CMD_OPT_MAX_VERBOSITY;
-	g_nVerbosity = pcla->nVerbosity;
-
-	/////////////////////////////////////////////////////////////////////////
-	// validate commands
-
-	pcla->nCmdFlags = _GET_CMD(pcla->nOptFlags);
-	if(!_HAS_CMD(pcla->nOptFlags))
-	{
-		if(!(pcla->nOptFlags & (_OV_HAS_MATERIAL | _OV_HAS_SERIAL)))
-			return GFA_FU_ERROR_NOTHING_TO_DO;
-	}
-	else if(!_HAS_VALID_CMD(pcla->nOptFlags))
-		return GFA_FU_ERROR_MULTIPLE_COMMANDS;
-
-	/////////////////////////////////////////////////////////////////////////
-	// validate connection options if required
-
-	if(!_IS_OFFLINE_CMD(pcla->nOptFlags))
-	{
-		if(	(pcla->modbParity != 'E') && 
-			(pcla->modbParity != 'O') &&
-			(pcla->modbParity != 'N'))
-			return GFA_FU_ERROR_INVALID_PARITY;
-		if(!pcla->nElevBaudrate)
-			pcla->nElevBaudrate = pcla->nInitBaudrate;
-		if(!GfaSerialIsValidBaudrate(pcla->nInitBaudrate))
-			return GFA_FU_ERROR_INVALID_BAUDRATE;
-		else if(!GfaSerialIsValidBaudrate(pcla->nElevBaudrate))
-			return GFA_FU_ERROR_INVALID_BAUDRATE;
-		else if((pcla->nOptFlags & (_OV_HAS_STATION_NUMBER | _OV_HAS_NODE_ADDR)) == (_OV_HAS_STATION_NUMBER | _OV_HAS_NODE_ADDR))
-			return GFA_FU_ERROR_NODE_STATION_MUTEX;
-		else if(pcla->nOptFlags & _OV_HAS_STATION_NUMBER)
-		{
-			if(!_IS_VALID_STATION_NUMBER(pcla->nStationNr))
-				return GFA_FU_ERROR_INVALID_STATION_NUM;
-			pcla->nNodeAddr = g_nodeTable[pcla->nStationNr - 1];
-		}
-		else if((pcla->nOptFlags & _OV_HAS_NODE_ADDR) && (NODE_IS_MULTICAST(pcla->nNodeAddr) || (pcla->nNodeAddr < MINET_MIN_NODE_ADDRESS)))
-			return GFA_FU_ERROR_INVALID_NODE_ADDR;
-	}
-
-	/////////////////////////////////////////////////////////////////////////
-	// validate command options
-
-	switch(pcla->nCmdFlags)
-	{
-	case _OV_HAS_UPLOAD_IMG:
-		if(!_HAS_REQUIRED_OPTIONS(pcla->nOptFlags, _REQ_OPTS_UPLOAD_IMG))
-		{
-			pcla->nMissingOptFlags = _GET_MISSING_OPTS(pcla->nOptFlags, _REQ_OPTS_UPLOAD_IMG);
-			return GFA_FU_ERROR_MISSING_COMMAND_OPT;
-		}
-		else if((pcla->nOptFlags & _OV_HAS_BLOCK_SIZE) && !_IS_VALID_BLOCK_SIZE(pcla->nBlockSize))
-			return GFA_FU_ERROR_INVALID_BLOCK_SIZE;
-		else if(pcla->nStartAddr == (uint32_t)-1)
-			return GFA_FU_ERROR_INVALID_APP_START_ADDR;
-		pcla->nUnusedOptFlags = _GET_UNUSED_OPTS(pcla->nOptFlags, _REQ_OPTS_UPLOAD_IMG, _OPT_OPTS_UPLOAD_IMG);
-		pcla->bNeedImgFile = true;
-		break;
-	case _OV_HAS_SHOW_FILE_IMG_INFO:
-		if(!_HAS_REQUIRED_OPTIONS(pcla->nOptFlags, _REQ_OPTS_SHOW_IMG_INFO_OFFLINE))
-		{
-			pcla->nMissingOptFlags = _GET_MISSING_OPTS(pcla->nOptFlags, _REQ_OPTS_SHOW_IMG_INFO_OFFLINE);
-			return GFA_FU_ERROR_MISSING_COMMAND_OPT;
-		}
-		pcla->nUnusedOptFlags = _GET_UNUSED_OPTS(pcla->nOptFlags, _REQ_OPTS_SHOW_IMG_INFO_OFFLINE, _OPT_OPTS_SHOW_IMG_INFO_OFFLINE);
-		pcla->bNeedImgFile = true;
-		break;
-	case _OV_HAS_SHOW_DEV_IMG_INFO:
-		if(!_HAS_REQUIRED_OPTIONS(pcla->nOptFlags, _REQ_OPTS_SHOW_IMG_INFO_ONLINE))
-		{
-			pcla->nMissingOptFlags = _GET_MISSING_OPTS(pcla->nOptFlags, _REQ_OPTS_SHOW_IMG_INFO_ONLINE);
-			return GFA_FU_ERROR_MISSING_COMMAND_OPT;
-		}
-		pcla->nUnusedOptFlags = _GET_UNUSED_OPTS(pcla->nOptFlags, _REQ_OPTS_SHOW_IMG_INFO_ONLINE, _OPT_OPTS_SHOW_IMG_INFO_ONLINE);
-		break;
-	case _OV_HAS_VALIDATE_IMG:
-		if(!_HAS_REQUIRED_OPTIONS(pcla->nOptFlags, _REQ_OPTS_VALIDATE_IMG))
-		{
-			pcla->nMissingOptFlags = _GET_MISSING_OPTS(pcla->nOptFlags, _REQ_OPTS_VALIDATE_IMG);
-			return GFA_FU_ERROR_MISSING_COMMAND_OPT;
-		}
-		pcla->nUnusedOptFlags = _GET_UNUSED_OPTS(pcla->nOptFlags, _REQ_OPTS_VALIDATE_IMG, _OPT_OPTS_VALIDATE_IMG);
-		pcla->bNeedImgFile = true;
-		break;
-	case _OV_HAS_SHOW_MAT_SER:
-		if(!_HAS_REQUIRED_OPTIONS(pcla->nOptFlags, _REQ_OPTS_SHOW_MAT_SER))
-		{
-			pcla->nMissingOptFlags = _GET_MISSING_OPTS(pcla->nOptFlags, _REQ_OPTS_SHOW_MAT_SER);
-			return GFA_FU_ERROR_MISSING_COMMAND_OPT;
-		}
-		pcla->nUnusedOptFlags = _GET_UNUSED_OPTS(pcla->nOptFlags, _REQ_OPTS_SHOW_MAT_SER, _OPT_OPTS_SHOW_MAT_SER);
-		break;
-	case _OV_HAS_SET_MAT_SER:
-		if(!_HAS_REQUIRED_OPTIONS(pcla->nOptFlags, _REQ_OPTS_SET_MAT_SER))
-		{
-			pcla->nMissingOptFlags = _GET_MISSING_OPTS(pcla->nOptFlags, _REQ_OPTS_SET_MAT_SER);
-			return GFA_FU_ERROR_MISSING_COMMAND_OPT;
-		}
-		if(	(strlen(pcla->pszMaterial) >= GFA_APP_MAX_IMG_MATERIAL_NUM_LENGTH) ||
-			(strlen(pcla->pszSerial) >= GFA_APP_MAX_IMG_SERIAL_NUM_LENGTH))
-		{
-			return GFA_FU_ERROR_MAT_OR_SER_TOO_LONG;
-		}
-		pcla->nUnusedOptFlags = _GET_UNUSED_OPTS(pcla->nOptFlags, _REQ_OPTS_SET_MAT_SER, _OPT_OPTS_SET_MAT_SER);
-		break;
-	case _OV_HAS_BOOT_PING:
-		if(!_HAS_REQUIRED_OPTIONS(pcla->nOptFlags, _REQ_OPTS_BOOT_PING))
-		{
-			pcla->nMissingOptFlags = _GET_MISSING_OPTS(pcla->nOptFlags, _REQ_OPTS_BOOT_PING);
-			return GFA_FU_ERROR_MISSING_COMMAND_OPT;
-		}
-		else if(pcla->nPingIntervalSec < 0)
-			return GFA_FU_ERROR_INVALID_COMMAND_OPT;
-		pcla->nUnusedOptFlags = _GET_UNUSED_OPTS(pcla->nOptFlags, _REQ_OPTS_BOOT_PING, _OPT_OPTS_BOOT_PING);
-		break;
-	case _OV_HAS_START_BOOT:
-		if(!_HAS_REQUIRED_OPTIONS(pcla->nOptFlags, _REQ_OPTS_START_BOOT))
-		{
-			pcla->nMissingOptFlags = _GET_MISSING_OPTS(pcla->nOptFlags, _REQ_OPTS_START_BOOT);
-			return GFA_FU_ERROR_MISSING_COMMAND_OPT;
-		}
-		pcla->nUnusedOptFlags = _GET_UNUSED_OPTS(pcla->nOptFlags, _REQ_OPTS_START_BOOT, _OPT_OPTS_START_BOOT);
-		break;
-	case _OV_HAS_RESET_BOOT:
-		if(!_HAS_REQUIRED_OPTIONS(pcla->nOptFlags, _REQ_OPTS_RESET_BOOT))
-		{
-			pcla->nMissingOptFlags = _GET_MISSING_OPTS(pcla->nOptFlags, _REQ_OPTS_RESET_BOOT);
-			return GFA_FU_ERROR_MISSING_COMMAND_OPT;
-		}
-		pcla->nUnusedOptFlags = _GET_UNUSED_OPTS(pcla->nOptFlags, _REQ_OPTS_RESET_BOOT, _OPT_OPTS_RESET_BOOT);
-		break;
-	case _OV_HAS_RESCUE_BOOT:
-		if(!_HAS_REQUIRED_OPTIONS(pcla->nOptFlags, _REQ_OPTS_RESCUE_BOOT))
-		{
-			pcla->nMissingOptFlags = _GET_MISSING_OPTS(pcla->nOptFlags, _REQ_OPTS_RESCUE_BOOT);
-			return GFA_FU_ERROR_MISSING_COMMAND_OPT;
-		}
-		pcla->nUnusedOptFlags = _GET_UNUSED_OPTS(pcla->nOptFlags, _REQ_OPTS_RESCUE_BOOT, _OPT_OPTS_RESCUE_BOOT);
-		break;
-	case _OV_HAS_MODBUS_START_BOOT:
-		if(!_HAS_REQUIRED_OPTIONS(pcla->nOptFlags, _REQ_OPTS_MODBUS_START_BOOT))
-		{
-			pcla->nMissingOptFlags = _GET_MISSING_OPTS(pcla->nOptFlags, _REQ_OPTS_MODBUS_START_BOOT);
-			return GFA_FU_ERROR_MISSING_COMMAND_OPT;
-		}
-		pcla->nUnusedOptFlags = _GET_UNUSED_OPTS(pcla->nOptFlags, _REQ_OPTS_MODBUS_START_BOOT, _OPT_OPTS_MODBUS_START_BOOT) & ~_OV_HAS_SLAVE_ADDR;
-		break;
-	default:
-		return 1;
-	}
-
-	return 0;
-}
-
-void GfaTfuCmdOptDumpOptions(LPCMD_LINE_ARGS pcla)
-{
-	if(pcla)
-	{
-		uint32_t nMask, nOpts = 0;
-		TRACE3("****************************\n");
-		TRACE3("COMMAND: %s\n", _GetOptName(pcla->nCmdFlags));
-		TRACE3("OPTIONS:\n");
-
-		switch(pcla->nCmdFlags)
-		{
-		case _OV_HAS_UPLOAD_IMG:
-			nOpts = pcla->nOptFlags & (_REQ_OPTS_UPLOAD_IMG | _OPT_OPTS_UPLOAD_IMG) & ~_OV_HAS_SLAVE_ADDR;
-			break;
-		case _OV_HAS_SHOW_FILE_IMG_INFO:
-			nOpts = pcla->nOptFlags & (_REQ_OPTS_SHOW_IMG_INFO_OFFLINE | _OPT_OPTS_SHOW_IMG_INFO_OFFLINE) & ~_OV_HAS_SLAVE_ADDR;
-			break;
-		case _OV_HAS_SHOW_DEV_IMG_INFO:
-			nOpts = pcla->nOptFlags & (_REQ_OPTS_SHOW_IMG_INFO_ONLINE | _OPT_OPTS_SHOW_IMG_INFO_ONLINE) & ~_OV_HAS_SLAVE_ADDR;
-			break;
-		case _OV_HAS_VALIDATE_IMG:
-			nOpts = pcla->nOptFlags & (_REQ_OPTS_VALIDATE_IMG | _OPT_OPTS_VALIDATE_IMG) & ~_OV_HAS_SLAVE_ADDR;
-			break;
-		case _OV_HAS_SHOW_MAT_SER:
-			nOpts = pcla->nOptFlags & (_REQ_OPTS_SHOW_MAT_SER | _OPT_OPTS_SHOW_MAT_SER) & ~_OV_HAS_SLAVE_ADDR;
-			break;
-		case _OV_HAS_SET_MAT_SER:
-			nOpts = pcla->nOptFlags & (_REQ_OPTS_SET_MAT_SER | _OPT_OPTS_SET_MAT_SER) & ~_OV_HAS_SLAVE_ADDR;
-			break;
-		case _OV_HAS_BOOT_PING:
-			nOpts = pcla->nOptFlags & (_REQ_OPTS_BOOT_PING | _OPT_OPTS_BOOT_PING) & ~_OV_HAS_SLAVE_ADDR;
-			break;
-		case _OV_HAS_START_BOOT:
-			nOpts = pcla->nOptFlags & (_REQ_OPTS_START_BOOT | _OPT_OPTS_START_BOOT) & ~_OV_HAS_SLAVE_ADDR;
-			break;
-		case _OV_HAS_RESET_BOOT:
-			nOpts = pcla->nOptFlags & (_REQ_OPTS_RESET_BOOT | _OPT_OPTS_RESET_BOOT) & ~_OV_HAS_SLAVE_ADDR;
-			break;
-		case _OV_HAS_RESCUE_BOOT:
-			nOpts = pcla->nOptFlags & (_REQ_OPTS_RESCUE_BOOT | _OPT_OPTS_RESCUE_BOOT) & ~_OV_HAS_SLAVE_ADDR;
-			break;
-		case _OV_HAS_MODBUS_START_BOOT:
-			nOpts = pcla->nOptFlags & (_REQ_OPTS_MODBUS_START_BOOT | _OPT_OPTS_MODBUS_START_BOOT) & ~_OV_HAS_SLAVE_ADDR;
-			break;
-		default:
-			return;
-		}
-
-		for(nMask = 1; nMask; nMask <<= 1)
-		{
-			if(nOpts & nMask)
-			{
-				TRACE3("  %-*s: %s\n", _MAX_OPT_LENGTH, _GetOptName(nMask), _GetOptValue(nMask, pcla));
-			}
-		}
-		if(pcla->nUnusedOptFlags)
-			TRACE3("  %-*s: %s\n", _MAX_OPT_LENGTH, "IGNORED", GfaTfuCmdOpt2String(pcla->nUnusedOptFlags));
-		TRACE3("****************************\n\n");
-	}
-}
-
-/////////////////////////////////////////////////////////////////////////////
-// Examples:
-//
-// ./gfativaflashutil --upload-img --itf-name="/dev/ttyO4" --init-baud-rate 9600 -elev-baud-rate 115200 -s1 "/opt/GfA/gfativaflashutil/OLS-1V1_0009_crc_9600Baud.bin" -v3
-// ./gfativaflashutil --validate-img --itf-name="/dev/ttyO4" --app-addr=0x2000 --node-addr=0x11 "/opt/GfA/gfativaflashutil/OLS-1V1_0009_crc.bin" -v3
-// ./gfativaflashutil --show-mat-ser --itf-name="/dev/ttyO4" --stat-num=1 -v3
-// ./gfativaflashutil --set-mat-ser --itf-name="/dev/ttyO4" --elev-baud-rate=115200 --node-addr=0x11 -v3 --material="G.Z.40015 P01" --serial="18-080015 1409"
-// ./gfativaflashutil --show-file-img-info "/opt/GfA/gfativaflashutil/OLS-1V1_0009_crc.bin" -a0x2000 -v3
-// ./gfativaflashutil --show-dev-img-info --itf-name="/dev/ttyO4" --elev-baud-rate=115200 --node-addr=0x11 -v3
-// ./gfativaflashutil --ping-target --ping-int=1 --itf-name="/dev/ttyO4" --node-addr=0x11 -v3
-// ./gfativaflashutil --start-boot --itf-name="/dev/ttyO4" --node-addr=0x11 -v3
-// ./gfativaflashutil --reset-boot --itf-name="/dev/ttyO4" --node-addr=0x11 -v3
-// ./gfativaflashutil --mb-start-boot --itf-name="/dev/ttyO4" --mb-slave-id 100 -v3
-//
-//
-
-void GfaTfuCmdOptDisplayHelp(void)
-{
-	printf("gfativaflashutil v%d.%d GfA GmbH (%s %s)\n\n", GFA_FU_VER_MAJOR, GFA_FU_VER_MINOR, __DATE__, __TIME__);
-	printf(" USAGE: gfativaflashutil COMMAND [OPTIONS] [IMG FILE]\n\n");
-
-	printf(" COMMANDS (only one command can be executed at one go):\n");
-	printf("  --%-*s   Upload <IMG FILE> to the device.\n", _MAX_CMD_LENGTH, _OPT_STRING_UPLOAD_IMG);
-	printf("  --%-*s   Validate <IMG FILE> with the device. (Attempts to\n", _MAX_CMD_LENGTH, _OPT_STRING_VALIDATE_IMG);
-	printf("    %-*s   match the material numbers as well).\n", _MAX_CMD_LENGTH, "");
-	printf("  --%-*s   Validate <IMG FILE> offline and display image\n", _MAX_CMD_LENGTH, _OPT_STRING_SHOW_FILE_IMG_INFO);
-	printf("    %-*s   information.\n", _MAX_CMD_LENGTH, "");
-	printf("  --%-*s   Display bootloader and (if available) application\n", _MAX_CMD_LENGTH, _OPT_STRING_SHOW_DEV_IMG_INFO);
-	printf("    %-*s   image information of the target device.\n", _MAX_CMD_LENGTH, "");
-	printf("  --%-*s   Display the material and serial number of the\n", _MAX_CMD_LENGTH, _OPT_STRING_SHOW_MAT_SER);
-	printf("    %-*s   target device.\n", _MAX_CMD_LENGTH, "");
-	printf("  --%-*s   Set the material and serial number of the\n", _MAX_CMD_LENGTH, _OPT_STRING_SET_MAT_SER);
-	printf("    %-*s   target device.\n", _MAX_CMD_LENGTH, "");
-	printf("  --%-*s   Ping the target device either once or continuously\n", _MAX_CMD_LENGTH, _OPT_STRING_BOOT_PING);
-	printf("    %-*s   dependig on the parameter --%s.\n", _MAX_CMD_LENGTH, "", _OPT_STRING_PING_INTERVAL);
-	printf("  --%-*s   Start the bootloader if an application is running.\n", _MAX_CMD_LENGTH, _OPT_STRING_START_BOOT);
-	printf("  --%-*s   Reset the bootloader if no application is running.\n", _MAX_CMD_LENGTH, _OPT_STRING_RESET_BOOT);
-	printf("    %-*s   If there is a valid application image in flash, it\n", _MAX_CMD_LENGTH, "");
-	printf("    %-*s   will be subsequently started.\n", _MAX_CMD_LENGTH, "");
-	printf("  --%-*s   Start the bootloader if a modbus application is\n", _MAX_CMD_LENGTH, _OPT_STRING_MODBUS_START_BOOT);
-	printf("    %-*s   running.\n", _MAX_CMD_LENGTH, "");
-	printf("  --%-*s   Try to re-establish communication with the bootloader\n", _MAX_CMD_LENGTH, _OPT_STRING_RESCUE_BOOT);
-	printf("    %-*s   if it's in a non-responsive state. This may arise\n", _MAX_CMD_LENGTH, "");
-	printf("    %-*s   after a failed image upload due to an interrupted\n", _MAX_CMD_LENGTH, "");
-	printf("    %-*s   communication line or any transmission failures. It\n", _MAX_CMD_LENGTH, "");
-	printf("    %-*s   seems, that a verbosity of 4 in combination with high\n", _MAX_CMD_LENGTH, "");
-	printf("    %-*s   baud rates can cause this problem.\n", _MAX_CMD_LENGTH, "");
-	printf("\n");
-
-	printf(" OPTIONS:\n");
-	printf("  --%-*s -d <INTERFACE> The name of the communication interface.\n", _MAX_OPT_LENGTH, _OPT_STRING_ITF_NAME);
-	printf("  --%-*s -s <STATION>   Station number. Will be mapped internally\n", _MAX_OPT_LENGTH, _OPT_STRING_STATION_NUMBER);
-	printf("    %-*s                to a node address. Must not be used in\n", _MAX_OPT_LENGTH, "");
-	printf("    %-*s                combination with --%s.\n", _MAX_OPT_LENGTH, "", _OPT_STRING_NODE_ADDR);
-	printf("  --%-*s -n <NODE>      The target's node address. Must not be used\n", _MAX_OPT_LENGTH, _OPT_STRING_NODE_ADDR);
-	printf("    %-*s                in combination with --%s.\n", _MAX_OPT_LENGTH, "", _OPT_STRING_STATION_NUMBER);
-	printf("  --%-*s -x <BAUDRATE>  Extended baud-rate to use for some (but not\n", _MAX_OPT_LENGTH, _OPT_STRING_X_BAUD_RATE);
-	printf("    %-*s                all) commands. Because a baud-rate switch\n", _MAX_OPT_LENGTH, "");
-	printf("    %-*s                involves data transfer as well, it will\n", _MAX_OPT_LENGTH, "");
-	printf("    %-*s                only be used with commands that require a\n", _MAX_OPT_LENGTH, "");
-	printf("    %-*s                great amount of data to be transferred!\n", _MAX_OPT_LENGTH, "");
-	printf("  --%-*s -a <BASE ADDR> Flash address where to load <IMG FILE>.\n", _MAX_OPT_LENGTH, _OPT_STRING_APP_BASE_ADDR);
-	printf("    %-*s                Defaults to 0x%X.\n", _MAX_OPT_LENGTH, "", CMD_OPT_DEFAULT_APP_BASE_ADDRESS);
-	printf("  --%-*s    <MATERIAL>  The material number to be set with the\n", _MAX_OPT_LENGTH, _OPT_STRING_MATERIAL);
-	printf("    %-*s                --%s command.\n", _MAX_OPT_LENGTH, "", _OPT_STRING_SET_MAT_SER);
-	printf("  --%-*s    <SERIAL>    The serial number to be set with the\n", _MAX_OPT_LENGTH, _OPT_STRING_SERIAL);
-	printf("    %-*s                --%s command.\n", _MAX_OPT_LENGTH, "", _OPT_STRING_SET_MAT_SER);
-	printf("  --%-*s -p <BLOCK SIZE Size of an upload-block in bytes. (4-76,\n", _MAX_OPT_LENGTH, _OPT_STRING_BLOCK_SIZE);
-	printf("    %-*s                must be a multiple of 4!). This option is\n", _MAX_OPT_LENGTH, "");
-	printf("    %-*s                only used with the --%s command.\n", _MAX_OPT_LENGTH, "", _OPT_STRING_UPLOAD_IMG);
-	printf("    %-*s                Defaults to %d. There is usually no need\n", _MAX_OPT_LENGTH, "", GFA_BOOTLOADER_DEF_SEND_DATA_BLOCK_SIZE);
-	printf("    %-*s                to alter this value.\n", _MAX_OPT_LENGTH, "");
-	printf("  --%-*s -i <SECONDS>   The optional ping interval in seconds. If a\n", _MAX_OPT_LENGTH, _OPT_STRING_PING_INTERVAL);
-	printf("    %-*s                value greater than 0 is provided, a ping\n", _MAX_OPT_LENGTH, "");
-	printf("    %-*s                will be made every <SECONDS> seconds. If\n", _MAX_OPT_LENGTH, "");
-	printf("    %-*s                the value is 0 (the default), only one ping\n", _MAX_OPT_LENGTH, "");
-	printf("    %-*s                will be executed. Use Ctrl + 'C' to abort a\n", _MAX_OPT_LENGTH, "");
-	printf("    %-*s                continuous ping.\n", _MAX_OPT_LENGTH, "");
-	printf("  --%-*s                Don't show block upload progress. This\n", _MAX_OPT_LENGTH, _OPT_STRING_NO_SHOW_PROGRESS);
-	printf("    %-*s                option is especially useful when redirecting\n", _MAX_OPT_LENGTH, "");
-	printf("    %-*s                the program output to a file.\n", _MAX_OPT_LENGTH, "");
-	printf("  --%-*s -e <MILLISEC>  Timeout in milliseconds for erasing one\n", _MAX_OPT_LENGTH, _OPT_STRING_PAGE_ERASE_TIME);
-	printf("    %-*s                flash page (usually 1 Kb). This value will\n", _MAX_OPT_LENGTH, "");
-	printf("    %-*s                be multiplied with the number of pages to\n", _MAX_OPT_LENGTH, "");
-	printf("    %-*s                be erased. This option is only used with\n", _MAX_OPT_LENGTH, "");
-	printf("    %-*s                the --%s command. Defaults to %d ms.\n", _MAX_OPT_LENGTH, "", _OPT_STRING_UPLOAD_IMG, CMD_OPT_DEFAULT_PAGE_ERASE_TIME);
-	printf("    %-*s                It's usually not necessary to increase this\n", _MAX_OPT_LENGTH, "");
-	printf("    %-*s                value.\n", _MAX_OPT_LENGTH, "");
-	printf("  --%-*s    <SLAVEID>   The address of the modbus slave, if there is\n", _MAX_OPT_LENGTH, _OPT_STRING_MODBUS_SLAVE_ID);
-	printf("    %-*s                any. This option is only used with the\n", _MAX_OPT_LENGTH, "");
-	printf("    %-*s                --%s command.\n", _MAX_OPT_LENGTH, "", _OPT_STRING_MODBUS_START_BOOT);
-	printf("  --%-*s    <REGISTER>  Address of the first of the modbus registers\n", _MAX_OPT_LENGTH, _OPT_STRING_MODBUS_CTRL_REG);
-	printf("    %-*s                that control the start of the bootloader.\n", _MAX_OPT_LENGTH, "");
-	printf("    %-*s                This option is only used with the\n", _MAX_OPT_LENGTH, "");
-	printf("    %-*s                --%s command. Defaults to %d.\n", _MAX_OPT_LENGTH, "", _OPT_STRING_MODBUS_START_BOOT, CMD_OPT_DEFAULT_MODBUS_START_REGISTER);
-	printf("  --%-*s -v <0-4>       Verbosity. 0 = quiet, 1 = error, 2 = info,\n", _MAX_OPT_LENGTH, _OPT_STRING_VERBOSITY);
-	printf("    %-*s                3 = status, 4 = debug. Be careful using a\n", _MAX_OPT_LENGTH, "");
-	printf("    %-*s                verbosity of 4 with commands that can cause\n", _MAX_OPT_LENGTH, "");
-	printf("    %-*s                heavy data transfer like --%s!\n", _MAX_OPT_LENGTH, "", _OPT_STRING_UPLOAD_IMG);
-	printf("  --%-*s -?             Show this help\n", _MAX_OPT_LENGTH, _OPT_STRING_HELP);
-	printf("\n");
-}
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <time.h>
+#include <errno.h>
+#include <getopt.h>
+#include <gfaserial.h>
+#include <gfabootlmast.h>
+#include "main.h"
+#include "cmdopt.h"
+#include "error.h"
+#include "output.h"
+#include "modbmst.h"
+
+/////////////////////////////////////////////////////////////////////////////
+
+#define _IS_POWER_OF_2(x)						(!!(x) && !((x) & ((x) - 1)))
+#define _IS_VALID_STATION_NUMBER(s)				(((s) > 0) && ((s) <= 99))
+#define _IS_VALID_BLOCK_SIZE(s)					(((s) >= GFA_BOOTLOADER_MIN_SEND_DATA_BLOCK_SIZE) && ((s) <= GFA_BOOTLOADER_MAX_SEND_DATA_BLOCK_SIZE) && !((s) & 0x03))
+
+/////////////////////////////////////////////////////////////////////////////
+
+#define _OV_HAS_UPLOAD_IMG						((uint32_t)0x00000001)
+#define _OV_HAS_SHOW_FILE_IMG_INFO				((uint32_t)0x00000002)
+#define _OV_HAS_SHOW_DEV_IMG_INFO				((uint32_t)0x00000004)
+#define _OV_HAS_VALIDATE_IMG					((uint32_t)0x00000008)
+#define _OV_HAS_SHOW_MAT_SER					((uint32_t)0x00000010)
+#define _OV_HAS_SET_MAT_SER						((uint32_t)0x00000020)
+#define _OV_HAS_BOOT_PING						((uint32_t)0x00000040)
+#define _OV_HAS_START_BOOT						((uint32_t)0x00000080)
+#define _OV_HAS_RESET_BOOT						((uint32_t)0x00000100)
+#define _OV_HAS_RESCUE_BOOT						((uint32_t)0x00000200)
+
+#define _OV_HAS_IMG_FILE						((uint32_t)0x00001000)
+#define _OV_HAS_ITF_NAME						((uint32_t)0x00002000)
+#define _OV_HAS_BLOCK_SIZE						((uint32_t)0x00004000)
+#define _OV_HAS_APP_ADDR						((uint32_t)0x00008000)
+#define _OV_HAS_X_BAUD_RATE						((uint32_t)0x00010000)
+#define _OV_HAS_STATION_NUMBER					((uint32_t)0x00020000)
+#define _OV_HAS_NODE_ADDR						((uint32_t)0x00040000)
+#define _OV_HAS_SLAVE_ADDR						((uint32_t)0x00080000)
+#define _OV_HAS_MATERIAL						((uint32_t)0x00100000)
+#define _OV_HAS_SERIAL							((uint32_t)0x00200000)
+#define _OV_HAS_PING_INTERVAL					((uint32_t)0x00400000)
+#define _OV_HAS_NO_SHOW_PROGRESS				((uint32_t)0x00800000)
+#define _OV_HAS_PAGE_ERASE_TIME					((uint32_t)0x01000000)
+#define _OV_HAS_MODBUS_SLAVE_ID					((uint32_t)0x02000000)
+#define _OV_HAS_MODBUS_CTRL_REG					((uint32_t)0x04000000)
+#define _OV_HAS_FORCE_ALL_PARITIES				((uint32_t)0x08000000)
+
+/////////////////////////////////////////////////////////////////////////////
+
+#define _OV_CMD_MASK							((uint32_t)0x00000FFF)
+#define _OV_OPT_MASK							(~_OV_CMD_MASK)
+
+/////////////////////////////////////////////////////////////////////////////
+
+#define _REQ_CONNECTION_OPTS					(_OV_HAS_ITF_NAME | _OV_HAS_SLAVE_ADDR)
+
+#define _REQ_OPTS_UPLOAD_IMG					(_OV_HAS_IMG_FILE | _REQ_CONNECTION_OPTS)
+#define _REQ_OPTS_SHOW_IMG_INFO_OFFLINE			_OV_HAS_IMG_FILE
+#define _REQ_OPTS_SHOW_IMG_INFO_ONLINE			_REQ_CONNECTION_OPTS
+#define _REQ_OPTS_VALIDATE_IMG					(_OV_HAS_IMG_FILE | _REQ_CONNECTION_OPTS)
+#define _REQ_OPTS_SHOW_MAT_SER					_REQ_CONNECTION_OPTS
+#define _REQ_OPTS_SET_MAT_SER					(_REQ_CONNECTION_OPTS | _OV_HAS_MATERIAL | _OV_HAS_SERIAL)
+#define _REQ_OPTS_BOOT_PING						_REQ_CONNECTION_OPTS
+#define _REQ_OPTS_START_BOOT					_REQ_CONNECTION_OPTS
+#define _REQ_OPTS_RESET_BOOT					_REQ_CONNECTION_OPTS
+#define _REQ_OPTS_RESCUE_BOOT					_REQ_CONNECTION_OPTS
+
+/////////////////////////////////////////////////////////////////////////////
+
+#define _OPT_CONNECTION_OPTS					(_OV_HAS_STATION_NUMBER | _OV_HAS_NODE_ADDR | _OV_HAS_MODBUS_SLAVE_ID | _OV_HAS_MODBUS_CTRL_REG | _OV_HAS_FORCE_ALL_PARITIES)
+
+#define _OPT_OPTS_UPLOAD_IMG					(_OPT_CONNECTION_OPTS | _OV_HAS_IMG_FILE | _OV_HAS_APP_ADDR | _OV_HAS_X_BAUD_RATE | _OV_HAS_NO_SHOW_PROGRESS | _OV_HAS_PAGE_ERASE_TIME | _OV_HAS_BLOCK_SIZE)
+#define _OPT_OPTS_SHOW_IMG_INFO_OFFLINE			_OV_HAS_APP_ADDR
+#define _OPT_OPTS_SHOW_IMG_INFO_ONLINE			(_OPT_CONNECTION_OPTS | _OV_HAS_X_BAUD_RATE | _OV_HAS_APP_ADDR)
+#define _OPT_OPTS_VALIDATE_IMG					(_OPT_CONNECTION_OPTS | _OV_HAS_APP_ADDR)
+#define _OPT_OPTS_SHOW_MAT_SER					(_OPT_CONNECTION_OPTS)
+#define _OPT_OPTS_SET_MAT_SER					(_OPT_CONNECTION_OPTS)
+#define _OPT_OPTS_BOOT_PING						(_OPT_CONNECTION_OPTS | _OV_HAS_PING_INTERVAL)
+#define _OPT_OPTS_START_BOOT					_OPT_CONNECTION_OPTS
+#define _OPT_OPTS_RESET_BOOT					_OPT_CONNECTION_OPTS
+#define _OPT_OPTS_RESCUE_BOOT					_OPT_CONNECTION_OPTS
+
+/////////////////////////////////////////////////////////////////////////////
+
+#define _GET_OPTS(m)							((m) & _OV_OPT_MASK)
+#define _GET_CMD(m)								((m) & _OV_CMD_MASK)
+#define _HAS_CMD(m)								(!!_GET_CMD(m))
+#define _HAS_VALID_CMD(m)						(_HAS_CMD(m) && _IS_POWER_OF_2(_GET_CMD(m)))
+#define _IS_OFFLINE_CMD(m)						(_GET_CMD(m) == _OV_HAS_SHOW_FILE_IMG_INFO)
+#define _HAS_REQUIRED_OPTIONS(m, r)				((_GET_OPTS(m) & (r)) == (r))
+#define _GET_MISSING_OPTS(m, r)					((_GET_OPTS(m) ^ (r)) & (r))
+#define _GET_UNUSED_OPTS(m, r, o)				((_GET_OPTS(m) ^ (r)) & ~(o))
+
+/////////////////////////////////////////////////////////////////////////////
+
+#define _OPT_STRING_UPLOAD_IMG					"upload-img"
+#define _OPT_STRING_VALIDATE_IMG				"validate-img"
+#define _OPT_STRING_SHOW_FILE_IMG_INFO			"show-file-img-info"
+#define _OPT_STRING_SHOW_DEV_IMG_INFO			"show-dev-img-info"
+#define _OPT_STRING_SHOW_MAT_SER				"show-mat-ser"
+#define _OPT_STRING_SET_MAT_SER					"set-mat-ser"
+#define _OPT_STRING_BOOT_PING					"ping-target"
+#define _OPT_STRING_START_BOOT					"start-boot"
+#define _OPT_STRING_RESET_BOOT					"reset-boot"
+#define _OPT_STRING_RESCUE_BOOT					"revive-boot"
+#define _OPT_STRING_ITF_NAME					"itf-name"
+#define _OPT_STRING_X_BAUD_RATE					"x-baud-rate"
+#define _OPT_STRING_STATION_NUMBER				"stat-num"
+#define _OPT_STRING_NODE_ADDR					"node-addr"
+#define _OPT_STRING_MATERIAL					"material"
+#define _OPT_STRING_SERIAL						"serial"
+#define _OPT_STRING_SLAVE_ADDR					"stat-num or node-addr"
+#define _OPT_STRING_APP_BASE_ADDR				"app-addr"
+#define _OPT_STRING_BLOCK_SIZE					"block-size"
+#define _OPT_STRING_PING_INTERVAL				"ping-int"
+#define _OPT_STRING_PAGE_ERASE_TIME				"page-erase-time"
+#define _OPT_STRING_NO_SHOW_PROGRESS			"no-progress"
+#define _OPT_STRING_MODBUS_SLAVE_ID				"mb-slave-id"
+#define _OPT_STRING_MODBUS_CTRL_REG				"mb-bl-ctrl-reg"
+#define _OPT_STRING_FORCE_ALL_PARITIES			"force-all-par"
+#define _OPT_STRING_VERBOSITY					"verbosity"
+#define _OPT_STRING_HELP						"help"
+#define _OPT_STRING_IMG_FILE					"<IMG FILE>"
+
+#define _MAX_CMD_LENGTH							18
+#define _MAX_OPT_LENGTH							15
+
+/////////////////////////////////////////////////////////////////////////////
+
+int g_nVerbosity = CMD_OPT_DEFAULT_VERBOSITY;
+
+/////////////////////////////////////////////////////////////////////////////
+
+static const uint8_t g_nodeTable[] =
+{
+	// 1 - 99
+	0x11,	0x12,	0x13,	0x14,	0x15,	0x16,	0x17,	0x18,	0x19,	0x1A,
+	0x21,	0x22,	0x23,	0x24,	0x25,	0x26,	0x27,	0x28,	0x29,	0x2A,
+	0x31,	0x32,	0x33,	0x34,	0x35,	0x36,	0x37,	0x38,	0x39,	0x3A,
+	0x41,	0x42,	0x43,	0x44,	0x45,	0x46,	0x47,	0x48,	0x49,	0x4A,
+	0x51,	0x52,	0x53,	0x54,	0x55,	0x56,	0x57,	0x58,	0x59,	0x5A,
+	0x61,	0x62,	0x63,	0x64,	0x65,	0x66,	0x67,	0x68,	0x69,	0x6A,
+	0x71,	0x72,	0x73,	0x74,	0x75,	0x76,	0x77,	0x78,	0x79,	0x7A,
+	0x81,	0x82,	0x83,	0x84,	0x85,	0x86,	0x87,	0x88,	0x89,	0x8A,
+	0x91,	0x92,	0x93,	0x94,	0x95,	0x96,	0x97,	0x98,	0x99,	0x9A,
+	0xA1,	0xA2,	0xA3,	0xA4,	0xA5,	0xA6,	0xA7,	0xA8,	0xA9
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+static const char* _GetOptName(uint32_t nOpt)
+{
+	switch(nOpt)
+	{
+	case _OV_HAS_UPLOAD_IMG:
+		return _OPT_STRING_UPLOAD_IMG;
+	case _OV_HAS_VALIDATE_IMG:
+		return _OPT_STRING_VALIDATE_IMG;
+	case _OV_HAS_SHOW_FILE_IMG_INFO:
+		return _OPT_STRING_SHOW_FILE_IMG_INFO;
+	case _OV_HAS_SHOW_DEV_IMG_INFO:
+		return _OPT_STRING_SHOW_DEV_IMG_INFO;
+	case _OV_HAS_SHOW_MAT_SER:
+		return _OPT_STRING_SHOW_MAT_SER;
+	case _OV_HAS_BOOT_PING:
+		return _OPT_STRING_BOOT_PING;
+	case _OV_HAS_START_BOOT:
+		return _OPT_STRING_START_BOOT;
+	case _OV_HAS_RESET_BOOT:
+		return _OPT_STRING_RESET_BOOT;
+	case _OV_HAS_RESCUE_BOOT:
+		return _OPT_STRING_RESCUE_BOOT;
+	case _OV_HAS_PING_INTERVAL:
+		return _OPT_STRING_PING_INTERVAL;
+	case _OV_HAS_NO_SHOW_PROGRESS:
+		return _OPT_STRING_NO_SHOW_PROGRESS;
+	case _OV_HAS_ITF_NAME:
+		return _OPT_STRING_ITF_NAME;
+	case _OV_HAS_BLOCK_SIZE:
+		return _OPT_STRING_BLOCK_SIZE;
+	case _OV_HAS_APP_ADDR:
+		return _OPT_STRING_APP_BASE_ADDR;
+	case _OV_HAS_X_BAUD_RATE:
+		return _OPT_STRING_X_BAUD_RATE;
+	case _OV_HAS_PAGE_ERASE_TIME:
+		return _OPT_STRING_PAGE_ERASE_TIME;
+	case _OV_HAS_STATION_NUMBER:
+		return _OPT_STRING_STATION_NUMBER;
+	case _OV_HAS_NODE_ADDR:
+		return _OPT_STRING_NODE_ADDR;
+	case _OV_HAS_SLAVE_ADDR:
+		return _OPT_STRING_SLAVE_ADDR;
+	case _OV_HAS_MATERIAL:
+		return _OPT_STRING_MATERIAL;
+	case _OV_HAS_SERIAL:
+		return _OPT_STRING_SERIAL;
+	case _OV_HAS_SET_MAT_SER:
+		return _OPT_STRING_SET_MAT_SER;
+	case _OV_HAS_IMG_FILE:
+		return _OPT_STRING_IMG_FILE;
+	case _OV_HAS_MODBUS_SLAVE_ID:
+		return _OPT_STRING_MODBUS_SLAVE_ID;
+	case _OV_HAS_MODBUS_CTRL_REG:
+		return _OPT_STRING_MODBUS_CTRL_REG;
+	case _OV_HAS_FORCE_ALL_PARITIES:
+		return _OPT_STRING_FORCE_ALL_PARITIES;
+	default:
+		return NULL;
+	}
+}
+
+static const char* _GetOptValue(uint32_t nOpt, LPCMD_LINE_ARGS pcla)
+{
+	static char szValue[32];
+
+	switch(nOpt)
+	{
+	case _OV_HAS_UPLOAD_IMG:
+		return _OPT_STRING_UPLOAD_IMG;
+	case _OV_HAS_VALIDATE_IMG:
+		return _OPT_STRING_VALIDATE_IMG;
+	case _OV_HAS_SHOW_FILE_IMG_INFO:
+		return _OPT_STRING_SHOW_FILE_IMG_INFO;
+	case _OV_HAS_SHOW_DEV_IMG_INFO:
+		return _OPT_STRING_SHOW_DEV_IMG_INFO;
+	case _OV_HAS_SHOW_MAT_SER:
+		return _OPT_STRING_SHOW_MAT_SER;
+	case _OV_HAS_BOOT_PING:
+		return _OPT_STRING_BOOT_PING;
+	case _OV_HAS_START_BOOT:
+		return _OPT_STRING_START_BOOT;
+	case _OV_HAS_RESET_BOOT:
+		return _OPT_STRING_RESET_BOOT;
+	case _OV_HAS_RESCUE_BOOT:
+		return _OPT_STRING_RESCUE_BOOT;
+
+	case _OV_HAS_PING_INTERVAL:
+		snprintf(szValue, sizeof(szValue) - 1, "%d", pcla->nPingIntervalSec);
+		break;
+	case _OV_HAS_ITF_NAME:
+		return pcla->pszDevName ? pcla->pszDevName : "empty";
+	case _OV_HAS_BLOCK_SIZE:
+		snprintf(szValue, sizeof(szValue) - 1, "%u", pcla->nBlockSize);
+		break;
+	case _OV_HAS_APP_ADDR:
+		snprintf(szValue, sizeof(szValue) - 1, "0x%X", pcla->nStartAddr);
+		break;
+	case _OV_HAS_X_BAUD_RATE:
+		snprintf(szValue, sizeof(szValue) - 1, "%u", pcla->nExBaudrate);
+		break;
+	case _OV_HAS_PAGE_ERASE_TIME:
+		snprintf(szValue, sizeof(szValue) - 1, "%u", pcla->nPageErsaeTime);
+		break;
+	case _OV_HAS_STATION_NUMBER:
+		snprintf(szValue, sizeof(szValue) - 1, "%hhu", pcla->nStationNr);
+		break;
+	case _OV_HAS_NODE_ADDR:
+		snprintf(szValue, sizeof(szValue) - 1, "0x%02hhX", pcla->nNodeAddr);
+		break;
+	case _OV_HAS_SLAVE_ADDR:
+		return _OPT_STRING_SLAVE_ADDR;
+	case _OV_HAS_MATERIAL:
+		return pcla->pszMaterial ? pcla->pszMaterial : "empty";
+	case _OV_HAS_SERIAL:
+		return pcla->pszSerial ? pcla->pszSerial : "empty";
+	case _OV_HAS_IMG_FILE:
+		return pcla->pszImgFile ? pcla->pszImgFile : "empty";
+	case _OV_HAS_NO_SHOW_PROGRESS:
+		return pcla->bNoProgressBlock ? "true" : "false";
+	case _OV_HAS_MODBUS_SLAVE_ID:
+		snprintf(szValue, sizeof(szValue) - 1, "%hhu", pcla->nModbusSlvID);
+		break;
+	case _OV_HAS_MODBUS_CTRL_REG:
+		snprintf(szValue, sizeof(szValue) - 1, "%hu", pcla->nModbusCtrlReg);
+		break;
+	case _OV_HAS_FORCE_ALL_PARITIES:
+		return "true";
+	default:
+		return "empty";
+	}
+
+	szValue[31] = '\0';
+	return szValue;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+const char* GfaTfuCmdOpt2String(uint32_t nOpts)
+{
+	static char szOptStr[600];
+
+	if(nOpts)
+	{
+		int nCount = 0;
+		uint32_t nMask = 1;
+		memset(szOptStr, '\0', sizeof(szOptStr));
+
+		while(nMask)
+		{
+			if(nOpts & nMask)
+			{
+				const char *pszOptName = _GetOptName(nMask);
+				if(pszOptName)
+				{
+					if(nCount++)
+						strcat(szOptStr, ", ");
+					strcat(szOptStr, pszOptName);
+				}
+			}
+
+			nMask <<= 1;
+		}
+
+		return szOptStr;
+	}
+
+	return "";
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+static const struct option g_lo[] =
+{
+	{
+		_OPT_STRING_UPLOAD_IMG,
+		no_argument,
+		NULL,
+		ov_upload_img
+	},
+	{
+		_OPT_STRING_VALIDATE_IMG,
+		no_argument,
+		NULL,
+		ov_validate_img
+	},
+	{
+		_OPT_STRING_SHOW_FILE_IMG_INFO,
+		no_argument,
+		NULL,
+		ov_show_file_img_info
+	},
+	{
+		_OPT_STRING_SHOW_DEV_IMG_INFO,
+		no_argument,
+		NULL,
+		ov_show_dev_img_info
+	},
+	{
+		_OPT_STRING_SHOW_MAT_SER,
+		no_argument,
+		NULL,
+		ov_show_mat_ser
+	},
+	{
+		_OPT_STRING_SET_MAT_SER,
+		no_argument,
+		NULL,
+		ov_set_mat_ser
+	},
+	{
+		_OPT_STRING_MATERIAL,
+		required_argument,
+		NULL,
+		ov_material
+	},
+	{
+		_OPT_STRING_SERIAL,
+		required_argument,
+		NULL,
+		ov_serial
+	},
+	{
+		_OPT_STRING_NO_SHOW_PROGRESS,
+		no_argument,
+		NULL,
+		ov_no_progress
+	},
+	{
+		_OPT_STRING_BOOT_PING,
+		no_argument,
+		NULL,
+		ov_boot_ping
+	},
+	{
+		_OPT_STRING_START_BOOT,
+		no_argument,
+		NULL,
+		ov_start_boot
+	},
+	{
+		_OPT_STRING_RESET_BOOT,
+		no_argument,
+		NULL,
+		ov_reset_boot
+	},
+	{
+		_OPT_STRING_RESCUE_BOOT,
+		no_argument,
+		NULL,
+		ov_rescue_boot
+	},
+	{
+		_OPT_STRING_PING_INTERVAL,
+		required_argument,
+		NULL,
+		ov_ping_interval
+	},
+	{
+		_OPT_STRING_ITF_NAME,
+		required_argument,
+		NULL,
+		ov_itf_name
+	},
+	{
+		_OPT_STRING_X_BAUD_RATE,
+		required_argument,
+		NULL,
+		ov_x_baud_rate
+	},
+	{
+		_OPT_STRING_STATION_NUMBER,
+		required_argument,
+		NULL,
+		ov_station_number
+	},
+	{
+		_OPT_STRING_NODE_ADDR,
+		required_argument,
+		NULL,
+		ov_node_addr
+	},
+	{
+		_OPT_STRING_PAGE_ERASE_TIME,
+		required_argument,
+		NULL,
+		ov_page_erase_time
+	},
+	{
+		_OPT_STRING_APP_BASE_ADDR,
+		required_argument,
+		NULL,
+		ov_app_addr
+	},
+	{
+		_OPT_STRING_BLOCK_SIZE,
+		required_argument,
+		NULL,
+		ov_block_size
+	},
+	{
+		_OPT_STRING_MODBUS_SLAVE_ID,
+		required_argument,
+		NULL,
+		ov_mb_slave_id
+	},
+	{
+		_OPT_STRING_MODBUS_CTRL_REG,
+		required_argument,
+		NULL,
+		ov_mb_ctrl_reg
+	},
+	{
+		_OPT_STRING_FORCE_ALL_PARITIES,
+		no_argument,
+		NULL,
+		ov_force_all_par
+	},
+	{
+		_OPT_STRING_VERBOSITY,
+		required_argument,
+		NULL,
+		ov_verbosity
+	},
+	{
+		_OPT_STRING_HELP,
+		no_argument,
+		NULL,
+		ov_help
+	},
+	{
+		NULL,
+		0,
+		NULL,
+		0
+	}
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+static int64_t _ArgStr2Num(const char *pszNum, int64_t nDefault)
+{
+	if(pszNum && *pszNum)
+	{
+		char *pszEnd = NULL;
+		int64_t n = strtoll(pszNum, &pszEnd, 0);
+		if((((n == LLONG_MIN) || (n == LLONG_MAX)) && (errno == ERANGE)) || *pszEnd)
+			return nDefault;
+		return n;
+	}
+
+	return nDefault;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+
+void GfaTfuCmdOptInitOpts(LPCMD_LINE_ARGS pcla)
+{
+	memset(pcla, 0, sizeof(CMD_LINE_ARGS));
+	pcla->nBootBaudrate		= CMD_OPT_DEFAULT_BAUDRATE;
+	pcla->nInitBaudrate		= CMD_OPT_DEFAULT_BAUDRATE;
+	pcla->nModbBaudrate		= CMD_OPT_DEFAULT_BAUDRATE;
+	pcla->modbParity		= CMD_OPT_DEFAULT_MODBUS_PARITY;
+	pcla->nBlockSize		= GFA_BOOTLOADER_DEF_SEND_DATA_BLOCK_SIZE;
+	pcla->nStartAddr		= CMD_OPT_DEFAULT_APP_BASE_ADDRESS;
+	pcla->nPageErsaeTime	= CMD_OPT_DEFAULT_PAGE_ERASE_TIME;
+	pcla->nVerbosity		= CMD_OPT_DEFAULT_VERBOSITY;
+	pcla->nModbusCtrlReg	= CMD_OPT_DEFAULT_MODBUS_START_REGISTER;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+int GfaTfuCmdOptParse(int argc, char* argv[], LPCMD_LINE_ARGS pcla)
+{
+	int opt, nCount = 0;
+	pcla->nOptFlags = 0;
+
+	while((opt = getopt_long(argc, argv, "-a:b:d:e:fgi:m:n:ps:t:v:x:?", g_lo, NULL)) != -1)
+	{
+		const char *arg = optarg;
+
+		switch(opt)
+		{
+		case ov_img_file:
+			if(arg)
+			{
+				const char *pszFileBase = strrchr(arg, '/');
+				pcla->pszImgFile = arg;
+				if(pszFileBase)
+					pcla->pszImgFileBase = ++pszFileBase;
+				pcla->nOptFlags |= _OV_HAS_IMG_FILE;
+			}
+			break;
+		case ov_itf_name:
+			if(arg)
+			{
+				pcla->pszDevName = arg;
+				pcla->nOptFlags |= _OV_HAS_ITF_NAME;
+			}
+			break;
+		case ov_show_dev_img_info:
+			pcla->bShowDevImgInfo = true;
+			pcla->nOptFlags |= _OV_HAS_SHOW_DEV_IMG_INFO;
+			break;
+		case ov_show_file_img_info:
+			pcla->bShowFileImgInfo = true;
+			pcla->nOptFlags |= _OV_HAS_SHOW_FILE_IMG_INFO;
+			break;
+		case ov_show_mat_ser:
+			pcla->bShowMatSer = true;
+			pcla->nOptFlags |= _OV_HAS_SHOW_MAT_SER;
+			break;
+		case ov_set_mat_ser:
+			pcla->bSetMatSer = true;
+			pcla->nOptFlags |= _OV_HAS_SET_MAT_SER;
+			break;
+		case ov_validate_img:
+			pcla->bValidateImg = true;
+			pcla->nOptFlags |= _OV_HAS_VALIDATE_IMG;
+			break;
+		case ov_upload_img:
+			pcla->bUploadImg = true;
+			pcla->nOptFlags |= _OV_HAS_UPLOAD_IMG;
+			break;
+		case ov_boot_ping:
+			pcla->bPing = true;
+			pcla->nOptFlags |= _OV_HAS_BOOT_PING;
+			break;
+		case ov_start_boot:
+			pcla->bStartBoot = true;
+			pcla->nOptFlags |= _OV_HAS_START_BOOT;
+			break;
+		case ov_reset_boot:
+			pcla->bResetBoot = true;
+			pcla->nOptFlags |= _OV_HAS_RESET_BOOT;
+			break;
+		case ov_rescue_boot:
+			pcla->bReviveBoot = true;
+			pcla->nOptFlags |= _OV_HAS_RESCUE_BOOT;
+			break;
+		case ov_ping_interval:
+			pcla->nPingIntervalSec = (int32_t)_ArgStr2Num(arg, 0);
+			if(pcla->nPingIntervalSec > 0)
+				pcla->nOptFlags |= _OV_HAS_PING_INTERVAL;
+			break;
+		case ov_material:
+			if(arg && *arg)
+			{
+				pcla->pszMaterial = arg;
+				pcla->nOptFlags |= _OV_HAS_MATERIAL;
+			}
+			break;
+		case ov_serial:
+			if(arg && *arg)
+			{
+				pcla->pszSerial = arg;
+				pcla->nOptFlags |= _OV_HAS_SERIAL;
+			}
+			break;
+		case ov_no_progress:
+			pcla->bNoProgressBlock = true;
+			pcla->nOptFlags |= _OV_HAS_NO_SHOW_PROGRESS;
+			break;
+		case ov_block_size:
+			pcla->nBlockSize = (uint32_t)_ArgStr2Num(arg, GFA_BOOTLOADER_DEF_SEND_DATA_BLOCK_SIZE);
+			pcla->nOptFlags |= _OV_HAS_BLOCK_SIZE;
+			break;
+		case ov_app_addr:
+			pcla->nStartAddr = (uint32_t)_ArgStr2Num(arg, CMD_OPT_DEFAULT_APP_BASE_ADDRESS);
+			pcla->nOptFlags |= _OV_HAS_APP_ADDR;
+			break;
+		case ov_x_baud_rate:
+			pcla->nExBaudrate = (uint32_t)_ArgStr2Num(arg, 0);
+			pcla->nOptFlags |= _OV_HAS_X_BAUD_RATE;
+			break;
+		case ov_page_erase_time:
+			pcla->nPageErsaeTime = (uint32_t)_ArgStr2Num(arg, CMD_OPT_DEFAULT_PAGE_ERASE_TIME);
+			pcla->nOptFlags |= _OV_HAS_PAGE_ERASE_TIME;
+			break;
+		case ov_station_number:
+			pcla->nStationNr = (uint8_t)_ArgStr2Num(arg, 0);
+			pcla->nOptFlags |= _OV_HAS_STATION_NUMBER;
+			pcla->nOptFlags |= _OV_HAS_SLAVE_ADDR;
+			break;
+		case ov_node_addr:
+			pcla->nNodeAddr = (uint8_t)_ArgStr2Num(arg, 0);
+			pcla->nOptFlags |= _OV_HAS_NODE_ADDR;
+			pcla->nOptFlags |= _OV_HAS_SLAVE_ADDR;
+			break;
+		case ov_mb_slave_id:
+			pcla->nModbusSlvID = (uint8_t)_ArgStr2Num(arg, 0xFF);
+			pcla->nOptFlags |= _OV_HAS_MODBUS_SLAVE_ID;
+			break;
+		case ov_mb_ctrl_reg:
+			pcla->nModbusCtrlReg = (uint16_t)_ArgStr2Num(arg, CMD_OPT_DEFAULT_MODBUS_START_REGISTER);
+			pcla->nOptFlags |= _OV_HAS_MODBUS_CTRL_REG;
+			break;
+		case ov_force_all_par:
+			pcla->bForceAllParities = true;
+			pcla->nOptFlags |= _OV_HAS_FORCE_ALL_PARITIES;
+			break;
+		case ov_verbosity:
+			pcla->nVerbosity = (int)_ArgStr2Num(arg, CMD_OPT_DEFAULT_VERBOSITY);
+			break;
+		case ov_help:
+			++pcla->nQuestionMarks;
+			pcla->bShowHelp = true;
+			break;
+		}
+
+		++nCount;
+	}
+
+	if(nCount == 0)
+		pcla->bShowHelp = true;
+
+	return nCount;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+int GfaTfuCmdOptProcess(LPCMD_LINE_ARGS pcla)
+{
+	/////////////////////////////////////////////////////////////////////////
+	// If help is requested, do nothing else
+
+	if(pcla->bShowHelp)
+		return 1;
+
+	/////////////////////////////////////////////////////////////////////////
+	// adjust verbosity
+
+	if(pcla->nVerbosity < CMD_OPT_MIN_VERBOSITY)
+		pcla->nVerbosity = CMD_OPT_MIN_VERBOSITY;
+	else if(pcla->nVerbosity > CMD_OPT_MAX_VERBOSITY)
+		pcla->nVerbosity = CMD_OPT_MAX_VERBOSITY;
+	g_nVerbosity = pcla->nVerbosity;
+
+	/////////////////////////////////////////////////////////////////////////
+	// validate commands
+
+	pcla->nCmdFlags = _GET_CMD(pcla->nOptFlags);
+	if(!_HAS_CMD(pcla->nOptFlags))
+	{
+		if(!(pcla->nOptFlags & (_OV_HAS_MATERIAL | _OV_HAS_SERIAL)))
+			return GFA_FU_ERROR_NOTHING_TO_DO;
+	}
+	else if(!_HAS_VALID_CMD(pcla->nOptFlags))
+		return GFA_FU_ERROR_MULTIPLE_COMMANDS;
+
+	/////////////////////////////////////////////////////////////////////////
+	// validate connection options if required
+
+	if(!_IS_OFFLINE_CMD(pcla->nOptFlags))
+	{
+		if(!pcla->nExBaudrate)
+			pcla->nExBaudrate = pcla->nInitBaudrate;
+
+		if(!GfaSerialIsValidBaudrate(pcla->nExBaudrate))
+			return GFA_FU_ERROR_INVALID_BAUDRATE;
+		else if((pcla->nOptFlags & (_OV_HAS_STATION_NUMBER | _OV_HAS_NODE_ADDR)) == (_OV_HAS_STATION_NUMBER | _OV_HAS_NODE_ADDR))
+			return GFA_FU_ERROR_NODE_STATION_MUTEX;
+		else if(pcla->nOptFlags & _OV_HAS_STATION_NUMBER)
+		{
+			if(!_IS_VALID_STATION_NUMBER(pcla->nStationNr))
+				return GFA_FU_ERROR_INVALID_STATION_NUM;
+			pcla->nNodeAddr = g_nodeTable[pcla->nStationNr - 1];
+		}
+		else if((pcla->nOptFlags & _OV_HAS_NODE_ADDR) && (NODE_IS_MULTICAST(pcla->nNodeAddr) || (pcla->nNodeAddr < MINET_MIN_NODE_ADDRESS)))
+			return GFA_FU_ERROR_INVALID_NODE_ADDR;
+	}
+
+	/////////////////////////////////////////////////////////////////////////
+	// validate command options
+
+	switch(pcla->nCmdFlags)
+	{
+	case _OV_HAS_UPLOAD_IMG:
+		if(!_HAS_REQUIRED_OPTIONS(pcla->nOptFlags, _REQ_OPTS_UPLOAD_IMG))
+		{
+			pcla->nMissingOptFlags = _GET_MISSING_OPTS(pcla->nOptFlags, _REQ_OPTS_UPLOAD_IMG);
+			return GFA_FU_ERROR_MISSING_COMMAND_OPT;
+		}
+		else if((pcla->nOptFlags & _OV_HAS_BLOCK_SIZE) && !_IS_VALID_BLOCK_SIZE(pcla->nBlockSize))
+			return GFA_FU_ERROR_INVALID_BLOCK_SIZE;
+		else if(pcla->nStartAddr == (uint32_t)-1)
+			return GFA_FU_ERROR_INVALID_APP_START_ADDR;
+		pcla->nUnusedOptFlags = _GET_UNUSED_OPTS(pcla->nOptFlags, _REQ_OPTS_UPLOAD_IMG, _OPT_OPTS_UPLOAD_IMG);
+		pcla->bNeedImgFile = true;
+		break;
+	case _OV_HAS_SHOW_FILE_IMG_INFO:
+		if(!_HAS_REQUIRED_OPTIONS(pcla->nOptFlags, _REQ_OPTS_SHOW_IMG_INFO_OFFLINE))
+		{
+			pcla->nMissingOptFlags = _GET_MISSING_OPTS(pcla->nOptFlags, _REQ_OPTS_SHOW_IMG_INFO_OFFLINE);
+			return GFA_FU_ERROR_MISSING_COMMAND_OPT;
+		}
+		pcla->nUnusedOptFlags = _GET_UNUSED_OPTS(pcla->nOptFlags, _REQ_OPTS_SHOW_IMG_INFO_OFFLINE, _OPT_OPTS_SHOW_IMG_INFO_OFFLINE);
+		pcla->bNeedImgFile = true;
+		break;
+	case _OV_HAS_SHOW_DEV_IMG_INFO:
+		if(!_HAS_REQUIRED_OPTIONS(pcla->nOptFlags, _REQ_OPTS_SHOW_IMG_INFO_ONLINE))
+		{
+			pcla->nMissingOptFlags = _GET_MISSING_OPTS(pcla->nOptFlags, _REQ_OPTS_SHOW_IMG_INFO_ONLINE);
+			return GFA_FU_ERROR_MISSING_COMMAND_OPT;
+		}
+		pcla->nUnusedOptFlags = _GET_UNUSED_OPTS(pcla->nOptFlags, _REQ_OPTS_SHOW_IMG_INFO_ONLINE, _OPT_OPTS_SHOW_IMG_INFO_ONLINE);
+		break;
+	case _OV_HAS_VALIDATE_IMG:
+		if(!_HAS_REQUIRED_OPTIONS(pcla->nOptFlags, _REQ_OPTS_VALIDATE_IMG))
+		{
+			pcla->nMissingOptFlags = _GET_MISSING_OPTS(pcla->nOptFlags, _REQ_OPTS_VALIDATE_IMG);
+			return GFA_FU_ERROR_MISSING_COMMAND_OPT;
+		}
+		pcla->nUnusedOptFlags = _GET_UNUSED_OPTS(pcla->nOptFlags, _REQ_OPTS_VALIDATE_IMG, _OPT_OPTS_VALIDATE_IMG);
+		pcla->bNeedImgFile = true;
+		break;
+	case _OV_HAS_SHOW_MAT_SER:
+		if(!_HAS_REQUIRED_OPTIONS(pcla->nOptFlags, _REQ_OPTS_SHOW_MAT_SER))
+		{
+			pcla->nMissingOptFlags = _GET_MISSING_OPTS(pcla->nOptFlags, _REQ_OPTS_SHOW_MAT_SER);
+			return GFA_FU_ERROR_MISSING_COMMAND_OPT;
+		}
+		pcla->nUnusedOptFlags = _GET_UNUSED_OPTS(pcla->nOptFlags, _REQ_OPTS_SHOW_MAT_SER, _OPT_OPTS_SHOW_MAT_SER);
+		break;
+	case _OV_HAS_SET_MAT_SER:
+		if(!_HAS_REQUIRED_OPTIONS(pcla->nOptFlags, _REQ_OPTS_SET_MAT_SER))
+		{
+			pcla->nMissingOptFlags = _GET_MISSING_OPTS(pcla->nOptFlags, _REQ_OPTS_SET_MAT_SER);
+			return GFA_FU_ERROR_MISSING_COMMAND_OPT;
+		}
+		if(	(strlen(pcla->pszMaterial) >= GFA_APP_MAX_IMG_MATERIAL_NUM_LENGTH) ||
+			(strlen(pcla->pszSerial) >= GFA_APP_MAX_IMG_SERIAL_NUM_LENGTH))
+		{
+			return GFA_FU_ERROR_MAT_OR_SER_TOO_LONG;
+		}
+		pcla->nUnusedOptFlags = _GET_UNUSED_OPTS(pcla->nOptFlags, _REQ_OPTS_SET_MAT_SER, _OPT_OPTS_SET_MAT_SER);
+		break;
+	case _OV_HAS_BOOT_PING:
+		if(!_HAS_REQUIRED_OPTIONS(pcla->nOptFlags, _REQ_OPTS_BOOT_PING))
+		{
+			pcla->nMissingOptFlags = _GET_MISSING_OPTS(pcla->nOptFlags, _REQ_OPTS_BOOT_PING);
+			return GFA_FU_ERROR_MISSING_COMMAND_OPT;
+		}
+		else if(pcla->nPingIntervalSec < 0)
+			return GFA_FU_ERROR_INVALID_COMMAND_OPT;
+		pcla->nUnusedOptFlags = _GET_UNUSED_OPTS(pcla->nOptFlags, _REQ_OPTS_BOOT_PING, _OPT_OPTS_BOOT_PING);
+		break;
+	case _OV_HAS_START_BOOT:
+		if(!_HAS_REQUIRED_OPTIONS(pcla->nOptFlags, _REQ_OPTS_START_BOOT))
+		{
+			pcla->nMissingOptFlags = _GET_MISSING_OPTS(pcla->nOptFlags, _REQ_OPTS_START_BOOT);
+			return GFA_FU_ERROR_MISSING_COMMAND_OPT;
+		}
+		pcla->nUnusedOptFlags = _GET_UNUSED_OPTS(pcla->nOptFlags, _REQ_OPTS_START_BOOT, _OPT_OPTS_START_BOOT);
+		break;
+	case _OV_HAS_RESET_BOOT:
+		if(!_HAS_REQUIRED_OPTIONS(pcla->nOptFlags, _REQ_OPTS_RESET_BOOT))
+		{
+			pcla->nMissingOptFlags = _GET_MISSING_OPTS(pcla->nOptFlags, _REQ_OPTS_RESET_BOOT);
+			return GFA_FU_ERROR_MISSING_COMMAND_OPT;
+		}
+		pcla->nUnusedOptFlags = _GET_UNUSED_OPTS(pcla->nOptFlags, _REQ_OPTS_RESET_BOOT, _OPT_OPTS_RESET_BOOT);
+		break;
+	case _OV_HAS_RESCUE_BOOT:
+		if(!_HAS_REQUIRED_OPTIONS(pcla->nOptFlags, _REQ_OPTS_RESCUE_BOOT))
+		{
+			pcla->nMissingOptFlags = _GET_MISSING_OPTS(pcla->nOptFlags, _REQ_OPTS_RESCUE_BOOT);
+			return GFA_FU_ERROR_MISSING_COMMAND_OPT;
+		}
+		pcla->nUnusedOptFlags = _GET_UNUSED_OPTS(pcla->nOptFlags, _REQ_OPTS_RESCUE_BOOT, _OPT_OPTS_RESCUE_BOOT);
+		break;
+	default:
+		return 1;
+	}
+
+	/////////////////////////////////////////////////////////////////////////
+	// validate optional options
+
+	if(pcla->nOptFlags & _OV_HAS_MODBUS_SLAVE_ID)
+	{
+		if(!MODBUS_IS_VALID_SLAVE_ID(pcla->nModbusSlvID))
+		{
+			return GFA_FU_ERROR_INVALID_MODBUS_SLV_ID;
+		}
+	}
+
+	return 0;
+}
+
+void GfaTfuCmdOptDumpOptions(LPCMD_LINE_ARGS pcla)
+{
+	if(pcla)
+	{
+		uint32_t nMask, nOpts = 0;
+		TRACE3("****************************\n");
+		TRACE3("COMMAND: %s\n", _GetOptName(pcla->nCmdFlags));
+		TRACE3("OPTIONS:\n");
+
+		switch(pcla->nCmdFlags)
+		{
+		case _OV_HAS_UPLOAD_IMG:
+			nOpts = pcla->nOptFlags & (_REQ_OPTS_UPLOAD_IMG | _OPT_OPTS_UPLOAD_IMG) & ~_OV_HAS_SLAVE_ADDR;
+			break;
+		case _OV_HAS_SHOW_FILE_IMG_INFO:
+			nOpts = pcla->nOptFlags & (_REQ_OPTS_SHOW_IMG_INFO_OFFLINE | _OPT_OPTS_SHOW_IMG_INFO_OFFLINE) & ~_OV_HAS_SLAVE_ADDR;
+			break;
+		case _OV_HAS_SHOW_DEV_IMG_INFO:
+			nOpts = pcla->nOptFlags & (_REQ_OPTS_SHOW_IMG_INFO_ONLINE | _OPT_OPTS_SHOW_IMG_INFO_ONLINE) & ~_OV_HAS_SLAVE_ADDR;
+			break;
+		case _OV_HAS_VALIDATE_IMG:
+			nOpts = pcla->nOptFlags & (_REQ_OPTS_VALIDATE_IMG | _OPT_OPTS_VALIDATE_IMG) & ~_OV_HAS_SLAVE_ADDR;
+			break;
+		case _OV_HAS_SHOW_MAT_SER:
+			nOpts = pcla->nOptFlags & (_REQ_OPTS_SHOW_MAT_SER | _OPT_OPTS_SHOW_MAT_SER) & ~_OV_HAS_SLAVE_ADDR;
+			break;
+		case _OV_HAS_SET_MAT_SER:
+			nOpts = pcla->nOptFlags & (_REQ_OPTS_SET_MAT_SER | _OPT_OPTS_SET_MAT_SER) & ~_OV_HAS_SLAVE_ADDR;
+			break;
+		case _OV_HAS_BOOT_PING:
+			nOpts = pcla->nOptFlags & (_REQ_OPTS_BOOT_PING | _OPT_OPTS_BOOT_PING) & ~_OV_HAS_SLAVE_ADDR;
+			break;
+		case _OV_HAS_START_BOOT:
+			nOpts = pcla->nOptFlags & (_REQ_OPTS_START_BOOT | _OPT_OPTS_START_BOOT) & ~_OV_HAS_SLAVE_ADDR;
+			break;
+		case _OV_HAS_RESET_BOOT:
+			nOpts = pcla->nOptFlags & (_REQ_OPTS_RESET_BOOT | _OPT_OPTS_RESET_BOOT) & ~_OV_HAS_SLAVE_ADDR;
+			break;
+		case _OV_HAS_RESCUE_BOOT:
+			nOpts = pcla->nOptFlags & (_REQ_OPTS_RESCUE_BOOT | _OPT_OPTS_RESCUE_BOOT) & ~_OV_HAS_SLAVE_ADDR;
+			break;
+		default:
+			return;
+		}
+
+		for(nMask = 1; nMask; nMask <<= 1)
+		{
+			if(nOpts & nMask)
+			{
+				TRACE3("  %-*s: %s\n", _MAX_OPT_LENGTH, _GetOptName(nMask), _GetOptValue(nMask, pcla));
+			}
+		}
+		if(pcla->nUnusedOptFlags)
+			TRACE3("  %-*s: %s\n", _MAX_OPT_LENGTH, "IGNORED", GfaTfuCmdOpt2String(pcla->nUnusedOptFlags));
+		TRACE3("****************************\n\n");
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Examples:
+//
+// ./gfativaflashutil --upload-img --itf-name="/dev/ttyO4" -x-baud-rate 230400 -t1 "/opt/GfA/gfativaflashutil/OLS-1V1_0009_crc_9600Baud.bin" -v3 -p -b72
+// ./gfativaflashutil --validate-img --itf-name="/dev/ttyO4" --app-addr=0x2000 --node-addr=0x11 "/opt/GfA/gfativaflashutil/OLS-1V1_0009_crc.bin" -v3
+// ./gfativaflashutil --show-mat-ser --itf-name="/dev/ttyO4" --stat-num=1 --mb-slave-id 100 -v3
+// ./gfativaflashutil --set-mat-ser --itf-name="/dev/ttyO4" --x-baud-rate=115200 --node-addr=0x11 -v3 --material="G.Z.40015 P01" --serial="18-080015 1409"
+// ./gfativaflashutil --show-file-img-info "/opt/GfA/gfativaflashutil/OLS-1V1_0009_crc.bin" -a0x2000 -v3
+// ./gfativaflashutil --show-dev-img-info --itf-name="/dev/ttyO4" --x-baud-rate=115200 --node-addr=0x11 -v3
+// ./gfativaflashutil --ping-target --ping-int=1 --itf-name="/dev/ttyO4" --node-addr=0x11 -v3
+// ./gfativaflashutil --start-boot --itf-name="/dev/ttyO4" --node-addr=0x11 -v3
+// ./gfativaflashutil --reset-boot --itf-name="/dev/ttyO4" --node-addr=0x11 -v3
+// ./gfativaflashutil --mb-start-boot --itf-name="/dev/ttyO4" --mb-slave-id 100 -v3
+//
+//
+
+void GfaTfuCmdOptDisplayHelp(bool bGfaUser)
+{
+	printf("\n╔═══════════════════════════════════════════════════════╗\n");
+	printf("║ gfativaflashutil v%d.%d GfA GmbH (%s %s) ║\n", GFA_FU_VER_MAJOR, GFA_FU_VER_MINOR, __DATE__, __TIME__);
+	printf("╚═══════════════════════════════════════════════════════╝\n\n");
+	printf(" USAGE: gfativaflashutil COMMAND [OPTIONS] [IMG FILE]\n\n");
+
+	printf(" COMMANDS (only one command per execution is permitted):\n");
+	printf("  --%-*s   Upload <IMG FILE> to the device.\n", _MAX_CMD_LENGTH, _OPT_STRING_UPLOAD_IMG);
+	printf("  --%-*s   Validate <IMG FILE> with the device. (Attempts to\n", _MAX_CMD_LENGTH, _OPT_STRING_VALIDATE_IMG);
+	printf("    %-*s   match the material numbers as well).\n", _MAX_CMD_LENGTH, "");
+	printf("  --%-*s   Validate <IMG FILE> offline and display image\n", _MAX_CMD_LENGTH, _OPT_STRING_SHOW_FILE_IMG_INFO);
+	printf("    %-*s   information.\n", _MAX_CMD_LENGTH, "");
+	printf("  --%-*s   Display bootloader and (if available) application\n", _MAX_CMD_LENGTH, _OPT_STRING_SHOW_DEV_IMG_INFO);
+	printf("    %-*s   image information of the target device.\n", _MAX_CMD_LENGTH, "");
+	printf("  --%-*s   Display the material and serial number of the\n", _MAX_CMD_LENGTH, _OPT_STRING_SHOW_MAT_SER);
+	printf("    %-*s   target device.\n", _MAX_CMD_LENGTH, "");
+	
+	if(bGfaUser)
+	{
+		printf("  --%-*s   Set the material and serial number of the\n", _MAX_CMD_LENGTH, _OPT_STRING_SET_MAT_SER);
+		printf("    %-*s   target device.\n", _MAX_CMD_LENGTH, "");
+	}
+	
+	printf("  --%-*s   Ping the target device either once or continuously\n", _MAX_CMD_LENGTH, _OPT_STRING_BOOT_PING);
+	printf("    %-*s   dependig on the parameter --%s.\n", _MAX_CMD_LENGTH, "", _OPT_STRING_PING_INTERVAL);
+	printf("  --%-*s   Start the bootloader if an application is running.\n", _MAX_CMD_LENGTH, _OPT_STRING_START_BOOT);
+	printf("  --%-*s   Reset the bootloader if no application is running.\n", _MAX_CMD_LENGTH, _OPT_STRING_RESET_BOOT);
+	printf("    %-*s   If there is a valid application image in flash, it\n", _MAX_CMD_LENGTH, "");
+	printf("    %-*s   will be subsequently started.\n", _MAX_CMD_LENGTH, "");
+	printf("  --%-*s   Try to re-establish communication with the bootloader\n", _MAX_CMD_LENGTH, _OPT_STRING_RESCUE_BOOT);
+	printf("    %-*s   if it's in a non-responsive state. This may arise\n", _MAX_CMD_LENGTH, "");
+	printf("    %-*s   after a failed image upload due to an interrupted\n", _MAX_CMD_LENGTH, "");
+	printf("    %-*s   communication line or any transmission failures. It\n", _MAX_CMD_LENGTH, "");
+	printf("    %-*s   seems, that a verbosity of 4 in combination with high\n", _MAX_CMD_LENGTH, "");
+	printf("    %-*s   baud rates can also cause this problem.\n", _MAX_CMD_LENGTH, "");
+	printf("\n");
+
+	printf(" OPTIONS:\n");
+	printf("  --%-*s -d <INTERFACE> The name of the communication interface.\n", _MAX_OPT_LENGTH, _OPT_STRING_ITF_NAME);
+	printf("  --%-*s -t <STATION>   Station number. Will be mapped internally\n", _MAX_OPT_LENGTH, _OPT_STRING_STATION_NUMBER);
+	printf("    %-*s                to a node address. Must not be used in\n", _MAX_OPT_LENGTH, "");
+	printf("    %-*s                combination with --%s.\n", _MAX_OPT_LENGTH, "", _OPT_STRING_NODE_ADDR);
+	printf("  --%-*s -n <NODE>      The target's node address. Must not be used\n", _MAX_OPT_LENGTH, _OPT_STRING_NODE_ADDR);
+	printf("    %-*s                in combination with --%s.\n", _MAX_OPT_LENGTH, "", _OPT_STRING_STATION_NUMBER);
+	printf("  --%-*s -x <BAUDRATE>  Extended baud-rate to use for some (but not\n", _MAX_OPT_LENGTH, _OPT_STRING_X_BAUD_RATE);
+	printf("    %-*s                all) commands. Because a baud-rate switch\n", _MAX_OPT_LENGTH, "");
+	printf("    %-*s                involves data transfer as well, it will\n", _MAX_OPT_LENGTH, "");
+	printf("    %-*s                only be used with commands that require a\n", _MAX_OPT_LENGTH, "");
+	printf("    %-*s                great amount of data to be transferred!\n", _MAX_OPT_LENGTH, "");
+	printf("  --%-*s -a <BASE ADDR> Flash address where to load <IMG FILE>.\n", _MAX_OPT_LENGTH, _OPT_STRING_APP_BASE_ADDR);
+	printf("    %-*s                Defaults to 0x%X.\n", _MAX_OPT_LENGTH, "", CMD_OPT_DEFAULT_APP_BASE_ADDRESS);
+
+	if(bGfaUser)
+	{
+		printf("  --%-*s -m <MATERIAL>  The material number to be set with the\n", _MAX_OPT_LENGTH, _OPT_STRING_MATERIAL);
+		printf("    %-*s                --%s command.\n", _MAX_OPT_LENGTH, "", _OPT_STRING_SET_MAT_SER);
+		printf("  --%-*s -s <SERIAL>    The serial number to be set with the\n", _MAX_OPT_LENGTH, _OPT_STRING_SERIAL);
+		printf("    %-*s                --%s command.\n", _MAX_OPT_LENGTH, "", _OPT_STRING_SET_MAT_SER);
+	}
+
+	printf("  --%-*s -b <BLOCKSIZE> Size of an upload-block in bytes. (4-76,\n", _MAX_OPT_LENGTH, _OPT_STRING_BLOCK_SIZE);
+	printf("    %-*s                must be a multiple of 4!). This option is\n", _MAX_OPT_LENGTH, "");
+	printf("    %-*s                only used with the --%s command.\n", _MAX_OPT_LENGTH, "", _OPT_STRING_UPLOAD_IMG);
+	printf("    %-*s                Defaults to %d. There is usually no need\n", _MAX_OPT_LENGTH, "", GFA_BOOTLOADER_DEF_SEND_DATA_BLOCK_SIZE);
+	printf("    %-*s                to alter this value.\n", _MAX_OPT_LENGTH, "");
+	printf("  --%-*s -i <SECONDS>   The optional ping interval in seconds. If a\n", _MAX_OPT_LENGTH, _OPT_STRING_PING_INTERVAL);
+	printf("    %-*s                value greater than 0 is provided, a ping\n", _MAX_OPT_LENGTH, "");
+	printf("    %-*s                will be made every <SECONDS> seconds. If\n", _MAX_OPT_LENGTH, "");
+	printf("    %-*s                the value is 0 (the default), only one ping\n", _MAX_OPT_LENGTH, "");
+	printf("    %-*s                will be executed. Use Ctrl + 'C' to abort a\n", _MAX_OPT_LENGTH, "");
+	printf("    %-*s                continuous ping.\n", _MAX_OPT_LENGTH, "");
+	printf("  --%-*s -p             Don't show block upload progress. This\n", _MAX_OPT_LENGTH, _OPT_STRING_NO_SHOW_PROGRESS);
+	printf("    %-*s                option is especially useful when redirecting\n", _MAX_OPT_LENGTH, "");
+	printf("    %-*s                the program output to a file. No argument.\n", _MAX_OPT_LENGTH, "");
+	printf("  --%-*s -e <MILLISEC>  Timeout in milliseconds for erasing one\n", _MAX_OPT_LENGTH, _OPT_STRING_PAGE_ERASE_TIME);
+	printf("    %-*s                flash page (usually 1 Kb). This value will\n", _MAX_OPT_LENGTH, "");
+	printf("    %-*s                be multiplied with the number of pages to\n", _MAX_OPT_LENGTH, "");
+	printf("    %-*s                be erased. This option is only used with\n", _MAX_OPT_LENGTH, "");
+	printf("    %-*s                the --%s command. Defaults to %d ms.\n", _MAX_OPT_LENGTH, "", _OPT_STRING_UPLOAD_IMG, CMD_OPT_DEFAULT_PAGE_ERASE_TIME);
+	printf("    %-*s                It's usually not necessary to increase this\n", _MAX_OPT_LENGTH, "");
+	printf("    %-*s                value.\n", _MAX_OPT_LENGTH, "");
+	printf("  --%-*s    <SLAVEID>   The address of the modbus slave, if there is\n", _MAX_OPT_LENGTH, _OPT_STRING_MODBUS_SLAVE_ID);
+	printf("    %-*s                a modbus application running on the target.\n", _MAX_OPT_LENGTH, "");
+	printf("  --%-*s    <REGISTER>  Address of the first of the modbus registers\n", _MAX_OPT_LENGTH, _OPT_STRING_MODBUS_CTRL_REG);
+	printf("    %-*s                that control the start of the bootloader.\n", _MAX_OPT_LENGTH, "");
+	printf("    %-*s                Defaults to %d.\n", _MAX_OPT_LENGTH, "", CMD_OPT_DEFAULT_MODBUS_START_REGISTER);
+	printf("  --%-*s -f             Force modbus and mininet auto-bauding to\n", _MAX_OPT_LENGTH, _OPT_STRING_FORCE_ALL_PARITIES);
+	printf("    %-*s                scan for a connection at all possible\n", _MAX_OPT_LENGTH, "");
+	printf("    %-*s                parities. This option has no argument.\n", _MAX_OPT_LENGTH, "");
+	printf("  --%-*s -v <0-4>       Verbosity. 0 = quiet, 1 = error, 2 = info,\n", _MAX_OPT_LENGTH, _OPT_STRING_VERBOSITY);
+	printf("    %-*s                3 = status, 4 = debug. Be careful using a\n", _MAX_OPT_LENGTH, "");
+	printf("    %-*s                verbosity of 4 with commands that can cause\n", _MAX_OPT_LENGTH, "");
+	printf("    %-*s                heavy data transfer like --%s!\n", _MAX_OPT_LENGTH, "", _OPT_STRING_UPLOAD_IMG);
+	printf("  --%-*s -?             Show this help\n", _MAX_OPT_LENGTH, _OPT_STRING_HELP);
+	printf("\n");
+}

+ 13 - 15
cmdopt.h

@@ -39,24 +39,21 @@ typedef enum _OptValues
 	ov_start_boot,
 	ov_reset_boot,
 	ov_rescue_boot,
-	ov_mb_start_boot,
-	ov_material,
-	ov_serial,
-	ov_no_progress,
-//	ov_init_baud_rate,
-//	ov_mb_baud_rate,
-//	ov_mb_parity,
 	ov_mb_slave_id,
 	ov_mb_ctrl_reg,
-	ov_x_baud_rate		= 'x',
-	ov_page_erase_time	= 'e',
+	ov_app_addr			= 'a',
+	ov_block_size		= 'b',
 	ov_itf_name			= 'd',
-	ov_block_size		= 'p',
+	ov_page_erase_time	= 'e',
+	ov_force_all_par	= 'f',
 	ov_ping_interval	= 'i',
-	ov_app_addr			= 'a',
-	ov_station_number	= 's',
+	ov_material			= 'm',
 	ov_node_addr		= 'n',
+	ov_no_progress		= 'p',
+	ov_serial			= 's',
+	ov_station_number	= 't',
 	ov_verbosity		= 'v',
+	ov_x_baud_rate		= 'x',
 	ov_help				= '?'
 }OptValues;
 
@@ -80,13 +77,14 @@ typedef struct _CMD_LINE_ARGS
 	bool bReviveBoot;
 	bool bNeedImgFile;
 	bool bNoProgressBlock;
-	bool bModbusStartBoot;
+	bool bForceAllParities;
 	uint8_t modbParity;
 	int16_t nModbusCtrlReg;
+	int nQuestionMarks;
 	int32_t nPingIntervalSec;
 	uint32_t nBootBaudrate;
 	uint32_t nInitBaudrate;
-	uint32_t nElevBaudrate;
+	uint32_t nExBaudrate;
 	uint32_t nModbBaudrate;
 	uint32_t nBlockSize;
 	uint32_t nStartAddr;
@@ -111,7 +109,7 @@ int			GfaTfuCmdOptParse(int argc, char* argv[], LPCMD_LINE_ARGS pcla);
 int			GfaTfuCmdOptProcess(LPCMD_LINE_ARGS pcla);
 const char*	GfaTfuCmdOpt2String(uint32_t nOpts);
 void		GfaTfuCmdOptDumpOptions(LPCMD_LINE_ARGS pcla);
-void		GfaTfuCmdOptDisplayHelp(void);
+void		GfaTfuCmdOptDisplayHelp(bool bGfaUser);
 
 /////////////////////////////////////////////////////////////////////////////
 #ifdef __cplusplus

+ 2 - 0
error.c

@@ -61,6 +61,8 @@ const char* GfaTfuStrError(int nError)
 		return "Modbus error negative acknowledge";
 	case GFA_MB_ERROR_MEMORY_PARITY_ERROR:
 		return "Modbus parity error";
+	case GFA_FU_ERROR_INVALID_MODBUS_SLV_ID:
+		return "Invalid Modbus Slave-ID";
 	default:
 		return GfaBlmStrError(nError);
 	}

+ 1 - 0
error.h

@@ -29,6 +29,7 @@ extern "C" {
 #define GFA_FU_ERROR_MISSING_COMMAND_OPT		MAKE_GFA_FU_ERROR(12)
 #define GFA_FU_ERROR_INVALID_APP_START_ADDR		MAKE_GFA_FU_ERROR(13)
 #define GFA_FU_ERROR_INVALID_PARITY				MAKE_GFA_FU_ERROR(14)
+#define GFA_FU_ERROR_INVALID_MODBUS_SLV_ID		MAKE_GFA_FU_ERROR(15)
 #define GFA_FU_ERROR_NOTHING_TO_DO				MAKE_GFA_FU_ERROR(20)
 
 #define GFA_FU_ERROR_IMG_HEADER_NOT_FOUND		MAKE_GFA_IMG_ERROR(1)

+ 192 - 261
main.c

@@ -24,10 +24,6 @@
 #include "error.h"
 
 /////////////////////////////////////////////////////////////////////////////
-
-#define _AUTO_BAUD					1
-#define _MODBUS_DETECT				1
-
 /////////////////////////////////////////////////////////////////////////////
 
 #define _ABBR_BOOTLOADER			"Boot"
@@ -80,11 +76,11 @@ static int _ShowDevImgInfo(HIMGFILE hIf, HGFABLM hBlm, LPCCMD_LINE_ARGS pcla, GF
 			bStartApp = true;
 		}
 
-        if(pcla->nElevBaudrate != pcla->nInitBaudrate)
+        if(pcla->nExBaudrate != pcla->nInitBaudrate)
         {
-			TRACE3("Setting baud-rate to %u.\n", pcla->nElevBaudrate);
+			TRACE3("Setting baud-rate to %u.\n", pcla->nExBaudrate);
 
-			if((nRet = GfaBlmBootloaderSetBaudrate(hBlm, pcla->nNodeAddr, pcla->nElevBaudrate)) != 0)
+			if((nRet = GfaBlmBootloaderSetBaudrate(hBlm, pcla->nNodeAddr, pcla->nExBaudrate)) != 0)
 			{
 				TRACE1("Error: %s!\n", GfaTfuStrError(errno));
 				return nRet;
@@ -102,7 +98,7 @@ static int _ShowDevImgInfo(HIMGFILE hIf, HGFABLM hBlm, LPCCMD_LINE_ARGS pcla, GF
 			return nRet;
 	    }
 
-        if(pcla->nElevBaudrate != pcla->nInitBaudrate)
+        if(pcla->nExBaudrate != pcla->nInitBaudrate)
         {
 			TRACE3("Setting baud-rate to %u.\n", pcla->nInitBaudrate);
 
@@ -251,8 +247,9 @@ static int _Ping(HIMGFILE hIf, HGFABLM hBlm, LPCCMD_LINE_ARGS pcla, GFA_BLM_EXEC
 
 	if(ctx == GfaBlmCtx_ModB)
 	{
-		if(!(hMbm = GfaMbMstOpen(hDev)))
+		if(!(hMbm = GfaMbMstOpen(hMst, pcla->nModbBaudrate, pcla->modbParity, pcla->nModbusCtrlReg)))
 			return -1;
+		GfaMbMstSetVerbosity(hMbm, pcla->nVerbosity);
 
 		if(GfaMininetDeviceGetConfigParams(hDev, &scp, sizeof(scp)) != sizeof(scp))
 		{
@@ -393,11 +390,11 @@ static int _UploadImg(HIMGFILE hIf, HGFABLM hBlm, LPCCMD_LINE_ARGS pcla, GFA_BLM
 			}
 		}
 
-        if(pcla->nElevBaudrate != pcla->nBootBaudrate)
+        if(pcla->nExBaudrate != pcla->nBootBaudrate)
         {
-			TRACE3("Setting baud-rate to %u.\n", pcla->nElevBaudrate);
+			TRACE3("Setting baud-rate to %u.\n", pcla->nExBaudrate);
 
-			if((nRet = GfaBlmBootloaderSetBaudrate(hBlm, pcla->nNodeAddr, pcla->nElevBaudrate)) != 0)
+			if((nRet = GfaBlmBootloaderSetBaudrate(hBlm, pcla->nNodeAddr, pcla->nExBaudrate)) != 0)
 			{
 				TRACE1("Error: %s!\n", GfaTfuStrError(errno));
 				return nRet;
@@ -412,7 +409,7 @@ static int _UploadImg(HIMGFILE hIf, HGFABLM hBlm, LPCCMD_LINE_ARGS pcla, GFA_BLM
         {
 	    	if((nRet = GfaBlmBUCmdSendData(hBlm, pcla->nNodeAddr, pImgData, nImgLength, pcla->nBlockSize)) != 0)
 	    	{
-		        if(pcla->nElevBaudrate != pcla->nBootBaudrate)
+		        if(pcla->nExBaudrate != pcla->nBootBaudrate)
 		        {
 					TRACE3("Trying to reset baud-rate to %u.\n", pcla->nBootBaudrate);
 
@@ -427,7 +424,7 @@ static int _UploadImg(HIMGFILE hIf, HGFABLM hBlm, LPCCMD_LINE_ARGS pcla, GFA_BLM
 	    }
 	    else
         {
-	        if(pcla->nElevBaudrate != pcla->nBootBaudrate)
+	        if(pcla->nExBaudrate != pcla->nBootBaudrate)
 	        {
 				TRACE3("Trying to reset baud-rate to %u.\n", pcla->nBootBaudrate);
 
@@ -454,30 +451,6 @@ static int _UploadImg(HIMGFILE hIf, HGFABLM hBlm, LPCCMD_LINE_ARGS pcla, GFA_BLM
 
 /////////////////////////////////////////////////////////////////////////////
 
-static int _StartBootloader(HIMGFILE hIf, HGFABLM hBlm, LPCCMD_LINE_ARGS pcla, GFA_BLM_EXEC_CONTEXT ctx)
-{
-	int nRet;
-	UNUSED(hIf);
-
-	if(ctx == GfaBlmCtx_Boot)
-	{
-		TRACE2("Bootloader already executing!\n");
-		return 0; // no error
-	}
-
-	TRACE3("Starting bootloader.\n");
-
-	if((nRet = GfaBlmBootloaderExecute(hBlm, pcla->nNodeAddr, NULL, 1000)) != 0)
-	{
-		TRACE1("Error: %s!\n", GfaTfuStrError(errno));
-		return nRet;
-	}
-
-	return 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////
-
 static int _ResetBootloader(HIMGFILE hIf, HGFABLM hBlm, LPCCMD_LINE_ARGS pcla, GFA_BLM_EXEC_CONTEXT ctx)
 {
 	int nRet;
@@ -497,25 +470,14 @@ static int _ResetBootloader(HIMGFILE hIf, HGFABLM hBlm, LPCCMD_LINE_ARGS pcla, G
 		return nRet;
     }
 
-#if 0
-	TRACE3("Detecting running image.\n");
-
-    if((ctx = GfaBlmGetExecutionContext(hBlm, pcla->nNodeAddr)) == GfaBlmCtx_Err)
-    {
-		TRACE1("Error: %s!\n", GfaTfuStrError(errno));
-		return nRet;
-    }
-
-    TRACE3("Currently running: %s.\n", (ctx == GfaBlmCtx_App) ? "Application" : "Bootloader");
-#endif
 	return 0;
 }
 
 /////////////////////////////////////////////////////////////////////////////
 
-static int _ReviveBootloader(HIMGFILE hIf, HGFABLM hBlm, LPCCMD_LINE_ARGS pcla, GFA_BLM_EXEC_CONTEXT ctx, bool bSilent)
+static int _ReviveBootloader(HIMGFILE hIf, HGFABLM hBlm, LPCCMD_LINE_ARGS pcla, GFA_BLM_EXEC_CONTEXT ctx, bool bImplicitCall)
 {
-	int nLen, nRet, nCnt = 0;
+	int nLen, nRet = 0, nCnt = 0;
 	uint8_t nStatus;
 	struct timespec ts = {0, 250000000};
 	UNUSED(hIf);
@@ -531,7 +493,7 @@ static int _ReviveBootloader(HIMGFILE hIf, HGFABLM hBlm, LPCCMD_LINE_ARGS pcla,
 	HGFAMINEMST hMst = GfaBlmGetMininetMasterHandle(hBlm);
 	memset(data, 0, sizeof(data));
 
-	if(!bSilent)
+	if(!bImplicitCall)
 		TRACE2("Reviving bootloader.\n");
 
 	while(!g_bSig)
@@ -583,60 +545,31 @@ static int _ReviveBootloader(HIMGFILE hIf, HGFABLM hBlm, LPCCMD_LINE_ARGS pcla,
 static int _ModbusStartBootloader(HIMGFILE hIf, HGFABLM hBlm, LPCCMD_LINE_ARGS pcla, GFA_BLM_EXEC_CONTEXT ctx)
 {
 	HGFAMINEMST hMst	= GfaBlmGetMininetMasterHandle(hBlm);
-	HMINETDEV hDev		= GfaMininetMasterGetDeviceHandle(hMst);
 	UNUSED(hIf);
 	UNUSED(ctx);
 
 	TRACE3("Starting bootloader.\n");
 
-	if(hDev)
+	if(hMst)
 	{
-		HGFAMBMST hMbm = GfaMbMstOpen(hDev);
+		HGFAMBMST hMbm = GfaMbMstOpen(hMst, pcla->nModbBaudrate, pcla->modbParity, pcla->nModbusCtrlReg);
 
 		if(hMbm)
 		{
 			int nRet;
-			GFA_SER_CFG_PARAMS scp, scpSave;
-			struct timeval tvRX = {0, 500000};
-			uint16_t regs[2];
+			GfaMbMstSetVerbosity(hMbm, pcla->nVerbosity);
 
-			if(GfaMininetDeviceGetConfigParams(hDev, &scp, sizeof(scp)) != sizeof(scp))
-				return -1;
-			memcpy(&scpSave, &scp, sizeof(scpSave));
-			scp.parity	= pcla->modbParity;
-			scp.baud	= pcla->nModbBaudrate;
-			if(GfaMininetDeviceSetConfigParams(hDev, &scp, sizeof(scp)) != 0)
-				return -1;
-
-			GfaMininetMasterSaveTimeouts(hMst);
-			GfaMininetMasterSetTimeouts(hMst, &tvRX, NULL);
-
-			TRACE4("Reading modbus registers %hu and %hu.\n", pcla->nModbusCtrlReg, pcla->nModbusCtrlReg + 1);
-
-            if((nRet = GfaMbMstReadHoldingRegisters(hMbm, pcla->nModbusSlvID, pcla->nModbusCtrlReg, 2, regs)) == 0)
-            {
-				GfaMbMstWaitFrameDelay(hMbm);
-
-				TRACE4("Writing modbus registers %hu and %hu - values: 0x%04hX and 0x%04hX\n", pcla->nModbusCtrlReg + 2, pcla->nModbusCtrlReg + 3, regs[0], regs[1]);
-
-	            if((nRet = GfaMbMstWriteMultipleRegisters(hMbm, pcla->nModbusSlvID, pcla->nModbusCtrlReg + 2, 2, regs)) != 0)
-		        {
-					TRACE1("Error: %s!\n", GfaTfuStrError(errno));
-		        }
-
-				usleep(GFA_BOOTLOADER_EXEC_WAIT_TIME * 1000);
+			if((nRet = GfaMbMstBootloaderExecute(hMbm, pcla->nModbusSlvID)) == 0)
+			{
+				if(pcla->nNodeAddr)
+					GfaMininetMasterResetLocalIndex(hMst, pcla->nNodeAddr);
 			}
 			else
 			{
 				TRACE1("Error: %s!\n", GfaTfuStrError(errno));
 			}
 
-			GfaMininetMasterRestoreTimeouts(hMst);
-			if(pcla->nNodeAddr)
-				GfaMininetMasterResetLocalIndex(hMst, pcla->nNodeAddr);
 			GfaMbMstClose(hMbm);
-			if(GfaMininetDeviceSetConfigParams(hDev, &scpSave, sizeof(scpSave)) != 0)
-				return -1;
 			return nRet;
 		}
 	}
@@ -647,86 +580,106 @@ static int _ModbusStartBootloader(HIMGFILE hIf, HGFABLM hBlm, LPCCMD_LINE_ARGS p
 
 /////////////////////////////////////////////////////////////////////////////
 
-#if _AUTO_BAUD
+static int _StartBootloader(HIMGFILE hIf, HGFABLM hBlm, LPCCMD_LINE_ARGS pcla, GFA_BLM_EXEC_CONTEXT ctx)
+{
+	int nRet;
+
+	if(ctx == GfaBlmCtx_Boot)
+	{
+		TRACE2("Bootloader already executing!\n");
+		return 0; // no error
+	}
+	else if(ctx == GfaBlmCtx_ModB)
+	{
+		return _ModbusStartBootloader(hIf, hBlm, pcla, ctx);
+	}
+
+	TRACE3("Starting bootloader.\n");
+
+	if((nRet = GfaBlmBootloaderExecute(hBlm, pcla->nNodeAddr, NULL, 1000)) != 0)
+	{
+		TRACE1("Error: %s!\n", GfaTfuStrError(errno));
+		return nRet;
+	}
+
+	return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
 static bool _AutoBaud(HGFABLM hBlm, LPCMD_LINE_ARGS pcla)
 {
 	const uint32_t nBaudrates[] = {19200, 9600, 38400, 57600, 115200, 230400, 4800, 460800/*, 921600, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400*/};
-	const uint8_t nParities[] = {'N'/*, 'E', 'O'*/};
-	uint32_t i, j;
+	const uint8_t nParities[] = {'N', 'E', 'O'};
+	size_t nCntParities = pcla->bForceAllParities ? _countof(nParities) : 1;
 	HGFAMINEMST hMst	= GfaBlmGetMininetMasterHandle(hBlm);
 	HMINETDEV hDev		= GfaMininetMasterGetDeviceHandle(hMst);
 
 	if(hDev)
 	{
-		HGFAMBMST hMbm;
+		int nRet;
+		size_t i, j;
+		bool bBaudrateHit = false;
+		struct timeval tvRX = {0, 100000};
 		GFA_SER_CFG_PARAMS scp, scpSave;
 
 		if(GfaMininetDeviceGetConfigParams(hDev, &scp, sizeof(scp)) != sizeof(scp))
 			return false;
 		memcpy(&scpSave, &scp, sizeof(scpSave));
 
-		hMbm = GfaMbMstOpen(hDev);
+		GfaMininetMasterSaveTimeouts(hMst);
+		GfaMininetMasterSetTimeouts(hMst, &tvRX, NULL);
 
-		if(hMbm)
+		for(i = 0; i < nCntParities; ++i)
 		{
-			int nRet;
-			bool bHit = false;
-			struct timeval tvRX = {0, 100000};
-
-			GfaMininetMasterSaveTimeouts(hMst);
-			GfaMininetMasterSetTimeouts(hMst, &tvRX, NULL);
+			scp.parity = nParities[i];
 
-			for(i = 0; i < _countof(nParities); ++i)
+			for(j = 0; j < _countof(nBaudrates); ++j)
 			{
-				scp.parity = nParities[i];
+				scp.baud = nBaudrates[j];
+				
+				TRACE4("MiniNet - Try to connect @ %u,8,1,%c\n", scp.baud, scp.parity);
 
-				for(j = 0; j < _countof(nBaudrates); ++j)
+				if(GfaMininetDeviceSetConfigParams(hDev, &scp, sizeof(scp)) != 0)
 				{
-					scp.baud = nBaudrates[j];
-
-					if(GfaMininetDeviceSetConfigParams(hDev, &scp, sizeof(scp)) != 0)
-					{
-						nRet = -1;
-						break;
-					}
-					if((nRet = GfaBlmResetSlaveIndex(hBlm, pcla->nNodeAddr)) == 0)
-	            	{
-	            		pcla->nInitBaudrate = nBaudrates[j];
-	            		if(!pcla->nElevBaudrate)
-	            			pcla->nElevBaudrate = pcla->nInitBaudrate;
-	            		bHit = true;
-	            		TRACE3("Detected connection @ %u,8,1,%c\n", scp.baud, scp.parity);
-	            		break;
-	            	}
-					usleep(10000);
-	            }
-
-	            if(bHit)
-	            	break;
-	        }
+					nRet = -1;
+					break;
+				}
+				if((nRet = GfaBlmResetSlaveIndex(hBlm, pcla->nNodeAddr)) == 0)
+            	{
+            		pcla->nInitBaudrate = nBaudrates[j];
+            		if(!pcla->nExBaudrate)
+            			pcla->nExBaudrate = pcla->nInitBaudrate;
+            		bBaudrateHit = true;
+            		TRACE3("Detected MiniNet connection @ %u,8,1,%c\n", scp.baud, scp.parity);
+            		break;
+            	}
+				usleep(10000);
+            }
+
+            if(bBaudrateHit)
+            	break;
+        }
 
-			GfaMininetMasterRestoreTimeouts(hMst);
-			GfaMbMstClose(hMbm);
-			if(nRet)
-			{
-				GfaMininetDeviceSetConfigParams(hDev, &scpSave, sizeof(scpSave));
-				return false;
-			}
-			return true;
+		GfaMininetMasterRestoreTimeouts(hMst);
+		if(nRet)
+		{
+			GfaMininetDeviceSetConfigParams(hDev, &scpSave, sizeof(scpSave));
+			return false;
 		}
+		return true;
 	}
 
 	return false;
 }
-#endif	//	_AUTO_BAUD
 
 /////////////////////////////////////////////////////////////////////////////
 
-#if _MODBUS_DETECT
-static bool _DetectModbusApplication(HGFABLM hBlm, LPCMD_LINE_ARGS pcla)
+static bool _ModbusAutobaud(HGFABLM hBlm, LPCMD_LINE_ARGS pcla)
 {
 	const uint32_t nBaudrates[] = {19200, 9600, 38400, 57600, 115200, 230400, 4800, 460800/*, 921600, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400*/};
-	const uint8_t nParities[] = {'E', 'O'/*, 'N'*/};
+	const uint8_t nParities[] = {'E', 'O', 'N'};
+	size_t nCntParities = pcla->bForceAllParities ? _countof(nParities) : 2;
 	uint32_t i, j;
 	HGFAMINEMST hMst	= GfaBlmGetMininetMasterHandle(hBlm);
 	HMINETDEV hDev		= GfaMininetMasterGetDeviceHandle(hMst);
@@ -740,19 +693,19 @@ static bool _DetectModbusApplication(HGFABLM hBlm, LPCMD_LINE_ARGS pcla)
 			return false;
 		memcpy(&scpSave, &scp, sizeof(scpSave));
 
-		hMbm = GfaMbMstOpen(hDev);
+		hMbm = GfaMbMstOpen(hMst, pcla->nModbBaudrate, pcla->modbParity, pcla->nModbusCtrlReg);
 
 		if(hMbm)
 		{
 			int nRet;
-			bool bHit = false;
+			bool bBaudrateHit = false;
 			struct timeval tvRX = {0, 200000};
-			uint16_t regs[2];
 
+			GfaMbMstSetVerbosity(hMbm, pcla->nVerbosity);
 			GfaMininetMasterSaveTimeouts(hMst);
 			GfaMininetMasterSetTimeouts(hMst, &tvRX, NULL);
 
-			for(i = 0; i < _countof(nParities); ++i)
+			for(i = 0; i < nCntParities; ++i)
 			{
 				scp.parity = nParities[i];
 
@@ -760,24 +713,26 @@ static bool _DetectModbusApplication(HGFABLM hBlm, LPCMD_LINE_ARGS pcla)
 				{
 					scp.baud = nBaudrates[j];
 
+					TRACE4("Modbus - Try to connect @ %u,8,1,%c\n", scp.baud, scp.parity);
+
 					if(GfaMininetDeviceSetConfigParams(hDev, &scp, sizeof(scp)) != 0)
 					{
 						nRet = -1;
 						break;
 					}
-	            	if((nRet = GfaMbMstReadHoldingRegisters(hMbm, pcla->nModbusSlvID, pcla->nModbusCtrlReg, 2, regs)) == 0)
+					if((nRet = GfaMbMstPing(hMbm, pcla->nModbusSlvID)) == 0)
 	            	{
 						pcla->modbParity = nParities[i];
 	            		pcla->nModbBaudrate = nBaudrates[j];
-	            		TRACE3("Detected modbus application @ %u,8,1,%c\n", pcla->nModbBaudrate, pcla->modbParity);
-	            		bHit = true;
+	            		TRACE3("Detected Modbus application @ %u,8,1,%c\n", pcla->nModbBaudrate, pcla->modbParity);
+	            		bBaudrateHit = true;
 						GfaMbMstWaitFrameDelay(hMbm);
 	            		break;
 	            	}
 					GfaMbMstWaitFrameDelay(hMbm);
 	            }
 
-	            if(bHit)
+	            if(bBaudrateHit)
 	            	break;
 	        }
 
@@ -791,7 +746,6 @@ static bool _DetectModbusApplication(HGFABLM hBlm, LPCMD_LINE_ARGS pcla)
 
 	return false;
 }
-#endif	//	_MODBUS_DETECT
 
 /////////////////////////////////////////////////////////////////////////////
 
@@ -801,6 +755,7 @@ static void _OnUploadProgress(const char *pszFile, int nLine, LPGFA_BLM_DL_PROGR
 	{
 		static struct timespec tsStart, tsEnd;
 		static uint32_t nBlockNr;
+		static int nOldPerc;
 		LPCCMD_LINE_ARGS pcla = (LPCCMD_LINE_ARGS)pdlpp->pParam;
 		int64_t nInterval = 0;
 		char szPercString[8];
@@ -830,18 +785,19 @@ static void _OnUploadProgress(const char *pszFile, int nLine, LPGFA_BLM_DL_PROGR
 			break;
 		case GBDPS_StartUploadBlocks:
 			nBlockNr = 0;
+			nOldPerc = -1;
 			TRACE2("Start sending data to node 0x%02hhX - block size: %u bytes.\n", pdlpp->nNodeAddr, pdlpp->nCbBlock);
 			GfaTfuGetClock(&tsStart);
 			break;
 		case GBDPS_UploadBlock:
 			if(!pcla || !pcla->bNoProgressBlock)
 			{
-				nPerc = GfaTfuGetPercentString(pdlpp->nCbSent, pdlpp->nCbTotal, szPercString, sizeof(szPercString));
-//				if(!(nPerc % 5))
+				if((nPerc = GfaTfuGetPercentString(pdlpp->nCbSent, pdlpp->nCbTotal, szPercString, sizeof(szPercString)))!= nOldPerc)
 				{
 					if((pcla->nVerbosity < 4) && (nBlockNr > 0))
 						TRACE2("\b\b\b\b\b");
 					TRACE2("%s", szPercString);
+					nOldPerc = nPerc;
 				}
 			}
 			++nBlockNr;
@@ -864,34 +820,38 @@ static void _OnUploadProgress(const char *pszFile, int nLine, LPGFA_BLM_DL_PROGR
 int main(int argc, char* argv[])
 {
 	int nRet = 0;
-	HIMGFILE hIf = NULL;
-	HGFABLM hBlm = NULL;
-	CMD_LINE_ARGS cla;
+	HIMGFILE hIf = NULL;	// Handle to the image file
+	HGFABLM hBlm = NULL;	// Handle to the bootloader master
+	CMD_LINE_ARGS cla;		// structure that receives all required options and parameters
 	struct sigaction sa;
 
 	/////////////////////////////////////////////////////////////////////////
 	// process command line
 
-	GfaTfuCmdOptInitOpts(&cla);
-	nRet = GfaTfuCmdOptParse(argc, argv, &cla);
+	GfaTfuCmdOptInitOpts(&cla);				// set options defaults
+	GfaTfuCmdOptParse(argc, argv, &cla);	// parse the command line
 
-	if((nRet = GfaTfuCmdOptProcess(&cla)) != 0)
+	if((nRet = GfaTfuCmdOptProcess(&cla)) != 0)	// validate the options in their context
 	{
 		if(nRet > 0)
 		{
-			GfaTfuCmdOptDisplayHelp();
+			GfaTfuCmdOptDisplayHelp(cla.nQuestionMarks == 3);	// show Help and exit
 			return 0;
 		}
-		else if(nRet == (int)GFA_FU_ERROR_MISSING_COMMAND_OPT)
+		else if(nRet == (int)GFA_FU_ERROR_MISSING_COMMAND_OPT)	// A required option is missing
 			TRACE0("Error: %s: %s!\n", GfaTfuStrError(nRet), GfaTfuCmdOpt2String(cla.nMissingOptFlags));
-		else if(nRet == (int)GFA_FU_ERROR_MULTIPLE_COMMANDS)
+		else if(nRet == (int)GFA_FU_ERROR_MULTIPLE_COMMANDS)	// Multiple commands were given
 			TRACE0("Error: %s: %s!\n", GfaTfuStrError(nRet), GfaTfuCmdOpt2String(cla.nCmdFlags));
 		else
-			TRACE0("Error: %s!\n", GfaTfuStrError(nRet));
+			TRACE0("Error: %s!\n", GfaTfuStrError(nRet));		// Another error occured
 
 		return nRet;
 	}
 
+	// At this point all command line options have been parsed and validated and the cla structure
+	// contains all necessary parameters for the requested command.
+	// Dump an overview of the provided command and the explicitly set options.
+
 	GfaTfuCmdOptDumpOptions(&cla);
 
 	/////////////////////////////////////////////////////////////////////////
@@ -900,37 +860,37 @@ int main(int argc, char* argv[])
 	memset(&sa, 0, sizeof(sa));
 
 	sa.sa_handler = _SigHandler;
-    sigaction(SIGHUP, &sa, NULL);	// handles user's terminal disconnect
-    sigaction(SIGQUIT, &sa, NULL);	// handles Ctrl + '\'
-	sigaction(SIGTERM, &sa, NULL);	// handles normal termination
-	sigaction(SIGABRT, &sa, NULL);	// handles abnormal termination (i.e. abort())
-	sigaction(SIGINT, &sa, NULL);	// handles Ctrl + 'C'
+    sigaction(SIGHUP, &sa, NULL);	// handle user's terminal disconnect
+    sigaction(SIGQUIT, &sa, NULL);	// handle Ctrl + '\'
+	sigaction(SIGTERM, &sa, NULL);	// handle normal termination
+	sigaction(SIGABRT, &sa, NULL);	// handle abnormal termination (i.e. abort())
+	sigaction(SIGINT, &sa, NULL);	// handle Ctrl + 'C'
 
 	sa.sa_handler = SIG_IGN;
-    sigaction(SIGTSTP, &sa, NULL);	// ignores Ctrl + 'Z'
-    sigaction(SIGSTOP, &sa, NULL);	// ignores Stop
-    sigaction(SIGCONT, &sa, NULL);	// ignores Continue
-    sigaction(SIGCHLD, &sa, NULL);	// ignores child process termination
-    sigaction(0, &sa, NULL);		// ignores shell termination
+    sigaction(SIGTSTP, &sa, NULL);	// ignore Ctrl + 'Z'
+    sigaction(SIGSTOP, &sa, NULL);	// ignore Stop
+    sigaction(SIGCONT, &sa, NULL);	// ignore Continue
+    sigaction(SIGCHLD, &sa, NULL);	// ignore child process termination
+    sigaction(0, &sa, NULL);		// ignore shell termination
 
 	/////////////////////////////////////////////////////////////////////////
 
 	do
 	{
-		GFA_BLM_CFG_PARAMS blmcp;
+		GFA_BLM_CFG_PARAMS blmcp;	// bootloader master configuration struct
 		memset(&blmcp, 0, sizeof(blmcp));
 
-		if(cla.bNeedImgFile)
-		{
+		if(cla.bNeedImgFile)		// If the given command requires an image file, open it.
+		{							// This also performs some basic validations on the file.
 			TRACE3("Opening image file %s.\n", cla.pszImgFile);
 
 		    if(!(hIf = GfaTfuImageFileOpen(cla.pszImgFile, cla.nStartAddr, true)))
 		    {
-				TRACE1("Error: %s!\n", GfaTfuStrError(errno));
+				TRACE1("Error: %s!\n", GfaTfuStrError(errno)); // Something is wrong with the file
 				break;
 		    }
 
-		    if(cla.bShowFileImgInfo)
+		    if(cla.bShowFileImgInfo) // If only the file's image information was requested, dump it and exit.
 		    {
 		    	GFA_IMG_INFO ii;
 		    	GfaTfuImageFileGetInfo(hIf, &ii);
@@ -939,34 +899,43 @@ int main(int argc, char* argv[])
 			}
 		}
 
+		// As of this point we require a connection to the target.
+		// In our case we request the serial device interface,
+		// which is in turn used by the abstract mininet device
+
 		if(GfaSerialGetDeviceInterface(&blmcp.mmcp.devcfg.itf))
 		{
-			GFA_SER_CFG_PARAMS scp;
+			GFA_SER_CFG_PARAMS scp;			// serial device configuration parameters
 			memset(&scp, 0, sizeof(scp));
 
 			/////////////////////////////////////////////////////////////////
 			// serial device configuration
 
-			scp.baud			= cla.bModbusStartBoot ? cla.nModbBaudrate : cla.nInitBaudrate; // start with initial baud-rate
-			scp.data			= 8;
-			scp.stop			= 1;
-			scp.parity			= cla.bModbusStartBoot ? cla.modbParity : 'N'; // modbus slaves use parity, mininet slaves not
+			scp.baud			= cla.nInitBaudrate;	// open with default bootloader baud-rate
+			scp.data			= 8;					// always use 8 data bits
+			scp.stop			= 1;					// always use 1 stop bit
+			scp.parity			= 'N';					// open with mininet default
 #ifdef _TARGET_BUILD
-			scp.bHandleTxEcho	= true;
+			scp.bHandleTxEcho	= true;					// On GfA Sitara targets we have to handle transmit echo!
+			scp.bIsRS485		= true;					// Interface is a RS485
 #endif	//	_TARGET_BUILD
 
 			/////////////////////////////////////////////////////////////////
 			// mininet master device configuration
+			// since the mininet master handles the opening and closing of the
+			// mininet device, which is the abstraction layer of our physical
+			// device and in turn handles our serial device, we provide the
+			// appropriate parameters.
 
-			blmcp.mmcp.devcfg.pszDeviceName		= cla.pszDevName;
-			blmcp.mmcp.devcfg.pDevParams		= &scp;
-			blmcp.mmcp.devcfg.nSizeDevParams	= sizeof(scp);
+			blmcp.mmcp.devcfg.pszDeviceName		= cla.pszDevName;	// name of the interface, i.e. "/dev/ttyO4"
+			blmcp.mmcp.devcfg.pDevParams		= &scp;				// pointer to the serial device configuration parameters
+			blmcp.mmcp.devcfg.nSizeDevParams	= sizeof(scp);		// size of the serial device configuration parameters
 
 			/////////////////////////////////////////////////////////////////
 			// bootloader master configuration
 
-			blmcp.pfnDlProgress					= _OnUploadProgress;
-			blmcp.pUserParam					= &cla;
+			blmcp.pfnDlProgress					= _OnUploadProgress;	// progress output function for the image upload
+			blmcp.pUserParam					= &cla;					// will be passed to the progress function
 
 			/////////////////////////////////////////////////////////////////
 			// open a bootloader master instance
@@ -975,102 +944,62 @@ int main(int argc, char* argv[])
 
 			if((hBlm = GfaBlmOpen(&blmcp)))
 			{
+				// At this point the bootloader master, the mininet master and the mininet device
+				// have been opened and initialized successfully
+
 				GFA_BLM_EXEC_CONTEXT ctx;
-#if _MODBUS_DETECT
 				bool bModbusAppDetected = false;
-#endif	//	_MODBUS_DETECT
-
-				GfaBlmSetVerbosity(hBlm, cla.nVerbosity);
-
-				if(cla.bModbusStartBoot)
-				{
-					nRet = _ModbusStartBootloader(hIf, hBlm, &cla, GfaBlmCtx_Err);
-					break;
-				}
 
+				GfaBlmSetVerbosity(hBlm, cla.nVerbosity);	// Set the verbosity of the bootloader master and it's descendants
 				TRACE3("Connecting to target.\n");
 
-#if _AUTO_BAUD
-				if(!_AutoBaud(hBlm, &cla))
+				if(!_AutoBaud(hBlm, &cla)) // Try to detect the mininet baudrate and parity
 				{
-#if _MODBUS_DETECT
-		        	if(!cla.nModbusSlvID || !(bModbusAppDetected = _DetectModbusApplication(hBlm, &cla)))
-#endif	//	_MODBUS_DETECT
+					// If no mininet connection could be detected, try to scan for a modbus connection, given that
+					// a valid modbus slave-id has been provided.
+		        	if(	!MODBUS_IS_VALID_SLAVE_ID(cla.nModbusSlvID) ||
+		        		!(bModbusAppDetected = _ModbusAutobaud(hBlm, &cla)))
 		        	{
 						TRACE1("Error: %s!\n", GfaTfuStrError(errno));
 						break;
 		        	}
 				}
-#else	//	_AUTO_BAUD
-				if((nRet = GfaBlmResetSlaveIndex(hBlm, cla.nNodeAddr)) != 0)
-		        {
-		        	if(errno == ETIMEDOUT)
-		        	{
-		        		if(cla.nBootBaudrate != cla.nInitBaudrate)
-		        		{
-							TRACE3("Failed with baud-rate %u. Retrying with %u.\n", cla.nInitBaudrate, cla.nBootBaudrate);
-							GfaBlmSetDeviceBaudrate(hBlm, cla.nBootBaudrate);
-
-							if((nRet = GfaBlmResetSlaveIndex(hBlm, cla.nNodeAddr)) != 0)
-					        {
-#if _MODBUS_DETECT
-					        	if(!cla.nModbusSlvID || !(bModbusAppDetected = _DetectModbusApplication(hBlm, &cla)))
-#endif	//	_MODBUS_DETECT
-					        	{
-									TRACE1("Error: %s!\n", GfaTfuStrError(errno));
-									break;
-					        	}
-					        }
-					    }
-					    else
-					    {
-#if _MODBUS_DETECT
-				        	if(!cla.nModbusSlvID || !(bModbusAppDetected = _DetectModbusApplication(hBlm, &cla)))
-#endif	//	_MODBUS_DETECT
-				        	{
-								TRACE1("Error: %s!\n", GfaTfuStrError(errno));
-								break;
-				        	}
-					    }
-		        	}
-		        	else
-		        	{
-						TRACE1("Error: %s!\n", GfaTfuStrError(errno));
-						break;
-					}
-		        }
-#endif	//	_AUTO_BAUD
 
-#if _MODBUS_DETECT
 		        if(bModbusAppDetected)
 		        {
+		        	// A modbus application is running on the target
 					ctx = GfaBlmCtx_ModB;
 
-					if(cla.bUploadImg || cla.bShowDevImgInfo || cla.bSetMatSer || cla.bStartBoot)
+					// Most (but not all) of the commands require the bootloader to be started implicitly, especially when a modbus application is running,
+					// which is not capable of any command that even a minintet application could handle
+
+					if(cla.bUploadImg || cla.bShowDevImgInfo || cla.bValidateImg || cla.bShowMatSer || cla.bSetMatSer)
 					{
 						if((nRet = _ModbusStartBootloader(hIf, hBlm, &cla, ctx)) != 0)
 							break;
 
 						ctx = GfaBlmCtx_Boot;
-
-						if(cla.bStartBoot)
-							break;
 					}
-					else if(!cla.bPing)
-					{
-						break;
+					else if(!cla.bPing && !cla.bStartBoot)	// Ping works with a modbus application as well, so there's no need to start the bootloader.
+					{										// --start-boot is an explicit command on it's own and is handled later.
+						TRACE2("Nothing to do.\n");			// At this point either a --reset-boot or --revive-boot command was given. Both commands make no sense when any
+						break;								// kind of application is running, so just exit.
 					}
 		        }
 		        else
-#endif	//	_MODBUS_DETECT
 				{
+		        	// No modbus application was detected, so we are dealing either with a mininet application or the bootloader itself. Find out what ...
 					TRACE3("Detecting running image.\n");
 
 					ctx = GfaBlmGetExecutionContext(hBlm, cla.nNodeAddr);
 
-			        if(	(ctx == GfaBlmCtx_Err) ||
-			        	(ctx == GfaBlmCtx_Boot))
+			        if((ctx == GfaBlmCtx_Err) || (ctx == GfaBlmCtx_Boot))
 			        {
+			        	// Either an error was returned, or the bootloader is running.
+			        	// In the latter case, we don't know yet, if it is in a responsive state, because the detection
+			        	// involved mininet communication only! We force the bootloader into a working state preventively.
+			        	// If the bootloader has already been working correctly, nothing will happen.
+
 			        	if((nRet = _ReviveBootloader(hIf, hBlm, &cla, ctx, true)) != 0)
 			        	{
 							TRACE1("Error: %s!\n", GfaTfuStrError(errno));
@@ -1082,34 +1011,36 @@ int main(int argc, char* argv[])
 
 			        TRACE3("Currently running: %s.\n", (ctx == GfaBlmCtx_App) ? "Application" : "Bootloader");
 			    }
+			    
+			    // handle the commands
 
 				if(cla.bUploadImg)
-					nRet = _UploadImg(hIf, hBlm, &cla, ctx);
-				else if(cla.bShowDevImgInfo)
-					nRet = _ShowDevImgInfo(hIf, hBlm, &cla, ctx);
+					nRet = _UploadImg(hIf, hBlm, &cla, ctx);				// handle --upload-img
 				else if(cla.bValidateImg)
-					nRet = _ValidateImg(hIf, hBlm, &cla, ctx);
+					nRet = _ValidateImg(hIf, hBlm, &cla, ctx);				// handle --validate-img
+				else if(cla.bShowDevImgInfo)
+					nRet = _ShowDevImgInfo(hIf, hBlm, &cla, ctx);			// handle --show-dev-img-info
 				else if(cla.bShowMatSer)
-					nRet = _ShowMatSer(hIf, hBlm, &cla, ctx);
+					nRet = _ShowMatSer(hIf, hBlm, &cla, ctx);				// handle --show-mat-ser
 				else if(cla.bSetMatSer)
-					nRet = _SetMatSer(hIf, hBlm, &cla, ctx);
+					nRet = _SetMatSer(hIf, hBlm, &cla, ctx);				// handle --set-mat-ser
 				else if(cla.bPing)
-					nRet = _Ping(hIf, hBlm, &cla, ctx);
+					nRet = _Ping(hIf, hBlm, &cla, ctx);						// handle --ping-target
 				else if(cla.bStartBoot)
-					nRet = _StartBootloader(hIf, hBlm, &cla, ctx);
+					nRet = _StartBootloader(hIf, hBlm, &cla, ctx);			// handle --start-boot
 				else if(cla.bResetBoot)
-					nRet = _ResetBootloader(hIf, hBlm, &cla, ctx);
+					nRet = _ResetBootloader(hIf, hBlm, &cla, ctx);			// handle --reset-boot
 				else if(cla.bReviveBoot)
-					nRet = _ReviveBootloader(hIf, hBlm, &cla, ctx, false);
+					nRet = _ReviveBootloader(hIf, hBlm, &cla, ctx, false);	// handle --revive-boot
 			}
 			else
-	        {
+	        {	// GfaBlmOpen failed
 				TRACE1("Error: %s!\n", GfaTfuStrError(errno));
 				break;
 	        }
 		}
 		else
-        {
+        {	// GfaSerialGetDeviceInterface failed
 			TRACE1("Error: %s!\n", GfaTfuStrError(errno));
 			break;
         }

+ 203 - 38
modbmst.c

@@ -4,7 +4,10 @@
 #include <stddef.h>
 #include <byteswap.h>
 #include <unistd.h>
+#include <time.h>
 #include <errno.h>
+#include <gfaserial.h>
+#include "output.h"
 #include "error.h"
 #include "modbmst.h"
 
@@ -70,6 +73,32 @@ static const uint8_t g_crcLo[] =
     0x43, 0x83, 0x41, 0x81, 0x80, 0x40
 };
 
+/////////////////////////////////////////////////////////////////////////////
+
+#define _PING_SIGNATURE									0xA55A
+
+/////////////////////////////////////////////////////////////////////////////
+
+typedef struct _GFA_MB_MST
+{
+	HGFAMINEMST hMiNeMst;
+	HMINETDEV hMiNeDev;
+	int nVerbosity;
+	FILE *pDumpCtx;
+	uint32_t nBaudrate;
+	uint16_t nModbusCtrlReg;
+	struct timespec tsStart;
+	uint8_t parity;
+}GFA_MB_MST, *LPGFA_MB_MST;
+typedef const GFA_MB_MST *LPCGFA_MB_MST;
+
+/////////////////////////////////////////////////////////////////////////////
+
+const struct timespec* Ns2Timespec(uint64_t nsTime, struct timespec *pts);
+int64_t TimespecDiff(const struct timespec *pts1, const struct timespec *pts2);
+
+/////////////////////////////////////////////////////////////////////////////
+
 static uint16_t _Crc16(const void *pData, size_t nCbData)
 {
 	const uint8_t *pBuffer = (const uint8_t*)pData;
@@ -89,34 +118,6 @@ static uint16_t _Crc16(const void *pData, size_t nCbData)
 
 /////////////////////////////////////////////////////////////////////////////
 
-typedef struct _GFA_MB_MST
-{
-	HMINETDEV hDev;
-}GFA_MB_MST, *LPGFA_MB_MST;
-typedef const GFA_MB_MST *LPCGFA_MB_MST;
-
-/////////////////////////////////////////////////////////////////////////////
-#if 0
-static uint16_t _Crc16(uint16_t nCrc, const void *pData, size_t nCbData)
-{
-	if(pData && nCbData)
-	{
-	    uint8_t x;
-	    const uint8_t *pbData = (const uint8_t*)pData;
-
-		while(nCbData--)
-		{
-		    x = nCrc >> 8 ^ *pbData++;
-		    x ^= (x >> 4);
-		    nCrc = (nCrc << 8) ^ ((uint16_t)(x << 12)) ^ ((uint16_t)(x <<5)) ^ ((uint16_t)x);
-		}
-	}
-
-	return nCrc;
-}
-#endif
-/////////////////////////////////////////////////////////////////////////////
-
 static void _CpyUnalignedUint16(void *pTo, const void *pFrom, size_t nCntWords)
 {
 	size_t i, j;
@@ -135,13 +136,20 @@ static void _CpyUnalignedUint16(void *pTo, const void *pFrom, size_t nCntWords)
 
 /////////////////////////////////////////////////////////////////////////////
 
-HGFAMBMST GfaMbMstOpen(HMINETDEV hDev)
+HGFAMBMST GfaMbMstOpen(HGFAMINEMST hMiNeMst, uint32_t nBaudrate, uint8_t parity, uint16_t nModbusCtrlReg)
 {
-	if(hDev)
+	if(hMiNeMst)
 	{
 		LPGFA_MB_MST pMst = (LPGFA_MB_MST)malloc(sizeof(GFA_MB_MST));
 		memset(pMst, 0, sizeof(GFA_MB_MST));
-		pMst->hDev = hDev;
+		pMst->hMiNeMst			= hMiNeMst;
+		pMst->hMiNeDev			= GfaMininetMasterGetDeviceHandle(hMiNeMst);
+		pMst->nBaudrate			= nBaudrate;
+		pMst->parity			= parity;
+		pMst->nModbusCtrlReg	= nModbusCtrlReg;
+		pMst->nVerbosity		= 2;
+		pMst->pDumpCtx			= stdout;
+		GfaMininetMasterGetStartClock(hMiNeMst, &pMst->tsStart);
 		return (HGFAMBMST)pMst;
 	}
 
@@ -162,11 +170,29 @@ void GfaMbMstClose(HGFAMBMST hMst)
 
 /////////////////////////////////////////////////////////////////////////////
 
+int GfaMbMstSetVerbosity(HGFAMBMST hMst, int nVerbosity)
+{
+	if(hMst)
+	{
+		LPGFA_MB_MST pMst = (LPGFA_MB_MST)hMst;
+		if(nVerbosity < 0)
+			nVerbosity = 0;
+		else if(nVerbosity > 4)
+			nVerbosity = 4;
+		pMst->nVerbosity = nVerbosity;
+		return 0;
+	}
+
+	errno = EINVAL;
+	return -1;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
 ssize_t GfaMbMstCreateADU(uint8_t slvID, uint8_t func, const void *pData, size_t nCbData, LPMODBUS_RTU_ADU pAdu)
 {
 	if(	pAdu &&
-		(slvID != MODBUS_BROADCAST_ADDRESS) &&
-		(slvID <= MODBUS_MAX_SLAVE_ID))
+		MODBUS_IS_VALID_SLAVE_ID(slvID))
 	{
 		uint16_t nCRC16;
 		size_t nCbAdu = 2;
@@ -239,7 +265,7 @@ void GfaMbMstWaitFrameDelay(HGFAMBMST hMst)
 		uint32_t nDelay, nBaudrate;
 		LPGFA_MB_MST pMst = (LPGFA_MB_MST)hMst;
 
-		if(	!GfaMininetDeviceGetBaudrate(pMst->hDev, &nBaudrate) &&
+		if(	!GfaMininetDeviceGetBaudrate(pMst->hMiNeDev, &nBaudrate) &&
 			nBaudrate <= 19200)
 			nDelay = (350000000 / nBaudrate + 5) / 10;
 		else
@@ -255,7 +281,8 @@ ssize_t GfaMbMstSendADU(HGFAMBMST hMst, LPCMODBUS_RTU_ADU pAdu, size_t nCbAdu)
 	if(hMst && pAdu && nCbAdu >= 4)
 	{
 		LPGFA_MB_MST pMst = (LPGFA_MB_MST)hMst;
-		return GfaMininetDeviceTransmit(pMst->hDev, pAdu, nCbAdu);
+		GfaMbMstDumpADU(hMst, pAdu, nCbAdu, true, NULL);
+		return GfaMininetDeviceTransmit(pMst->hMiNeDev, pAdu, nCbAdu);
 	}
 
 	errno = EINVAL;
@@ -268,8 +295,11 @@ ssize_t GfaMbMstRecvADU(HGFAMBMST hMst, LPMODBUS_RTU_ADU pAdu, size_t nCbAdu)
 {
 	if(hMst && pAdu && nCbAdu >= 4)
 	{
+		ssize_t nRet;
 		LPGFA_MB_MST pMst = (LPGFA_MB_MST)hMst;
-		return GfaMininetDeviceReceive(pMst->hDev, pAdu, nCbAdu);
+		if((nRet = GfaMininetDeviceReceive(pMst->hMiNeDev, pAdu, nCbAdu)) > 0)
+			GfaMbMstDumpADU(hMst, pAdu, nCbAdu, false, NULL);
+		return nRet;
 	}
 
 	errno = EINVAL;
@@ -278,6 +308,57 @@ ssize_t GfaMbMstRecvADU(HGFAMBMST hMst, LPMODBUS_RTU_ADU pAdu, size_t nCbAdu)
 
 /////////////////////////////////////////////////////////////////////////////
 
+void GfaMbMstDumpADU(HGFAMBMST hMst, LPCMODBUS_RTU_ADU pAdu, size_t nCbAdu, bool bTX, const char *pszAnnotation)
+{
+	if(hMst && pAdu && nCbAdu)
+	{
+		LPGFA_MB_MST pMst = (LPGFA_MB_MST)hMst;
+		FILE *pf = pMst->pDumpCtx;
+		
+		if(pf && (pMst->nVerbosity >= 4))
+		{
+			size_t i, nCbData = nCbAdu - 4;
+			uint16_t nCRC16;
+			struct timespec tsCur, tsIntv;
+			clock_gettime(CLOCK_MONOTONIC, &tsCur);
+			uint64_t nInterval = TimespecDiff(&tsCur, &pMst->tsStart);
+			Ns2Timespec(nInterval, &tsIntv);
+
+			if(nCbAdu >= 4)
+			{
+				fprintf(pf, "\n///////////////////////////////////////\n");
+				fprintf(pf, "// Modbus - %ld.%03ld\n", tsIntv.tv_sec, tsIntv.tv_nsec / 1000000);
+				fprintf(pf, "// %s:\n", bTX ? "TX" : "RX");
+				fprintf(pf, "SlvID:  %02hhX (%hhu)\n", pAdu->slvID, pAdu->slvID);
+				fprintf(pf, "Func:   %02hhX (%hhu)\n", pAdu->func, pAdu->func);
+				fprintf(pf, "Data:   ");
+
+				for(i = 0; i < nCbData; i++)
+				{
+					fprintf(pf, "[%02hhX]", (char)pAdu->b[i]);
+				}
+
+				_CpyUnalignedUint16(&nCRC16, &pAdu->b[i], 1);
+				fprintf(pf, "\nCRC16:  %02hX (%hu)\n", nCRC16, nCRC16);
+				if(pszAnnotation)
+				fprintf(pf, "Annot.: %s\n", pszAnnotation);
+			}
+			else
+			{
+				fprintf(pf, "\n///////////////////////////////////////\n");
+				fprintf(pf, "// Modbus - %ld:%03ld\n", tsIntv.tv_sec, tsIntv.tv_nsec / 1000000);
+				fprintf(pf, "// %s:\n", bTX ? "TX" : "RX");
+				fprintf(pf, "Invalid Modbus-Frame!\n");
+			}
+
+			fprintf(pf, "\n");
+			fflush(pf);
+		}
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
 ssize_t GfaMbMstReadHoldingRegisters(HGFAMBMST hMst, uint8_t slvID, uint16_t nRegStart, uint16_t nRegCount, void *pRegs)
 {
 	if(hMst && pRegs && nRegCount > 0)
@@ -309,6 +390,16 @@ ssize_t GfaMbMstReadHoldingRegisters(HGFAMBMST hMst, uint8_t slvID, uint16_t nRe
 			errno = MAKE_GFA_MODBUS_ERROR(nMBErr);
 			return -1;
 		}
+		else if(nRet != (int)(5 + nRegCount * sizeof(uint16_t)))
+		{
+			errno = EPROTO;
+			return -1;
+		}
+		else if(adu.b[0] != (nRegCount * sizeof(uint16_t)))
+		{
+			errno = EPROTO;
+			return -1;
+		}
 
 		_CpyUnalignedUint16(pRegs, &adu.b[1], nRegCount);
 		return 0;
@@ -352,6 +443,16 @@ ssize_t GfaMbMstWriteMultipleRegisters(HGFAMBMST hMst, uint8_t slvID, uint16_t n
 			errno = MAKE_GFA_MODBUS_ERROR(nMBErr);
 			return -1;
 		}
+		else if(nRet != 8)
+		{
+			errno = EPROTO;
+			return -1;
+		}
+		else if((bswap_16(adu.w[0]) != nRegStart) || (bswap_16(adu.w[1]) != nRegCount))
+		{
+			errno = EPROTO;
+			return -1;
+		}
 		
 		return 0;
 	}
@@ -366,11 +467,14 @@ int GfaMbMstPing(HGFAMBMST hMst, uint8_t slvID)
 {
 	if(hMst)
 	{
+		static uint8_t b = 0;
 		int nMBErr;
 		ssize_t nRet;
 		MODBUS_RTU_ADU adu;
 		uint8_t func = MB_FUNC_DIAGNOSTIC;
-		uint16_t data[2] = {MB_SUBFUNC_RETURN_QUERY_DATA, 0x1234};
+		uint16_t nEcho = ++b;
+		nEcho |= (~b << 8);
+		uint16_t data[2] = {bswap_16(MB_SUBFUNC_RETURN_QUERY_DATA), nEcho};
 
 		if((nRet = GfaMbMstCreateADU(slvID, func, &data, sizeof(data), &adu)) < 0)
 			return -1;
@@ -378,6 +482,10 @@ int GfaMbMstPing(HGFAMBMST hMst, uint8_t slvID)
 		if((nRet = GfaMbMstSendADU(hMst, &adu, nRet)) < 0)
 			return -1;
 
+		adu.w[0] = ~MB_SUBFUNC_RETURN_QUERY_DATA;
+		adu.w[1] = ~adu.w[1];
+		adu.w[2] = 0;
+		
 		if((nRet = GfaMbMstRecvADU(hMst, &adu, 8)) <= 0)
 			return -1;
 		
@@ -389,8 +497,65 @@ int GfaMbMstPing(HGFAMBMST hMst, uint8_t slvID)
 			errno = MAKE_GFA_MODBUS_ERROR(nMBErr);
 			return -1;
 		}
+		else if(nRet != 8)
+		{
+			errno = EPROTO;
+			return -1;
+		}
+		else if((adu.w[0] != data[0]) || (adu.w[1] != data[1]))
+		{
+			errno = EPROTO;
+			return -1;
+		}
 		
-		return (adu.w[1] == data[1]) ? 0 : -1;
+		return 0;
+	}
+
+	errno = EINVAL;
+	return -1;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+int GfaMbMstBootloaderExecute(HGFAMBMST hMst, uint8_t slvID)
+{
+	if(hMst)
+	{
+		int nRet, nErr;
+		GFA_SER_CFG_PARAMS scp, scpSave;
+		struct timeval tvRX = {0, 500000};
+		uint16_t regs[2];
+		LPGFA_MB_MST pMst = (LPGFA_MB_MST)hMst;
+
+		if(GfaMininetDeviceGetConfigParams(pMst->hMiNeDev, &scp, sizeof(scp)) != sizeof(scp))
+			return -1;
+		memcpy(&scpSave, &scp, sizeof(scpSave));
+		scp.parity	= pMst->parity;
+		scp.baud	= pMst->nBaudrate;
+		if(GfaMininetDeviceSetConfigParams(pMst->hMiNeDev, &scp, sizeof(scp)) != 0)
+			return -1;
+
+		GfaMininetMasterSaveTimeouts(pMst->hMiNeMst);
+		GfaMininetMasterSetTimeouts(pMst->hMiNeMst, &tvRX, NULL);
+
+		TRACE4("Reading modbus registers %hu and %hu.\n", pMst->nModbusCtrlReg, pMst->nModbusCtrlReg + 1);
+
+        if((nRet = GfaMbMstReadHoldingRegisters(hMst, slvID, pMst->nModbusCtrlReg, 2, regs)) == 0)
+        {
+			GfaMbMstWaitFrameDelay(hMst);
+
+			TRACE4("Writing modbus registers %hu and %hu - values: 0x%04hX and 0x%04hX\n", pMst->nModbusCtrlReg + 2, pMst->nModbusCtrlReg + 3, regs[0], regs[1]);
+
+            if((nRet = GfaMbMstWriteMultipleRegisters(hMst, slvID, pMst->nModbusCtrlReg + 2, 2, regs)) == 0)
+				usleep(GFA_BOOTLOADER_EXEC_WAIT_TIME * 1000);
+		}
+
+		nErr = errno;
+		GfaMininetMasterRestoreTimeouts(pMst->hMiNeMst);
+		if(GfaMininetDeviceSetConfigParams(pMst->hMiNeDev, &scpSave, sizeof(scpSave)) != 0)
+			return -1;
+		errno = nErr;
+		return nRet;
 	}
 
 	errno = EINVAL;

+ 7 - 2
modbmst.h

@@ -4,7 +4,7 @@
 #if !defined(AGD_MODBMST_H__7E9090EE_6B4D_4499_8080_03241FAC527B__INCLUDED_)
 #define AGD_MODBMST_H__7E9090EE_6B4D_4499_8080_03241FAC527B__INCLUDED_
 
-#include <gfamininetdev.h>
+#include <gfamininetmst.h>
 #include <stdint.h>
 #include <stdbool.h>
 
@@ -70,6 +70,8 @@ extern "C" {
 #define MODBUS_MAX_RW_WRITE_REGISTERS					121
 #define MODBUS_MAX_DATA_PAYLOAD							252
 
+#define MODBUS_IS_VALID_SLAVE_ID(id)					(((id) != MODBUS_BROADCAST_ADDRESS) && ((id) <= MODBUS_MAX_SLAVE_ID))
+
 /////////////////////////////////////////////////////////////////////////////
 
 typedef struct _MODBUS_RTU_ADU
@@ -90,7 +92,7 @@ typedef void		*HGFAMBMST;
 
 /////////////////////////////////////////////////////////////////////////////
 
-HGFAMBMST GfaMbMstOpen(HMINETDEV hDev);
+HGFAMBMST GfaMbMstOpen(HGFAMINEMST hMiNeMst, uint32_t nBaudrate, uint8_t parity, uint16_t nModbusCtrlReg);
 void GfaMbMstClose(HGFAMBMST hMst);
 ssize_t GfaMbMstSendADU(HGFAMBMST hMst, LPCMODBUS_RTU_ADU pAdu, size_t nCbAdu);
 ssize_t GfaMbMstRecvADU(HGFAMBMST hMst, LPMODBUS_RTU_ADU pAdu, size_t nCbAdu);
@@ -98,6 +100,9 @@ ssize_t GfaMbMstReadHoldingRegisters(HGFAMBMST hMst, uint8_t slvID, uint16_t nRe
 ssize_t GfaMbMstWriteMultipleRegisters(HGFAMBMST hMst, uint8_t slvID, uint16_t nRegStart, uint16_t nRegCount, const void *pRegs);
 void GfaMbMstWaitFrameDelay(HGFAMBMST hMst);
 int GfaMbMstPing(HGFAMBMST hMst, uint8_t slvID);
+int GfaMbMstBootloaderExecute(HGFAMBMST hMst, uint8_t slvID);
+int GfaMbMstSetVerbosity(HGFAMBMST hMst, int nVerbosity);
+void GfaMbMstDumpADU(HGFAMBMST hMst, LPCMODBUS_RTU_ADU pAdu, size_t nCbAdu, bool bTX, const char *pszAnnotation);
 
 ssize_t GfaMbMstCreateADU(uint8_t slvID, uint8_t func, const void *pData, size_t nCbData, LPMODBUS_RTU_ADU pAdu);
 bool GfaMbMstValidateADU(uint8_t slvID, uint8_t func, LPCMODBUS_RTU_ADU pAdu, size_t nCbAdu, int *pnMbErr);