X-Git-Url: https://git.zerfleddert.de/cgi-bin/gitweb.cgi/hmcfgusb/blobdiff_plain/7ba4ea19644711d8f86c2df3a244da797e23cc96..refs/heads/master:/firmware.c diff --git a/firmware.c b/firmware.c index a52bccf..e1cf34e 100644 --- a/firmware.c +++ b/firmware.c @@ -1,6 +1,6 @@ /* generic firmware-functions for HomeMatic * - * Copyright (c) 2014-16 Michael Gernoth + * Copyright (c) 2014-20 Michael Gernoth * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to @@ -32,18 +32,223 @@ #include #include #include +#include #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 512 +#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 0x10000 + +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);