joepasquariello / FlasherX

Over-the-air firmware updates for Teensy LC/3.x/4.x/MicroMod
52 stars 14 forks source link

Add a more flexible C++ interface #2

Closed psykokwak-com closed 11 months ago

psykokwak-com commented 1 year ago

Hello, I developped a more flexible interface to fill the buffer and upgrade the system from a pusher (like a webserver). It simply need three members calls. Here is the principle :

Flasher.begin();
File file = FS.open("myfile.hex", FILE_READ);
while(file.available()) {
  char buffer[512];
  size_t r = file.read(buffer, 512);
  if (!Flasher.addChunk(buffer, r)) {
    Flasher.abort();
    break;
  }
}
file.close();
if (Flasher.finish()) {
  // Flash OK
} else {
  // Flash NOK
  Flasher.abort();
}

You can append the "buffer" char by char or chunk by chunk.

Here the implementation :

//******************************************************************************
// FXUTIL.H -- FlasherX utility functions
//******************************************************************************
#ifndef FXUTIL_H_
#define FXUTIL_H_

#include <Arduino.h>
#include <list>

extern "C" {
#include "FlashTxx.h"       // TLC/T3x/T4x/TMM flash primitives
}

//#include "Utils.h"

#define LOGF(...)

typedef struct {
    char buffer[96];
    int bufferlen;
    bool complete;
    uint32_t line;
} flasher_line_t;

typedef struct {
  char *data;                   // pointer to array allocated elsewhere
  unsigned int addr;    // address in intel hex record
  unsigned int code;    // intel hex record type (0=data, etc.)
  unsigned int num;     // number of data bytes in intel hex record

  uint32_t base;            // base address to be added to intel hex 16-bit addr
  uint32_t min;             // min address in hex file
  uint32_t max;             // max address in hex file

  int eof;                      // set true on intel hex EOF (code = 1)
  int lines;                    // number of hex records received  
} hex_info_t;

class FlasherClass {
public:
    FlasherClass();

    bool begin();
    bool addChunk(const char *buffer, int bufferlen);
    bool finish();
    void abort();

protected:
    uint32_t _buffer_addr;
    uint32_t _buffer_size;
    std::list<flasher_line_t> _lines;
  char _hex_data[32] __attribute__((aligned(8)));   // buffer for hex data
    hex_info_t _hex;
};

extern FlasherClass Flasher;

#endif

//******************************************************************************
// FXUTIL.CPP -- FlasherX utility functions
//******************************************************************************
#include <Arduino.h>
#include <stdio.h>      // sscanf(), etc.
#include <string.h>     // strlen(), etc.

#include "FXUtil.h"

//******************************************************************************
// process_hex_record()     process record and return okay (0) or error (1)
//******************************************************************************
int process_hex_record(hex_info_t *hex)
{
  if (hex->code == 0) { // data -- update min/max address so far
    if (hex->base + hex->addr + hex->num > hex->max)
      hex->max = hex->base + hex->addr + hex->num;
    if (hex->base + hex->addr < hex->min)
      hex->min = hex->base + hex->addr;
  }
  else if (hex->code == 1) { // EOF (:flash command not received yet)
    hex->eof = 1;
  }
  else if (hex->code == 2) { // extended segment address (top 16 of 24-bit addr)
    hex->base = ((hex->data[0] << 8) | hex->data[1]) << 4;
  }
  else if (hex->code == 3) { // start segment address (80x86 real mode only)
    return 1;
  }
  else if (hex->code == 4) { // extended linear address (top 16 of 32-bit addr)
    hex->base = ((hex->data[0] << 8) | hex->data[1]) << 16;
  }
  else if (hex->code == 5) { // start linear address (32-bit big endian addr)
    hex->base = (hex->data[0] << 24) | (hex->data[1] << 16)
      | (hex->data[2] << 8) | (hex->data[3] << 0);
  }
  else {
    return 1;
  }

  return 0;
}

//******************************************************************************
// Intel Hex record foramt:
//
// Start code:  one character, ASCII colon ':'.
// Byte count:  two hex digits, number of bytes (hex digit pairs) in data field.
// Address:     four hex digits
// Record type: two hex digits, 00 to 05, defining the meaning of the data field.
// Data:        n bytes of data represented by 2n hex digits.
// Checksum:    two hex digits, computed value used to verify record has no errors.
//
// Examples:
//  :10 9D30 00 711F0000AD38000005390000F5460000 35
//  :04 9D40 00 01480000 D6
//  :00 0000 01 FF
//******************************************************************************

/* Intel HEX read/write functions, Paul Stoffregen, paul@ece.orst.edu */
/* This code is in the public domain.  Please retain my name and */
/* email address in distributed copies, and let me know about any bugs */

/* I, Paul Stoffregen, give no warranty, expressed or implied for */
/* this software and/or documentation provided, including, without */
/* limitation, warranty of merchantability and fitness for a */
/* particular purpose. */

// type modifications by Jon Zeeff

/* parses a line of intel hex code, stores the data in bytes[] */
/* and the beginning address in addr, and returns a 1 if the */
/* line was valid, or a 0 if an error occured.  The variable */
/* num gets the number of bytes that were stored into bytes[] */

int parse_hex_line(const char *theline, char *bytes,
  unsigned int *addr, unsigned int *num, unsigned int *code)
{
  unsigned sum, len, cksum;
  const char *ptr;
  int temp;

  *num = 0;
  if (theline[0] != ':')
    return 0;
  if (strlen(theline) < 11)
    return 0;
  ptr = theline + 1;
  if (!sscanf(ptr, "%02x", &len))
    return 0;
  ptr += 2;
  if (strlen(theline) < (11 + (len * 2)))
    return 0;
  if (!sscanf(ptr, "%04x", (unsigned int *)addr))
    return 0;
  ptr += 4;
  /* Serial.printf("Line: length=%d Addr=%d\n", len, *addr); */
  if (!sscanf(ptr, "%02x", code))
    return 0;
  ptr += 2;
  sum = (len & 255) + ((*addr >> 8) & 255) + (*addr & 255) + (*code & 255);
  while (*num != len)
  {
    if (!sscanf(ptr, "%02x", &temp))
      return 0;
    bytes[*num] = temp;
    ptr += 2;
    sum += bytes[*num] & 255;
    (*num)++;
    if (*num >= 256)
      return 0;
  }
  if (!sscanf(ptr, "%02x", &cksum))
    return 0;

  if (((sum & 255) + (cksum & 255)) & 255)
    return 0;     /* checksum error */
  return 1;
}

FlasherClass::FlasherClass()
  : _buffer_addr(0)
  , _buffer_size(0)
{
}

bool FlasherClass::begin()
{
  _lines.clear();

  _hex.data = _hex_data;
  _hex.addr = 0;
  _hex.num = 0;
  _hex.code = 0;
  _hex.base = 0;
  _hex.min = 0xFFFFFFFF;
  _hex.max = 0;
  _hex.eof = 0;
  _hex.lines = 0;

  if (firmware_buffer_init(&_buffer_addr, &_buffer_size) == 0)
    return false;

  LOGF("FlasherClass::begin() : _buffer_addr=%08X _buffer_size=%u\n", _buffer_addr, _buffer_size);

  return true;
}

bool FlasherClass::addChunk(const char *buffer, int bufferlen)
{
  if (bufferlen == 0)
    return false;

  //LOGF("FlasherClass::addChunk() : ");

  for (int i = 0; i < bufferlen; i++) {

    // If there is no lines in the lines list
    if (_lines.empty()) {
      flasher_line_t l = { "", 0, false, 1 };
      _lines.push_front(l);
      //LOGF("0");
    }

    // If end of line
    if (buffer[i] == '\n' || buffer[i] == '\r') {
      // Create a new line only if the current one is not empty
      if (_lines.front().bufferlen > 0) {
        _lines.front().complete = true;
        flasher_line_t l = { "", 0, false, _lines.front().line + 1 };
        _lines.push_front(l);
        //LOGF("1");
      }
      continue;
    }

    // Append to line
    if (_lines.front().bufferlen < sizeof(flasher_line_t::buffer))
      _lines.front().buffer[_lines.front().bufferlen++] = buffer[i];
  }

  //LOGF("2");

  while (_lines.back().complete) {
    if (parse_hex_line((const char *)_lines.back().buffer, _hex.data, &_hex.addr, &_hex.num, &_hex.code) == 0) {
      LOGF("FlasherClass : parse_hex_line() error\n");
      _lines.pop_back();
      return false;
    }

    //LOGF("3");

    _lines.pop_back();

    if (process_hex_record(&_hex) != 0) {
      LOGF("FlasherClass : invalid hex code %d\n", _hex.code);
      return false;
    }

    if (_hex.code != 0)
      continue;

    //LOGF("4");

    uint32_t addr = _buffer_addr + _hex.base + _hex.addr - FLASH_BASE_ADDR;
    if (_hex.max > (FLASH_BASE_ADDR + _buffer_size)) {
      LOGF("FlasherClass : max address %08lX too large\n", _hex.max);
      return false;
    }

    if (!IN_FLASH(_buffer_addr)) {
      memcpy((void *)addr, (void *)_hex.data, _hex.num);

      //LOGF("5");
    }

    if (IN_FLASH(_buffer_addr)) {
      int error = flash_write_block(addr, _hex.data, _hex.num);
      if (error) {
        LOGF("FlasherClass : error %02X in flash_write_block()\n", error);
        return false;
      }

      //LOGF("6");

    }
    _hex.lines++;
  }

  //LOGF("7 - %d  %d\n", _lines.size(), _lines.front().line);

  return true;
}

bool FlasherClass::finish()
{
  LOGF("FlasherClass::finish()\n");

  // check FSEC value in new code -- abort if incorrect
#if defined(KINETISK) || defined(KINETISL)
  uint32_t value = *(uint32_t *)(0x40C + _buffer_addr);
  if (value == 0xfffff9de) {
    LOGF("new code contains correct FSEC value %08lX\n", value);
  }
  else {
    LOGF("abort - FSEC value %08lX should be FFFFF9DE\n", value);
    return false;
  }
#endif

  // check FLASH_ID in new code - abort if not found
  if (check_flash_id(_buffer_addr, _hex.max - _hex.min)) {
    LOGF("FlasherClass : new code contains correct target ID %s\n", FLASH_ID);
  }
  else {
    LOGF("FlasherClass :  new code missing string %s\n", FLASH_ID);
    return false;
  }

  flash_move(FLASH_BASE_ADDR, _buffer_addr, _hex.max - _hex.min);

  // NEVER RETURN ?

  //firmware_buffer_free(_buffer_addr, _buffer_size);

  return true;
}

void FlasherClass::abort()
{
  firmware_buffer_free(_buffer_addr, _buffer_size);
}

FlasherClass Flasher;
`
joepasquariello commented 11 months ago

Thank you for your suggestions. I need to keep the interface compatible with C for now, but I will come back to this when I do another update.