diff options
author | Gabor Juhos <juhosg@openwrt.org> | 2007-08-02 19:05:28 +0000 |
---|---|---|
committer | Gabor Juhos <juhosg@openwrt.org> | 2007-08-02 19:05:28 +0000 |
commit | 20fc312ae90ef7c5623c1e01ef4373f9d7622581 (patch) | |
tree | 1b4c0bb244435c8f6696f8759ac138d846eee6f2 /tools/firmware-utils/src/mkzynfw.c | |
parent | f6b18f60344692439bf1a942365b7b0946dae8c5 (diff) | |
download | upstream-20fc312ae90ef7c5623c1e01ef4373f9d7622581.tar.gz upstream-20fc312ae90ef7c5623c1e01ef4373f9d7622581.tar.bz2 upstream-20fc312ae90ef7c5623c1e01ef4373f9d7622581.zip |
add firmware generation tool for ZyXEL boards
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@8318 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'tools/firmware-utils/src/mkzynfw.c')
-rwxr-xr-x | tools/firmware-utils/src/mkzynfw.c | 998 |
1 files changed, 998 insertions, 0 deletions
diff --git a/tools/firmware-utils/src/mkzynfw.c b/tools/firmware-utils/src/mkzynfw.c new file mode 100755 index 0000000000..8fb3dadb58 --- /dev/null +++ b/tools/firmware-utils/src/mkzynfw.c @@ -0,0 +1,998 @@ +/* + * $Id$ + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos <juhosg@freemail.hu> + * + * This code was based on the information of the ZyXEL's firmware + * image format written by Kolja Waschk, can be found at: + * http://www.ixo.de/info/zyxel_uclinux + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> /* for unlink() */ +#include <libgen.h> +#include <getopt.h> /* for getopt() */ +#include <stdarg.h> +#include <errno.h> +#include <sys/stat.h> +#include <endian.h> /* for __BYTE_ORDER */ +#if defined(__CYGWIN__) +# include <byteswap.h> +#endif + +#include "zynos.h" + +#if (__BYTE_ORDER == __LITTLE_ENDIAN) +# define HOST_TO_LE16(x) (x) +# define HOST_TO_LE32(x) (x) +# define LE16_TO_HOST(x) (x) +# define LE32_TO_HOST(x) (x) +# define HOST_TO_BE16(x) bswap_16(x) +# define HOST_TO_BE32(x) bswap_32(x) +# define BE16_TO_HOST(x) bswap_16(x) +# define BE32_TO_HOST(x) bswap_32(x) +#else +# define HOST_TO_BE16(x) (x) +# define HOST_TO_BE32(x) (x) +# define BE16_TO_HOST(x) (x) +# define BE32_TO_HOST(x) (x) +# define HOST_TO_LE16(x) bswap_16(x) +# define HOST_TO_LE32(x) bswap_32(x) +# define LE16_TO_HOST(x) bswap_16(x) +# define LE32_TO_HOST(x) bswap_32(x) +#endif + +#define ALIGN(x,y) (((x)+((y)-1)) & ~((y)-1)) + +#define MAX_NUM_BLOCKS 8 +#define MAX_ARG_COUNT 32 +#define MAX_ARG_LEN 1024 +#define FILE_BUF_LEN (16*1024) + + +struct csum_state{ + int odd; + uint32_t sum; + uint32_t tmp; +}; + +struct fw_block { + uint32_t align; /* alignment of this block */ + char *file_name; /* name of the file */ + uint32_t file_size; /* length of the file */ + char *mmap_name; /* name in the MMAP table */ + int type; /* block type */ + uint32_t padlen; + uint8_t padc; +}; + +#define BLOCK_TYPE_BOOTEXT 0 +#define BLOCK_TYPE_RAW 1 + +struct fw_mmap { + uint32_t addr; + uint32_t size; + uint32_t user_addr; + uint32_t user_size; +}; +#define MMAP_DATA_SIZE 1024 +#define MMAP_ALIGN 16 + +struct board_info { + char *name; /* model name */ + char *desc; /* description */ + uint16_t model; /* model id */ + uint32_t flash_base; /* flash base address */ + uint32_t flash_size; /* board flash size */ + uint32_t code_start; /* code start address */ + uint32_t fw_offs; /* offset of the firmware within the flash */ +}; + +/* + * Globals + */ +char *progname; +char *ofname = NULL; +int verblevel = 0; + +struct board_info *board = NULL; + +struct fw_block blocks[MAX_NUM_BLOCKS]; +struct fw_block *bootext_block = NULL; +int num_blocks = 0; + +#define ADM5120_FLASH_BASE 0xBFC00000 +#define ADM5120_CODE_START 0x80008000 + +/* TODO: check values for AR7 */ +#define AR7_FLASH_BASE 0xB0000000 +#define AR7_CODE_START 0x94008000 + +#define BOARD(n, d, m, fb, fs, cs, fo) { \ + .name = (n), .desc=(d), .model = (m), \ + .flash_base = (fb), .flash_size = (fs)<<20, \ + .code_start = (cs), .fw_offs = (fo) \ + } + +#define ADMBOARD1(n, d, m, fs) BOARD(n, d, m, \ + ADM5120_FLASH_BASE, fs, ADM5120_CODE_START, 0x8000) + +#define ADMBOARD2(n, d, m, fs) BOARD(n, d, m, \ + ADM5120_FLASH_BASE, fs, ADM5120_CODE_START, 0x10000) + +#define AR7BOARD1(n, d, m, fs) BOARD(n, d, m, \ + AR7_FLASH_BASE, fs, AR7_CODE_START, 0x8000) + +static struct board_info boards[] = { + /* + * Infineon/ADMtek ADM5120 based boards + */ + ADMBOARD2("ES-2024A", "ZyXEL ES-2024A", ZYNOS_MODEL_ES_2024A, 4), + ADMBOARD2("ES-2024PWR", "ZyXEL ES-2024PWR", ZYNOS_MODEL_ES_2024PWR, 4), + ADMBOARD2("ES-2108", "ZyXEL ES-2108", ZYNOS_MODEL_ES_2108, 4), + ADMBOARD2("ES-2108-F", "ZyXEL ES-2108-F", ZYNOS_MODEL_ES_2108_F, 4), + ADMBOARD2("ES-2108-G", "ZyXEL ES-2108-G", ZYNOS_MODEL_ES_2108_G, 4), + ADMBOARD2("ES-2108-LC", "ZyXEL ES-2108-LC", ZYNOS_MODEL_ES_2108_LC, 4), + ADMBOARD2("ES-2108PWR", "ZyXEL ES-2108PWR", ZYNOS_MODEL_ES_2108PWR, 4), + ADMBOARD1("HS-100", "ZyXEL HomeSafe 100", ZYNOS_MODEL_HS_100, 2), + ADMBOARD1("HS-100W", "ZyXEL HomeSafe 100W", ZYNOS_MODEL_HS_100W, 2), + ADMBOARD1("P-334", "ZyXEL Prestige 334", ZYNOS_MODEL_P_334, 2), + ADMBOARD1("P-334U", "ZyXEL Prestige 334U", ZYNOS_MODEL_P_334U, 4), + ADMBOARD1("P-334W", "ZyXEL Prestige 334W", ZYNOS_MODEL_P_334W, 2), + ADMBOARD1("P-334WH", "ZyXEL Prestige 334WH", ZYNOS_MODEL_P_334WH, 4), + ADMBOARD1("P-334WHD", "ZyXEL Prestige 334WHD", ZYNOS_MODEL_P_334WHD, 4), + ADMBOARD1("P-334WT", "ZyXEL Prestige 334WT", ZYNOS_MODEL_P_334WT, 4), + ADMBOARD1("P-335", "ZyXEL Prestige 335", ZYNOS_MODEL_P_335, 4), + ADMBOARD1("P-335Plus", "ZyXEL Prestige 335Plus", ZYNOS_MODEL_P_335PLUS, 4), + ADMBOARD1("P-335U", "ZyXEL Prestige 335U", ZYNOS_MODEL_P_335U, 4), + ADMBOARD1("P-335WT", "ZyXEL Prestige 335WT", ZYNOS_MODEL_P_335WT, 4), +#if 0 + /* + * Texas Instruments AR7 based boards + */ + AR7BOARD1("P-660H-61", "ZyXEL P-660H-61", ZYNOS_MODEL_P_660H_61, 2), + AR7BOARD1("P-660H-63", "ZyXEL P-660H-63", ZYNOS_MODEL_P_660H_63, 2), + AR7BOARD1("P-660H-D1", "ZyXEL P-660H-D1", ZYNOS_MODEL_P_660H_D1, 2), + AR7BOARD1("P-660H-D3", "ZyXEL P-660H-D3", ZYNOS_MODEL_P_660H_D3, 2), + AR7BOARD1("P-660HW-61", "ZyXEL P-660HW-61", ZYNOS_MODEL_P_660HW_61, 2), + AR7BOARD1("P-660HW-63", "ZyXEL P-660HW-63", ZYNOS_MODEL_P_660HW_63, 2), + AR7BOARD1("P-660HW-67", "ZyXEL P-660HW-67", ZYNOS_MODEL_P_660HW_67, 2), + AR7BOARD1("P-660HW-D1", "ZyXEL P-660HW-D1", ZYNOS_MODEL_P_660HW_D1, 2), + AR7BOARD1("P-660HW-D3", "ZyXEL P-660HW-D3", ZYNOS_MODEL_P_660HW_D3, 2), + AR7BOARD1("P-660R-61", "ZyXEL P-660R-61", ZYNOS_MODEL_P_660R_61, 2), + AR7BOARD1("P-660R-61C", "ZyXEL P-660R-61C", ZYNOS_MODEL_P_660R_61C, 2), + AR7BOARD1("P-660R-63", "ZyXEL P-660R-63", ZYNOS_MODEL_P_660R_63, 2), + AR7BOARD1("P-660R-63C", "ZyXEL P-660R-63C", ZYNOS_MODEL_P_660R_63C, 2), + AR7BOARD1("P-660R-67", "ZyXEL P-660R-67", ZYNOS_MODEL_P_660R_67, 2), + AR7BOARD1("P-660R-D1", "ZyXEL P-660R-D1", ZYNOS_MODEL_P_660R_D1, 2), + AR7BOARD1("P-660R-D3", "ZyXEL P-660R-D3", ZYNOS_MODEL_P_660R_D3, 2), +#endif + {.name = NULL} +}; + +/* + * Message macros + */ +#define ERR(fmt, ...) do { \ + fflush(0); \ + fprintf(stderr, "[%s] *** error: " fmt "\n", progname, ## __VA_ARGS__ ); \ +} while (0) + +#define ERRS(fmt, ...) do { \ + int save = errno; \ + fflush(0); \ + fprintf(stderr, "[%s] *** error: " fmt ", %s\n", progname, ## __VA_ARGS__ \ + , strerror(save)); \ +} while (0) + +#define WARN(fmt, ...) do { \ + fprintf(stderr, "[%s] *** warning: " fmt "\n", progname, ## __VA_ARGS__ ); \ +} while (0) + +#define DBG(lev, fmt, ...) do { \ + if (verblevel < lev) \ + break;\ + fprintf(stderr, "[%s] " fmt "\n", progname, ## __VA_ARGS__ ); \ +} while (0) + +#define ERR_FATAL -1 +#define ERR_INVALID_IMAGE -2 + +/* + * Helper routines + */ +void +usage(int status) +{ + FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout; + struct board_info *board; + + fprintf(stream, "Usage: %s [OPTIONS...]\n", progname); + fprintf(stream, +"\n" +"Options:\n" +" -B <board> create image for the board specified with <board>.\n" +" valid <board> values:\n" + ); + for (board = boards; board->name != NULL; board++){ + fprintf(stream, +" %-12s= %s\n", + board->name, board->desc); + }; + fprintf(stream, +" -b <file>[:<align>]\n" +" add boot extension block to the image\n" +" -r <file>[:<align>]\n" +" add raw block to the image\n" +" -o <file> write output to the file <file>\n" +" -h show this screen\n" + ); + + exit(status); +} + + +/* + * argument parsing + */ +int +str2u32(char *arg, uint32_t *val) +{ + char *err = NULL; + uint32_t t; + + errno=0; + t = strtoul(arg, &err, 0); + if (errno || (err==arg) || ((err != NULL) && *err)) { + return -1; + } + + *val = t; + return 0; +} + + +int +str2u16(char *arg, uint16_t *val) +{ + char *err = NULL; + uint32_t t; + + errno=0; + t = strtoul(arg, &err, 0); + if (errno || (err==arg) || ((err != NULL) && *err) || (t >= 0x10000)) { + return -1; + } + + *val = t & 0xFFFF; + return 0; +} + +int +str2u8(char *arg, uint8_t *val) +{ + char *err = NULL; + uint32_t t; + + errno=0; + t = strtoul(arg, &err, 0); + if (errno || (err==arg) || ((err != NULL) && *err) || (t >= 0x100)) { + return -1; + } + + *val = t & 0xFF; + return 0; +} + +int +str2sig(char *arg, uint32_t *sig) +{ + if (strlen(arg) != 4) + return -1; + + *sig = arg[0] | (arg[1] << 8) | (arg[2] << 16) | (arg[3] << 24); + + return 0; +} + + +int +parse_arg(char *arg, char *buf, char *argv[]) +{ + int res = 0; + size_t argl; + char *tok; + char **ap = &buf; + int i; + + memset(argv, 0, MAX_ARG_COUNT * sizeof(void *)); + + if ((arg == NULL)) { + /* no arguments */ + return 0; + } + + argl = strlen(arg); + if (argl == 0) { + /* no arguments */ + return 0; + } + + if (argl >= MAX_ARG_LEN) { + /* argument is too long */ + argl = MAX_ARG_LEN-1; + } + + memcpy(buf, arg, argl); + buf[argl] = '\0'; + + for (i = 0; i < MAX_ARG_COUNT; i++) { + tok = strsep(ap, ":"); + if (tok == NULL) { + break; + } +#if 0 + else if (tok[0] == '\0') { + break; + } +#endif + argv[i] = tok; + res++; + } + + return res; +} + + +int +required_arg(char c, char *arg) +{ + if (arg == NULL || *arg != '-') + return 0; + + ERR("option -%c requires an argument\n", c); + return -1; +} + + +int +is_empty_arg(char *arg) +{ + int ret = 1; + if (arg != NULL) { + if (*arg) ret = 0; + }; + return ret; +} + + +void +csum_init(struct csum_state *css) +{ + css->odd = 0; + css->sum = 0; + css->tmp = 0; +} + + +void +csum_update(uint8_t *p, uint32_t len, struct csum_state *css) +{ + if (len == 0) + return; + + if (css->odd) { + css->sum += (css->tmp << 8) + p[0]; + if (css->sum > 0xFFFF) { + css->sum += 1; + css->sum &= 0xFFFF; + } + css->odd = 0; + len--; + p++; + } + + for ( ; len > 1; len -= 2, p +=2 ) { + css->sum += (p[0] << 8) + p[1]; + if (css->sum > 0xFFFF) { + css->sum += 1; + css->sum &= 0xFFFF; + } + } + + if (len == 1){ + css->tmp = p[0]; + css->odd = 1; + } +} + + +uint16_t +csum_get(struct csum_state *css) +{ + char pad = 0; + + csum_update(&pad, 1, css); + return css->sum; +} + +uint16_t +csum_buf(uint8_t *p, uint32_t len) +{ + struct csum_state css; + + csum_init(&css); + csum_update(p, len, &css); + return csum_get(&css); + +} + +/* + * routines to write data to the output file + */ +int +write_out_data(FILE *outfile, uint8_t *data, size_t len, + struct csum_state *css) +{ + errno = 0; + + fwrite(data, len, 1, outfile); + if (errno) { + ERR("unable to write output file"); + return -1; + } + + if (css) { + csum_update(data, len, css); + } + + return 0; +} + + +int +write_out_padding(FILE *outfile, size_t len, uint8_t padc, + struct csum_state *css) +{ + uint8_t buf[512]; + size_t buflen = sizeof(buf); + + memset(buf, padc, buflen); + while (len > 0) { + if (len < buflen) + buflen = len; + + if (write_out_data(outfile, buf, buflen, css)) + return -1; + + len -= buflen; + } + + return 0; +} + + +int +write_out_data_align(FILE *outfile, uint8_t *data, size_t len, size_t align, + struct csum_state *css) +{ + size_t padlen; + int res; + + res = write_out_data(outfile, data, len, css); + if (res) + return res; + + padlen = ALIGN(len,align) - len; + res = write_out_padding(outfile, padlen, 0xFF, css); + + return res; +} + + +int +write_out_header(FILE *outfile, struct zyn_rombin_hdr *hdr) +{ + struct zyn_rombin_hdr t; + + errno = 0; + if (fseek(outfile, 0, SEEK_SET) != 0) { + ERRS("fseek failed on output file"); + return -1; + } + + /* setup temporary header fields */ + memset(&t,0, sizeof(t)); + t.addr = HOST_TO_BE32(hdr->addr); + memcpy(&t.sig, ROMBIN_SIGNATURE, ROMBIN_SIG_LEN); + t.type = hdr->type; + t.flags = hdr->flags; + t.osize = HOST_TO_BE32(hdr->osize); + t.csize = HOST_TO_BE32(hdr->csize); + t.ocsum = HOST_TO_BE16(hdr->ocsum); + t.ccsum = HOST_TO_BE16(hdr->ccsum); + t.mmap_addr = HOST_TO_BE32(hdr->mmap_addr); + + return write_out_data(outfile, (uint8_t *)&t, sizeof(t), NULL); +} + + +int +write_out_mmap(FILE *outfile, struct fw_mmap *mmap, struct csum_state *css) +{ + struct zyn_mmt_hdr *mh; + uint8_t buf[MMAP_DATA_SIZE]; + uint32_t user_size; + char *data; + int res; + + memset(buf,0,sizeof(buf)); + + mh = (struct zyn_mmt_hdr *)buf; + + /* TODO: needs to recreate the memory map too? */ + mh->count=0; + + /* Build user data section */ + data = buf+sizeof(*mh); + data += sprintf(data,"Model 1 %d", BE16_TO_HOST(board->model)); + *data++ = '\0'; + /* TODO: make hardware version configurable? */ + data += sprintf(data,"HwVerRange 2 %d %d", 0, 0); + *data++ = '\0'; + + user_size = (uint8_t *)data - buf; + mh->user_start= HOST_TO_BE32(mmap->addr+sizeof(*mh)); + mh->user_end= HOST_TO_BE32(mmap->addr+user_size); + mh->csum = HOST_TO_BE16(csum_buf(buf+sizeof(*mh), user_size)); + + res = write_out_data(outfile, buf, sizeof(buf), css); + + return res; +} + + +int +block_stat_file(struct fw_block *block) +{ + struct stat st; + int res; + + if (block->file_name == NULL) + return 0; + + res = stat(block->file_name, &st); + if (res){ + ERRS("stat failed on %s", block->file_name); + return res; + } + + block->file_size = st.st_size; + return 0; +} + + +int +write_out_file(FILE *outfile, char *name, size_t len, struct csum_state *css) +{ + char buf[FILE_BUF_LEN]; + size_t buflen = sizeof(buf); + FILE *f; + int res; + + DBG(2, "writing out file, name=%s, len=%d", + name, len); + + errno = 0; + f = fopen(name,"r"); + if (errno) { + ERRS("unable to open file: %s", name); + return -1; + } + + while (len > 0) { + if (len < buflen) + buflen = len; + + /* read data from source file */ + errno = 0; + fread(buf, buflen, 1, f); + if (errno != 0) { + ERRS("unable to read from file: %s",name); + res = -1; + break; + } + + res = write_out_data(outfile, buf, buflen, css); + if (res) + break; + + len -= buflen; + } + + fclose(f); + return res; +} + + +int +write_out_block(FILE *outfile, struct fw_block *block, struct csum_state *css) +{ + int res; + + if (block == NULL) + return 0; + + if (block->file_name == NULL) + return 0; + + if (block->file_size == 0) + return 0; + + res = write_out_file(outfile, block->file_name, + block->file_size, css); + return res; +} + + +int +write_out_image(FILE *outfile) +{ + struct fw_block *block; + struct fw_mmap mmap; + struct zyn_rombin_hdr hdr; + struct csum_state css; + int i, res; + uint32_t offset; + uint32_t padlen; + + /* setup header fields */ + hdr.addr = board->code_start; + hdr.type = OBJECT_TYPE_BOOTEXT; + hdr.flags = ROMBIN_FLAG_40; + + res = write_out_header(outfile, &hdr); + if (res) + return res; + + offset = sizeof(hdr); + + csum_init(&css); + res = write_out_block(outfile, bootext_block, &css); + if (res) + return res; + + offset += bootext_block->file_size; + + padlen = ALIGN(offset,MMAP_ALIGN) - offset; + res = write_out_padding(outfile, padlen, 0xFF, &css); + if (res) + return res; + + offset += padlen; + + mmap.addr = board->flash_base + board->fw_offs + offset; + hdr.mmap_addr = mmap.addr; + res = write_out_mmap(outfile, &mmap, &css); + if (res) + return res; + + offset += MMAP_DATA_SIZE; + hdr.osize = offset - sizeof(hdr); + hdr.ocsum = csum_get(&css); + + for (i=0; i < num_blocks; i++) { + block = &blocks[i]; + + if (block->type == BLOCK_TYPE_BOOTEXT) + continue; + + padlen = ALIGN(offset,block->align) - offset; + res = write_out_padding(outfile, padlen, 0xFF, NULL); + if (res) + return res; + offset += padlen; + + res = write_out_block(outfile, block, NULL); + if (res) + return res; + offset += block->file_size; + } + + res = write_out_header(outfile, &hdr); + + return res; +} + + +struct board_info * +find_board(char *name) +{ + struct board_info *ret; + struct board_info *board; + + ret = NULL; + for (board = boards; board->name != NULL; board++){ + if (strcmp(name, board->name) == 0) { + ret = board; + break; + } + }; + + return ret; +} + + +int +parse_opt_board(char ch, char *arg) +{ + + DBG(1,"parsing board option: -%c %s", ch, arg); + + if (board != NULL) { + ERR("only one board option allowed"); + return -1; + } + + if (required_arg(ch, arg)) + return -1; + + board = find_board(arg); + if (board == NULL){ + ERR("invalid/unknown board specified: %s", arg); + return -1; + } + + return 0; +} + + +int +parse_opt_ofname(char ch, char *arg) +{ + + if (ofname != NULL) { + ERR("only one output file allowed"); + return -1; + } + + if (required_arg(ch, arg)) + return -1; + + ofname = arg; + + return 0; +} + + +int +parse_opt_block(char ch, char *arg) +{ + char buf[MAX_ARG_LEN]; + char *argv[MAX_ARG_COUNT]; + int argc; + char *p; + struct fw_block *block; + int i; + + if ( num_blocks > MAX_NUM_BLOCKS ) { + ERR("too many blocks specified"); + return -1; + } + + block = &blocks[num_blocks++]; + + /* setup default field values */ + block->padc = 0xFF; + + switch(ch) { + case 'b': + if (bootext_block) { + ERR("only one boot extension block allowed"); + break; + } + block->type = BLOCK_TYPE_BOOTEXT; + bootext_block = block; + break; + case 'r': + block->type = BLOCK_TYPE_RAW; + break; + } + + argc = parse_arg(arg, buf, argv); + + i = 0; + p = argv[i++]; + if (is_empty_arg(p)) { + ERR("no file specified in %s", arg); + return -1; + } else { + block->file_name = strdup(p); + if (block->file_name == NULL) { + ERR("not enough memory"); + return -1; + } + } + + if(block->type == BLOCK_TYPE_BOOTEXT) + return 0; + + p = argv[i++]; + if (!is_empty_arg(p)) { + if (str2u32(p, &block->align) != 0) { + ERR("invalid block align in %s", arg); + return -1; + } + } + + return 0; +} + + +int +calc_block_offsets(int type, uint32_t *offset) +{ + struct fw_block *block; + uint32_t next_offs; + uint32_t avail; + int i, res; + + DBG(1,"calculating block offsets, starting with %lu", + *offset); + + res = 0; + for (i = 0; i < num_blocks; i++) { + block = &blocks[i]; + + if (block->type != type) + continue; + + next_offs = ALIGN(*offset, block->align); + avail = board->flash_size - board->fw_offs - next_offs; + if (next_offs + block->file_size > avail) { + ERR("file %s is too big, offset = %u, size=%u," + " align = %u", block->file_name, + (unsigned)next_offs, + (unsigned)block->file_size, + (unsigned)block->align); + res = -1; + break; + } + + block->padlen = next_offs - *offset; + *offset += block->file_size; + } + + return res; +} + +int +process_blocks(void) +{ + struct fw_block *block; + uint32_t offset; + int i; + int res; + + /* collecting file stats */ + for (i = 0; i < num_blocks; i++) { + block = &blocks[i]; + res = block_stat_file(block); + if (res) + return res; + } + + offset = board->fw_offs + bootext_block->file_size; + res = calc_block_offsets(BLOCK_TYPE_RAW, &offset); + + return res; +} + + +int +main(int argc, char *argv[]) +{ + int optinvalid = 0; /* flag for invalid option */ + int c; + int res = EXIT_FAILURE; + + FILE *outfile; + + progname=basename(argv[0]); + + opterr = 0; /* could not print standard getopt error messages */ + while ( 1 ) { + optinvalid = 0; + + c = getopt(argc, argv, "b:B:ho:r:v"); + if (c == -1) + break; + + switch (c) { + case 'b': + case 'r': + optinvalid = parse_opt_block(c,optarg); + break; + case 'B': + optinvalid = parse_opt_board(c,optarg); + break; + case 'o': + optinvalid = parse_opt_ofname(c,optarg); + break; + case 'v': + verblevel++; + break; + case 'h': + usage(EXIT_SUCCESS); + break; + default: + optinvalid = 1; + break; + } + if (optinvalid != 0 ) { + ERR("invalid option: -%c", optopt); + goto out; + } + } + + if (board == NULL) { + ERR("no board specified"); + goto out; + } + + if (ofname == NULL) { + ERR("no output file specified"); + goto out; + } + + if (optind < argc) { + ERR("invalid option: %s", argv[optind]); + goto out; + } + + + if (process_blocks() != 0) { + goto out; + } + + outfile = fopen(ofname, "w"); + if (outfile == NULL) { + ERRS("could not open \"%s\" for writing", ofname); + goto out; + } + + if (write_out_image(outfile) != 0) + goto out_flush; + + DBG(1,"Image file %s completed.", ofname); + + res = EXIT_SUCCESS; + +out_flush: + fflush(outfile); + fclose(outfile); + if (res != EXIT_SUCCESS) { + unlink(ofname); + } +out: + return res; +} |