Skip to content

Commit

Permalink
Merge pull request #544 from arduino/ssu-lzss
Browse files Browse the repository at this point in the history
Add feature for decompressing LZSS compressed binary files by second stage bootloader SSU
  • Loading branch information
aentinger authored Jul 15, 2020
2 parents 761e1e6 + 807f450 commit 6bff0b2
Show file tree
Hide file tree
Showing 4 changed files with 2,537 additions and 2,205 deletions.
78 changes: 54 additions & 24 deletions libraries/SSU/extras/SSUBoot/SSUBoot.ino
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <FlashStorage.h>
#include <MKRGSM.h>

#include "lzss.h"

/**************************************************************************************
DEFINE
Expand All @@ -37,17 +38,16 @@
GLOBAL CONSTANTS
**************************************************************************************/

static constexpr char UPDATE_FILE_NAME[] = "UPDATE.BIN";
static constexpr char CHECK_FILE_NAME[] = "UPDATE.OK";

const char * UPDATE_FILE_NAME = "UPDATE.BIN";
const char * UPDATE_FILE_NAME_LZSS = "UPDATE.BIN.LZSS";
static const char * CHECK_FILE_NAME = "UPDATE.OK";

/**************************************************************************************
GLOBAL VARIABLES
**************************************************************************************/

FlashClass mcu_flash;

GSMFileUtils fileUtils;
GSMFileUtils fileUtils;

/**************************************************************************************
FUNCTION DECLARATION
Expand Down Expand Up @@ -75,29 +75,59 @@ int main()

// Try to update only if update file
// has been download successfully.
if (fileUtils.listFile(CHECK_FILE_NAME) == 1) {
uint32_t size = fileUtils.listFile(UPDATE_FILE_NAME);
size_t cycles = (size / blockSize) + 1;

if (size > SSU_SIZE) {
size -= SSU_SIZE;

/* Erase the MCU flash */
uint32_t flash_address = (uint32_t)SKETCH_START;
mcu_flash.erase((void*)flash_address, size);

for (auto i = 0; i < cycles; i++) {
uint8_t block[blockSize] { 0 };
digitalWrite(LED_BUILTIN, LOW);
uint32_t read = fileUtils.readBlock(UPDATE_FILE_NAME, (i * blockSize) + SSU_SIZE, blockSize, block);
digitalWrite(LED_BUILTIN, HIGH);
mcu_flash.write((void*)flash_address, block, read);
flash_address += read;
}
if (fileUtils.listFile(CHECK_FILE_NAME) > 0)
{
/* This is for LZSS compressed binaries. */
if (fileUtils.listFile(UPDATE_FILE_NAME_LZSS) > 0)
{
/* Erase the complete flash starting from the SSU forward
* because we've got no possibility of knowing how large
* the decompressed binary will finally be.
*/
mcu_flash.erase((void*)SKETCH_START, 0x40000 - (uint32_t)SKETCH_START);
/* Initialize the lzss module with the data which
* it requires.
*/
lzss_init((uint32_t)SKETCH_START);
/* During the process of decoding UPDATE.BIN.LZSS
* is decompressed and stored as UPDATE.BIN.
*/
lzss_decode();
/* Write the data remaining in the write buffer to
* the file.
*/
lzss_flush();
/* Signal a successul update. */
update_success = true;
}
/* This is for uncompressed binaries. */
else if (fileUtils.listFile(UPDATE_FILE_NAME) > 0)
{
uint32_t size = fileUtils.listFile(UPDATE_FILE_NAME);
size_t cycles = (size / blockSize) + 1;

if (size > SSU_SIZE) {
size -= SSU_SIZE;

/* Erase the MCU flash */
uint32_t flash_address = (uint32_t)SKETCH_START;
mcu_flash.erase((void*)flash_address, size);

for (auto i = 0; i < cycles; i++) {
uint8_t block[blockSize] { 0 };
digitalWrite(LED_BUILTIN, LOW);
uint32_t read = fileUtils.readBlock(UPDATE_FILE_NAME, (i * blockSize) + SSU_SIZE, blockSize, block);
digitalWrite(LED_BUILTIN, HIGH);
mcu_flash.write((void*)flash_address, block, read);
flash_address += read;
}
update_success = true;
}
}
/* Clean up in case of success */
if (update_success) {
fileUtils.deleteFile(UPDATE_FILE_NAME);
fileUtils.deleteFile(UPDATE_FILE_NAME_LZSS);
fileUtils.deleteFile(CHECK_FILE_NAME);
}
}
Expand Down
219 changes: 219 additions & 0 deletions libraries/SSU/extras/SSUBoot/lzss.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
/**************************************************************************************
INCLUDE
**************************************************************************************/

#include "lzss.h"

#include <stdlib.h>
#include <stdint.h>

#include <MKRGSM.h>
#include <FlashStorage.h>

/**************************************************************************************
DEFINE
**************************************************************************************/

#define EI 11 /* typically 10..13 */
#define EJ 4 /* typically 4..5 */
#define P 1 /* If match length <= P then output one character */
#define N (1 << EI) /* buffer size */
#define F ((1 << EJ) + 1) /* lookahead buffer size */

#define LZSS_EOF (-1)

#define FPUTC_BUF_SIZE (512)
#define FGETC_BUF_SIZE (512)

/**************************************************************************************
GLOBAL VARIABLES
**************************************************************************************/

extern GSMFileUtils fileUtils;
extern FlashClass mcu_flash;
extern const char * UPDATE_FILE_NAME_LZSS;

static uint32_t SKETCH_START = 0;
static uint32_t LZSS_FILE_SIZE = 0;

int bit_buffer = 0, bit_mask = 128;
unsigned char buffer[N * 2];

static char write_buf[FPUTC_BUF_SIZE];
static size_t write_buf_num_bytes = 0;
static size_t bytes_written_fputc = 0;
static size_t bytes_written_flash = 0;
static uint32_t flash_addr = 0;

/**************************************************************************************
PUBLIC FUNCTIONS
**************************************************************************************/

void lzss_init(uint32_t const sketch_start)
{
SKETCH_START = sketch_start;
flash_addr = sketch_start;
LZSS_FILE_SIZE = fileUtils.listFile(UPDATE_FILE_NAME_LZSS);
}

void lzss_flush()
{
bytes_written_fputc += write_buf_num_bytes;

/* Only write to the flash once we've surpassed
* the SSU in the update binary.
*/
if (bytes_written_fputc > (SKETCH_START - 0x2000))
{
mcu_flash.write((void*)flash_addr, write_buf, write_buf_num_bytes);
flash_addr += write_buf_num_bytes;
}

write_buf_num_bytes = 0;
}

/**************************************************************************************
PRIVATE FUNCTIONS
**************************************************************************************/

void lzss_fputc(int const c)
{
/* Buffer the decompressed data into a buffer so
* we can perform block writes and don't need to
* write every byte singly on the flash (which
* wouldn't be possible anyway).
*/
write_buf[write_buf_num_bytes] = static_cast<char>(c);
write_buf_num_bytes++;

/* The write buffer is full of decompressed
* data, write it to the flash now.
*/
if (write_buf_num_bytes == FPUTC_BUF_SIZE)
lzss_flush();
}

int lzss_fgetc()
{
static uint8_t read_buf[FGETC_BUF_SIZE];
static size_t read_buf_pos = FGETC_BUF_SIZE;
static size_t bytes_read_fgetc = 0;
static size_t bytes_read_from_modem = 0;

/* lzss_file_size is set within SSUBoot:main
* and contains the size of the LZSS file. Once
* all those bytes have been read its time to return
* LZSS_EOF in order to signal that the end of
* the file has been reached.
*/
if (bytes_read_fgetc == LZSS_FILE_SIZE)
return LZSS_EOF;

/* If there is no data left to be read from the read buffer
* than read a new block and store it into the read buffer.
*/
if (read_buf_pos == FGETC_BUF_SIZE)
{
/* Read the next block from the flash memory. */
bytes_read_from_modem += fileUtils.readBlock(UPDATE_FILE_NAME_LZSS, bytes_read_from_modem, FGETC_BUF_SIZE, read_buf);
/* Reset the read buffer position. */
read_buf_pos = 0;
}

uint8_t const c = read_buf[read_buf_pos];
read_buf_pos++;
bytes_read_fgetc++;

return c;
}

/**************************************************************************************
LZSS FUNCTIONS
**************************************************************************************/

void putbit1(void)
{
bit_buffer |= bit_mask;
if ((bit_mask >>= 1) == 0) {
lzss_fputc(bit_buffer);
bit_buffer = 0; bit_mask = 128;
}
}

void putbit0(void)
{
if ((bit_mask >>= 1) == 0) {
lzss_fputc(bit_buffer);
bit_buffer = 0; bit_mask = 128;
}
}

void output1(int c)
{
int mask;

putbit1();
mask = 256;
while (mask >>= 1) {
if (c & mask) putbit1();
else putbit0();
}
}

void output2(int x, int y)
{
int mask;

putbit0();
mask = N;
while (mask >>= 1) {
if (x & mask) putbit1();
else putbit0();
}
mask = (1 << EJ);
while (mask >>= 1) {
if (y & mask) putbit1();
else putbit0();
}
}

int getbit(int n) /* get n bits */
{
int i, x;
static int buf, mask = 0;

x = 0;
for (i = 0; i < n; i++) {
if (mask == 0) {
if ((buf = lzss_fgetc()) == LZSS_EOF) return LZSS_EOF;
mask = 128;
}
x <<= 1;
if (buf & mask) x++;
mask >>= 1;
}
return x;
}

void lzss_decode(void)
{
int i, j, k, r, c;

for (i = 0; i < N - F; i++) buffer[i] = ' ';
r = N - F;
while ((c = getbit(1)) != LZSS_EOF) {
if (c) {
if ((c = getbit(8)) == LZSS_EOF) break;
lzss_fputc(c);
buffer[r++] = c; r &= (N - 1);
} else {
if ((i = getbit(EI)) == LZSS_EOF) break;
if ((j = getbit(EJ)) == LZSS_EOF) break;
for (k = 0; k <= j + 1; k++) {
c = buffer[(i + k) & (N - 1)];
lzss_fputc(c);
buffer[r++] = c; r &= (N - 1);
}
}
}
}
18 changes: 18 additions & 0 deletions libraries/SSU/extras/SSUBoot/lzss.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#ifndef SSU_LZSS_H_
#define SSU_LZSS_H_

/**************************************************************************************
INCLUDE
**************************************************************************************/

#include <stdint.h>

/**************************************************************************************
FUNCTION DEFINITION
**************************************************************************************/

void lzss_init(uint32_t const sketch_start);
void lzss_decode();
void lzss_flush();

#endif /* SSU_LZSS_H_ */
Loading

0 comments on commit 6bff0b2

Please sign in to comment.