flash-ota: add support for directly flashing AsksinPP .hex-files
authorMichael Gernoth <michael@gernoth.net>
Mon, 14 Sep 2020 21:29:43 +0000 (23:29 +0200)
committerMichael Gernoth <michael@gernoth.net>
Mon, 14 Sep 2020 21:34:29 +0000 (23:34 +0200)
When flashing AsksinPP .hex-files the Atmel Atmega type needs to be
specified (-3 or -6) for correct memory-layout and flash page sizes.

firmware.c
firmware.h
flash-hmcfgusb.c
flash-hmmoduart.c
flash-ota.c

index 6a544a33bb35db42d330b7f313fb21700d669fbc..36ef43568a18255d13962b1a49f75b424b03a761 100644 (file)
@@ -1,6 +1,6 @@
 /* generic firmware-functions for HomeMatic
  *
- * Copyright (c) 2014-16 Michael Gernoth <michael@gernoth.net>
+ * Copyright (c) 2014-20 Michael Gernoth <michael@gernoth.net>
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <sys/time.h>
+#include <unistd.h>
 
 #include "util.h"
 #include "firmware.h"
 
+#define CRC16_INIT     0xFFFF
+#define CRC16_POLY     0x1021
+
 /* This might be wrong, but it works for current fw */
 #define MAX_BLOCK_LENGTH       2048
 
-struct firmware* firmware_read_firmware(char *filename, int debug)
+#define HEX_BLOCK_LENGTH_328P  128
+#define HEX_BLOCK_LENGTH_644P  256
+#define HEX_IMAGE_SIZE_328P    0x7000
+#define HEX_IMAGE_SIZE_644P    0xF000
+#define HEX_IMAGE_SIZE_MAX     0xFFFF
+
+static uint16_t crc16(uint8_t* buf, int length, uint16_t crc)
+{
+       int i;
+       uint16_t flag;
+
+       while (length--) {
+               for (i = 0; i < 8; i++) {
+                       flag = crc & 0x8000;
+                       crc <<= 1;
+                       if (*buf & 0x80) {
+                               crc |= 1;
+                       }
+                       if (flag) {
+                               crc ^= CRC16_POLY;
+                       }
+                       *buf <<= 1;
+               }
+               buf++;
+       }
+
+       return crc;
+}
+
+static struct firmware* firmware_read_ihex(int fd, struct firmware *fw, int atmega, int debug)
+{
+       uint8_t buf[2*MAX_BLOCK_LENGTH];
+       uint8_t image[HEX_IMAGE_SIZE_MAX];
+       uint16_t len = 0;
+       uint16_t addr = 0;
+       uint16_t type = 0;
+       uint32_t offset = 0;
+       uint32_t image_size = HEX_IMAGE_SIZE_328P;
+       uint32_t block_length = HEX_BLOCK_LENGTH_328P;
+       int r;
+       int i;
+
+       switch (atmega) {
+               case ATMEGA_644P:
+                       printf("Using Atmega644P values for direct hex flashing\n");
+                       image_size = HEX_IMAGE_SIZE_644P;
+                       block_length = HEX_BLOCK_LENGTH_644P;
+                       break;
+               case ATMEGA_328P:
+                       printf("Using Atmega328P values for direct hex flashing\n");
+                       image_size = HEX_IMAGE_SIZE_328P;
+                       block_length = HEX_BLOCK_LENGTH_328P;
+                       break;
+               default:
+                       fprintf(stderr, "Atmega-type (328P/644P) not specified for flashing hex files\n");
+                       exit(EXIT_FAILURE);
+                       break;
+       }
+
+       memset(image, 0xff, sizeof(image));
+
+       while (1) {
+               memset(buf, 0, sizeof(buf));
+               len = 2 /* len */ + 4 /* len */ + 2 /* type */;
+               r = read(fd, buf, len);
+               if (r < 0) {
+                       perror("read");
+                       exit(EXIT_FAILURE);
+               } else if (r == 0) {
+                       fprintf(stderr, "EOF without EOF record, Firmware file not valid!\n");
+                       exit(EXIT_FAILURE);
+               } else if (r != len) {
+                       printf("can't get record information!\n");
+                       exit(EXIT_FAILURE);
+               }
+
+               for (i = 0; i < r; i++) {
+                       if (!validate_nibble(buf[i])) {
+                               fprintf(stderr, "Firmware file not valid!\n");
+                               exit(EXIT_FAILURE);
+                       }
+               }
+
+               len = (ascii_to_nibble(buf[0]) & 0xf)<< 4;
+               len |= ascii_to_nibble(buf[1]) & 0xf;
+
+               addr = (ascii_to_nibble(buf[2]) & 0xf)<< 4;
+               addr |= ascii_to_nibble(buf[3]) & 0xf;
+               addr <<= 8;
+               addr |= (ascii_to_nibble(buf[4]) & 0xf)<< 4;
+               addr |= ascii_to_nibble(buf[5]) & 0xf;
+
+               type = (ascii_to_nibble(buf[6]) & 0xf)<< 4;
+               type |= ascii_to_nibble(buf[7]) & 0xf;
+
+               if (debug)
+                       printf("Length: %d, Address: 0x%04x, Type: 0x%02x\n", len, addr, type);
+
+               if (len > MAX_BLOCK_LENGTH) {
+                       fprintf(stderr, "Invalid block-length %u > %u for block %d!\n", len, MAX_BLOCK_LENGTH, fw->fw_blocks+1);
+                       exit(EXIT_FAILURE);
+               }
+
+               if (type == 0x00) {
+                       r = read(fd, buf, (len * 2) + 2 /* crc */);
+                       if (r < 0) {
+                               perror("read");
+                               exit(EXIT_FAILURE);
+                       } else if (r == 0) {
+                               break;
+                       } else if (r < ((len * 2) + 2)) {
+                               fprintf(stderr, "short read, aborting (%d < %d)\n", r, (len * 2) + 2);
+                               exit(EXIT_FAILURE);
+                       }
+
+                       for (i = 0; i < len * 2; i+=2) {
+                               if ((!validate_nibble(buf[i])) ||
+                                   (!validate_nibble(buf[i+1]))) {
+                                       fprintf(stderr, "Firmware file not valid!\n");
+                                       exit(EXIT_FAILURE);
+                               }
+
+                               image[addr + (i/2)] = (ascii_to_nibble(buf[i]) & 0xf)<< 4;
+                               image[addr + (i/2)] |= ascii_to_nibble(buf[i+1]) & 0xf;
+                       }
+
+                       while (1) {
+                               r = read(fd, buf, 1);
+                               if (r < 0) {
+                                       perror("read");
+                                       exit(EXIT_FAILURE);
+                               } else if (r == 0) {
+                                       break;
+                               } else {
+                                       if (buf[0] == ':') {
+                                               break;
+                                       }
+                               }
+                       }
+               } else if (type == 0x01) {
+                       break;
+               } else {
+                       fprintf(stderr, "Can't handle iHex type 0x%02x\n", type);
+                       exit(EXIT_FAILURE);
+               }
+       }
+
+       image[image_size-2] = 0x00;
+       image[image_size-1] = 0x00;
+
+       while (offset < image_size) {
+               fw->fw = realloc(fw->fw, sizeof(uint8_t*) * (fw->fw_blocks + 1));
+               if (fw->fw == NULL) {
+                       perror("Can't reallocate fw->fw-blocklist");
+                       exit(EXIT_FAILURE);
+               }
+
+               len = block_length;
+
+               fw->fw[fw->fw_blocks] = malloc(len + 4);
+               if (fw->fw[fw->fw_blocks] == NULL) {
+                       perror("Can't allocate memory for fw->fw-block");
+                       exit(EXIT_FAILURE);
+               }
+
+               fw->fw[fw->fw_blocks][0] = (fw->fw_blocks >> 8) & 0xff;
+               fw->fw[fw->fw_blocks][1] = fw->fw_blocks & 0xff;
+               fw->fw[fw->fw_blocks][2] = (len >> 8) & 0xff;
+               fw->fw[fw->fw_blocks][3] = len & 0xff;
+
+               memcpy(fw->fw[fw->fw_blocks] + 4, image + offset, len);
+
+               if ((len + offset) == image_size) {
+                       uint16_t crc;
+
+                       crc = crc16(image, image_size, CRC16_INIT);
+
+                       if (debug)
+                               printf("CRC: %04x\n", crc);
+
+                       fw->fw[fw->fw_blocks][len+3] = (crc >> 8) & 0xff;
+                       fw->fw[fw->fw_blocks][len+2] = crc & 0xff;
+               }
+
+               fw->fw_blocks++;
+               if (debug)
+                       printf("Firmware block %d with length %u read.\n", fw->fw_blocks, len);
+
+               offset += len;
+       }
+
+       if (fw->fw_blocks == 0) {
+               fprintf(stderr, "Firmware file not valid!\n");
+               exit(EXIT_FAILURE);
+       }
+
+       printf("Firmware with %d blocks successfully read.\n", fw->fw_blocks);
+
+       return fw;
+}
+
+struct firmware* firmware_read_firmware(char *filename, int atmega, int debug)
 {
        struct firmware *fw;
        struct stat stat_buf;
-       uint8_t buf[4096];
+       uint8_t buf[2*MAX_BLOCK_LENGTH];
        uint16_t len;
        int fd;
        int r;
@@ -69,6 +274,25 @@ struct firmware* firmware_read_firmware(char *filename, int debug)
        }
 
        printf("Reading firmware from %s...\n", filename);
+
+       memset(buf, 0, sizeof(buf));
+       r = read(fd, buf, 1);
+       if (r != 1) {
+               perror("read");
+               exit(EXIT_FAILURE);
+       }
+
+       //Intel hex?
+       if (buf[0] == ':') {
+               printf("HEX file detected (AsksinPP)\n");
+               return firmware_read_ihex(fd, fw, atmega, debug);
+       }
+
+       if (lseek(fd, 0, SEEK_SET) != 0) {
+               perror("lseek");
+               exit(EXIT_FAILURE);
+       }
+
        do {
                memset(buf, 0, sizeof(buf));
                r = read(fd, buf, 4);
index 5905489c4c309b4623cd63816b3c1c3d6798b2dd..4850b1b94d527345c99f610d0aeaec536daf0246 100644 (file)
@@ -1,6 +1,6 @@
 /* generic firmware-functions for HomeMatic
  *
- * Copyright (c) 2014-16 Michael Gernoth <michael@gernoth.net>
+ * Copyright (c) 2014-20 Michael Gernoth <michael@gernoth.net>
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to
@@ -26,5 +26,11 @@ struct firmware {
        int fw_blocks;
 };
 
-struct firmware* firmware_read_firmware(char *filename, int debug);
+enum atmega_device {
+       ATMEGA_UNKNOWN,
+       ATMEGA_328P,
+       ATMEGA_644P,
+};
+
+struct firmware* firmware_read_firmware(char *filename, int atmega, int debug);
 void firmware_free(struct firmware *fw);
index 8c858f2e2950199684863967f928d23b47663136..78a7a8025a437d13ea19015eef61b1628ca26027 100644 (file)
@@ -1,6 +1,6 @@
 /* flasher for HM-CFG-USB
  *
- * Copyright (c) 2013-16 Michael Gernoth <michael@gernoth.net>
+ * Copyright (c) 2013-20 Michael Gernoth <michael@gernoth.net>
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to
@@ -110,7 +110,7 @@ int main(int argc, char **argv)
                exit(EXIT_FAILURE);
        }
 
-       fw = firmware_read_firmware(filename, debug);
+       fw = firmware_read_firmware(filename, ATMEGA_UNKNOWN, debug);
        if (!fw)
                exit(EXIT_FAILURE);
 
index aa772bb615c558fc3c9fb9db98e5d2ca041637df..60f73f53e747917ee1d420e16ed4b4bb00e0d9de 100644 (file)
@@ -1,6 +1,6 @@
 /* flasher for HM-MOD-UART
  *
- * Copyright (c) 2016-17 Michael Gernoth <michael@gernoth.net>
+ * Copyright (c) 2016-20 Michael Gernoth <michael@gernoth.net>
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to
@@ -117,7 +117,7 @@ int main(int argc, char **argv)
                exit(EXIT_FAILURE);
        }
 
-       fw = firmware_read_firmware(filename, debug);
+       fw = firmware_read_firmware(filename, ATMEGA_UNKNOWN, debug);
        if (!fw)
                exit(EXIT_FAILURE);
 
index 94e93729cb8487c8f033470d5ac903090099bfe1..b7930fca6932e30e1e93efb6a37a5d756d61c204 100644 (file)
@@ -1,6 +1,6 @@
 /* flasher for HomeMatic-devices supporting OTA updates
  *
- * Copyright (c) 2014-17 Michael Gernoth <michael@gernoth.net>
+ * Copyright (c) 2014-20 Michael Gernoth <michael@gernoth.net>
  * Copyright (c) 2017 noansi (TSCULFW-support)
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -618,7 +618,8 @@ void flash_ota_syntax(char *prog)
 {
        fprintf(stderr, "Syntax: %s parameters options\n\n", prog);
        fprintf(stderr, "Mandatory parameters:\n");
-       fprintf(stderr, "\t-f firmware.eq3\tfirmware file to flash\n");
+       fprintf(stderr, "\t-f firmware.eq3\teq3 firmware file to flash\n");
+       fprintf(stderr, "or\t-f firmware.hex\thex firmware file to flash (AsksinPP), needs -3 or -6\n");
        fprintf(stderr, "\t-s SERIAL\tserial of device to flash (optional when using -D)\n");
        fprintf(stderr, "\nOptional parameters:\n");
        fprintf(stderr, "\t-c device\tenable CUL-mode with CUL at path \"device\"\n");
@@ -626,6 +627,8 @@ void flash_ota_syntax(char *prog)
        fprintf(stderr, "\t-l\t\tlower payloadlen (required for devices with little RAM, e.g. CUL v2 and CUL v4)\n");
        fprintf(stderr, "\t-S serial\tuse HM-CFG-USB with given serial\n");
        fprintf(stderr, "\t-U device\tuse HM-MOD-UART on given device\n");
+       fprintf(stderr, "\t-3\t\tuse Atmega328P configuration when directly flashing AsksinPP hex\n");
+       fprintf(stderr, "\t-6\t\tuse Atmega644P configuration when directly flashing AsksinPP hex\n");
        fprintf(stderr, "\t-h\t\tthis help\n");
        fprintf(stderr, "\nOptional parameters for automatically sending device to bootloader\n");
        fprintf(stderr, "\t-C\t\tHMID of central (3 hex-bytes, no prefix, e.g. ABCDEF)\n");
@@ -651,6 +654,7 @@ int main(int argc, char **argv)
        struct firmware *fw;
        char *hmcfgusb_serial = NULL;
        char *uart = NULL;
+       int atmega = ATMEGA_UNKNOWN;
        int block;
        int pfd;
        int debug = 0;
@@ -662,7 +666,7 @@ int main(int argc, char **argv)
 
        printf("HomeMatic OTA flasher version " VERSION "\n\n");
 
-       while((opt = getopt(argc, argv, "b:c:f:hls:C:D:K:S:U:")) != -1) {
+       while((opt = getopt(argc, argv, "b:c:f:hls:C:D:K:S:U:36")) != -1) {
                switch (opt) {
                        case 'b':
                                bps = atoi(optarg);
@@ -722,6 +726,12 @@ int main(int argc, char **argv)
                        case 'U':
                                uart = optarg;
                                break;
+                       case '3':
+                               atmega = ATMEGA_328P;
+                               break;
+                       case '6':
+                               atmega = ATMEGA_644P;
+                               break;
                        case 'h':
                        case ':':
                        case '?':
@@ -738,7 +748,7 @@ int main(int argc, char **argv)
                exit(EXIT_FAILURE);
        }
 
-       fw = firmware_read_firmware(fw_file, debug);
+       fw = firmware_read_firmware(fw_file, atmega, debug);
        if (!fw)
                exit(EXIT_FAILURE);
 
Impressum, Datenschutz