1/*
2 *
3 *  Copyright (C) 2007-2009 Gabor Juhos <juhosg@openwrt.org>
4 *
5 *  This program was based on the code found in various Linux
6 *  source tarballs released by Edimax for it's devices.
7 *  Original author: David Hsu <davidhsu@realtek.com.tw>
8 *
9 *  This program is free software; you can redistribute it and/or
10 *  modify it under the terms of the GNU General Public License
11 *  as published by the Free Software Foundation; either version 2
12 *  of the License, or (at your option) any later version.
13 *
14 *  This program is distributed in the hope that it will be useful,
15 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 *  GNU General Public License for more details.
18 *
19 *  You should have received a copy of the GNU General Public License
20 *  along with this program; if not, write to the
21 *  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 *  Boston, MA  02110-1301, USA.
23 */
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <stdint.h>
28#include <string.h>
29#include <unistd.h>     /* for unlink() */
30#include <libgen.h>
31#include <getopt.h>     /* for getopt() */
32#include <stdarg.h>
33#include <errno.h>
34#include <sys/stat.h>
35#include <endian.h>     /* for __BYTE_ORDER */
36#if defined(__CYGWIN__)
37#  include <byteswap.h>
38#endif
39
40#include "csysimg.h"
41
42#if (__BYTE_ORDER == __LITTLE_ENDIAN)
43#  define HOST_TO_LE16(x)	(x)
44#  define HOST_TO_LE32(x)	(x)
45#  define LE16_TO_HOST(x)	(x)
46#  define LE32_TO_HOST(x)	(x)
47#else
48#  define HOST_TO_LE16(x)	bswap_16(x)
49#  define HOST_TO_LE32(x)	bswap_32(x)
50#  define LE16_TO_HOST(x)	bswap_16(x)
51#  define LE32_TO_HOST(x)	bswap_32(x)
52#endif
53
54#define MAX_NUM_BLOCKS	8
55#define MAX_ARG_COUNT	32
56#define MAX_ARG_LEN	1024
57#define FILE_BUF_LEN	(16*1024)
58#define CSYS_PADC	0xFF
59
60#define BLOCK_TYPE_BOOT	0
61#define BLOCK_TYPE_CONF	1
62#define BLOCK_TYPE_WEBP	2
63#define BLOCK_TYPE_CODE	3
64#define BLOCK_TYPE_XTRA	4
65
66#define DEFAULT_BLOCK_ALIGN	0x10000U
67
68#define CSUM_SIZE_NONE	0
69#define CSUM_SIZE_8	1
70#define CSUM_SIZE_16	2
71
72
73struct csum_state{
74	int	size;
75	uint16_t val;
76	uint16_t tmp;
77	int	odd;
78};
79
80
81struct csys_block {
82	int		type;	/* type of the block */
83
84	int		need_file;
85	char		*file_name;	/* name of the file */
86	uint32_t	file_size;	/* length of the file */
87
88	unsigned char	sig[SIG_LEN];
89	uint32_t	addr;
90	int		addr_set;
91	uint32_t	align;
92	int		align_set;
93	uint8_t		padc;
94
95	uint32_t	size;
96	uint32_t	size_hdr;
97	uint32_t	size_csum;
98	uint32_t	size_avail;
99
100	struct csum_state *css;
101};
102
103
104struct board_info {
105	char *model;
106	char *name;
107	uint32_t flash_size;
108
109	char sig_boot[SIG_LEN];
110	char sig_conf[SIG_LEN];
111	char sig_webp[SIG_LEN];
112
113	uint32_t boot_size;
114	uint32_t conf_size;
115	uint32_t webp_size;
116	uint32_t webp_size_max;
117	uint32_t code_size;
118
119	uint32_t addr_code;
120	uint32_t addr_webp;
121};
122
123#define BOARD(m, n, f, sigb, sigw, bs, cs, ws, ac, aw) {\
124	.model = m, .name = n, .flash_size = f<<20, \
125	.sig_boot = sigb, .sig_conf = SIG_CONF, .sig_webp = sigw, \
126	.boot_size = bs, .conf_size = cs, \
127	.webp_size = ws, .webp_size_max = 3*0x10000, \
128	.addr_code = ac, .addr_webp = aw \
129	}
130
131#define BOARD_ADM(m,n,f, sigw) BOARD(m,n,f, ADM_BOOT_SIG, sigw, \
132	ADM_BOOT_SIZE, ADM_CONF_SIZE, ADM_WEBP_SIZE, \
133	ADM_CODE_ADDR, ADM_WEBP_ADDR)
134
135
136/*
137 * Globals
138 */
139char *progname;
140char *ofname = NULL;
141int verblevel = 0;
142int invalid_causes_error = 1;
143int keep_invalid_images = 0;
144
145struct board_info *board = NULL;
146
147struct csys_block *boot_block = NULL;
148struct csys_block *conf_block = NULL;
149struct csys_block *webp_block = NULL;
150struct csys_block *code_block = NULL;
151
152struct csys_block blocks[MAX_NUM_BLOCKS];
153int num_blocks = 0;
154
155static struct board_info boards[] = {
156	/* The original Edimax products */
157	BOARD_ADM("BR-6104K", "Edimax BR-6104K", 2, SIG_BR6104K),
158	BOARD_ADM("BR-6104KP", "Edimax BR-6104KP", 2, SIG_BR6104KP),
159	BOARD_ADM("BR-6104Wg", "Edimax BR-6104Wg", 2, SIG_BR6104Wg),
160	BOARD_ADM("BR-6114WG", "Edimax BR-6114WG", 2, SIG_BR6114WG),
161	BOARD_ADM("BR-6524K", "Edimax BR-6524K", 2, SIG_BR6524K),
162	BOARD_ADM("BR-6524KP", "Edimax BR-6524KP", 2, SIG_BR6524KP),
163	BOARD_ADM("BR-6524WG", "Edimax BR-6524WG", 4, SIG_BR6524WG),
164	BOARD_ADM("BR-6524WP", "Edimax BR-6524WP", 4, SIG_BR6524WP),
165	BOARD_ADM("BR-6541K", "Edimax BR-6541K", 2, SIG_BR6541K),
166	BOARD_ADM("BR-6541KP", "Edimax BR-6541K", 2, SIG_BR6541KP),
167	BOARD_ADM("BR-6541WP", "Edimax BR-6541WP", 4, SIG_BR6541WP),
168	BOARD_ADM("EW-7207APg", "Edimax EW-7207APg", 2, SIG_EW7207APg),
169	BOARD_ADM("PS-1205UWg", "Edimax PS-1205UWg", 2, SIG_PS1205UWg),
170	BOARD_ADM("PS-3205U", "Edimax PS-3205U", 2, SIG_PS3205U),
171	BOARD_ADM("PS-3205UWg", "Edimax PS-3205UWg", 2, SIG_PS3205UWg),
172
173	/* Hawking products */
174	BOARD_ADM("H2BR4", "Hawking H2BR4", 2, SIG_H2BR4),
175	BOARD_ADM("H2WR54G", "Hawking H2WR54G", 4, SIG_H2WR54G),
176
177	/* Planet products */
178	BOARD_ADM("XRT-401D", "Planet XRT-401D", 2, SIG_XRT401D),
179	BOARD_ADM("XRT-402D", "Planet XRT-402D", 2, SIG_XRT402D),
180
181	/* Conceptronic products */
182	BOARD_ADM("C54BSR4", "Conceptronic C54BSR4", 2, SIG_C54BSR4),
183
184	/* OSBRiDGE products */
185	BOARD_ADM("5GXi", "OSBDRiDGE 5GXi", 2, SIG_5GXI),
186
187	{.model = NULL}
188};
189
190/*
191 * Message macros
192 */
193#define ERR(fmt, ...) do { \
194	fflush(0); \
195	fprintf(stderr, "[%s] *** error: " fmt "\n", progname, ## __VA_ARGS__ ); \
196} while (0)
197
198#define ERRS(fmt, ...) do { \
199	int save = errno; \
200	fflush(0); \
201	fprintf(stderr, "[%s] *** error: " fmt "\n", progname, ## __VA_ARGS__ \
202		, strerror(save)); \
203} while (0)
204
205#define WARN(fmt, ...) do { \
206	fprintf(stderr, "[%s] *** warning: " fmt "\n", progname, ## __VA_ARGS__ ); \
207} while (0)
208
209#define DBG(lev, fmt, ...) do { \
210	if (verblevel < lev) \
211		break;\
212	fprintf(stderr, "[%s] " fmt "\n", progname, ## __VA_ARGS__ ); \
213} while (0)
214
215#define ERR_FATAL		-1
216#define ERR_INVALID_IMAGE	-2
217
218void
219usage(int status)
220{
221	FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout;
222	struct board_info *board;
223
224	fprintf(stream, "Usage: %s [OPTIONS...] <file>\n", progname);
225	fprintf(stream,
226"\n"
227"Options:\n"
228"  -B <board>      create image for the board specified with <board>.\n"
229"                  valid <board> values:\n"
230	);
231	for (board = boards; board->model != NULL; board++){
232		fprintf(stream,
233"                  %-12s: %s\n",
234		 board->model, board->name);
235	};
236	fprintf(stream,
237"  -d              don't throw error on invalid images\n"
238"  -k              keep invalid images\n"
239"  -b <file>[:<align>[:<padc>]]\n"
240"                  add boot code to the image\n"
241"  -c <file>[:<align>[:<padc>]]\n"
242"                  add configuration settings to the image\n"
243"  -r <file>:[<addr>][:<align>[:<padc>]]\n"
244"                  add runtime code to the image\n"
245"  -w [<file>:[<addr>][:<align>[:<padc>]]]\n"
246"                  add webpages to the image\n"
247"  -x <file>[:<align>[:<padc>]]\n"
248"                  add extra data at the end of the image\n"
249"  -h              show this screen\n"
250"Parameters:\n"
251"  <file>          write output to the file <file>\n"
252	);
253
254	exit(status);
255}
256
257static inline uint32_t align(uint32_t base, uint32_t alignment)
258{
259	uint32_t ret;
260
261	if (alignment) {
262		ret = (base + alignment - 1);
263		ret &= ~(alignment-1);
264	} else {
265		ret = base;
266	}
267
268	return ret;
269}
270
271/*
272 * argument parsing
273 */
274int
275str2u32(char *arg, uint32_t *val)
276{
277	char *err = NULL;
278	uint32_t t;
279
280	errno=0;
281	t = strtoul(arg, &err, 0);
282	if (errno || (err==arg) || ((err != NULL) && *err)) {
283		return -1;
284	}
285
286	*val = t;
287	return 0;
288}
289
290
291int
292str2u16(char *arg, uint16_t *val)
293{
294	char *err = NULL;
295	uint32_t t;
296
297	errno=0;
298	t = strtoul(arg, &err, 0);
299	if (errno || (err==arg) || ((err != NULL) && *err) || (t >= 0x10000)) {
300		return -1;
301	}
302
303	*val = t & 0xFFFF;
304	return 0;
305}
306
307int
308str2u8(char *arg, uint8_t *val)
309{
310	char *err = NULL;
311	uint32_t t;
312
313	errno=0;
314	t = strtoul(arg, &err, 0);
315	if (errno || (err==arg) || ((err != NULL) && *err) || (t >= 0x100)) {
316		return -1;
317	}
318
319	*val = t & 0xFF;
320	return 0;
321}
322
323int
324str2sig(char *arg, uint32_t *sig)
325{
326	if (strlen(arg) != 4)
327		return -1;
328
329	*sig = arg[0] | (arg[1] << 8) | (arg[2] << 16) | (arg[3] << 24);
330
331	return 0;
332}
333
334
335int
336parse_arg(char *arg, char *buf, char *argv[])
337{
338	int res = 0;
339	size_t argl;
340	char *tok;
341	char **ap = &buf;
342	int i;
343
344	memset(argv, 0, MAX_ARG_COUNT * sizeof(void *));
345
346	if ((arg == NULL)) {
347		/* no arguments */
348		return 0;
349	}
350
351	argl = strlen(arg);
352	if (argl == 0) {
353		/* no arguments */
354		return 0;
355	}
356
357	if (argl >= MAX_ARG_LEN) {
358		/* argument is too long */
359		argl = MAX_ARG_LEN-1;
360	}
361
362	memcpy(buf, arg, argl);
363	buf[argl] = '\0';
364
365	for (i = 0; i < MAX_ARG_COUNT; i++) {
366		tok = strsep(ap, ":");
367		if (tok == NULL) {
368			break;
369		}
370#if 0
371		else if (tok[0] == '\0') {
372			break;
373		}
374#endif
375		argv[i] = tok;
376		res++;
377	}
378
379	return res;
380}
381
382
383int
384required_arg(char c, char *arg)
385{
386	if (arg == NULL || *arg != '-')
387		return 0;
388
389	ERR("option -%c requires an argument\n", c);
390	return ERR_FATAL;
391}
392
393
394int
395is_empty_arg(char *arg)
396{
397	int ret = 1;
398	if (arg != NULL) {
399		if (*arg) ret = 0;
400	};
401	return ret;
402}
403
404
405void
406csum8_update(uint8_t *p, uint32_t len, struct csum_state *css)
407{
408	for ( ; len > 0; len --) {
409		css->val += *p++;
410	}
411}
412
413
414uint16_t
415csum8_get(struct csum_state *css)
416{
417	uint8_t t;
418
419	t = css->val;
420	return ~t + 1;
421}
422
423
424void
425csum16_update(uint8_t *p, uint32_t len, struct csum_state *css)
426{
427	uint16_t t;
428
429	if (css->odd) {
430		t = css->tmp + (p[0]<<8);
431		css->val += LE16_TO_HOST(t);
432		css->odd = 0;
433		len--;
434		p++;
435	}
436
437	for ( ; len > 1; len -= 2, p +=2 ) {
438		t = p[0] + (p[1] << 8);
439		css->val += LE16_TO_HOST(t);
440	}
441
442	if (len == 1) {
443		css->tmp = p[0];
444		css->odd = 1;
445	}
446}
447
448
449uint16_t
450csum16_get(struct csum_state *css)
451{
452	char pad = 0;
453
454	csum16_update(&pad, 1, css);
455	return ~css->val + 1;
456}
457
458
459void
460csum_init(struct csum_state *css, int size)
461{
462	css->val = 0;
463	css->tmp = 0;
464	css->odd = 0;
465	css->size = size;
466}
467
468
469void
470csum_update(uint8_t *p, uint32_t len, struct csum_state *css)
471{
472	switch (css->size) {
473	case CSUM_SIZE_8:
474		csum8_update(p,len,css);
475		break;
476	case CSUM_SIZE_16:
477		csum16_update(p,len,css);
478		break;
479	}
480}
481
482
483uint16_t
484csum_get(struct csum_state *css)
485{
486	uint16_t ret;
487
488	switch (css->size) {
489	case CSUM_SIZE_8:
490		ret = csum8_get(css);
491		break;
492	case CSUM_SIZE_16:
493		ret = csum16_get(css);
494		break;
495	}
496
497	return ret;
498}
499
500
501/*
502 * routines to write data to the output file
503 */
504int
505write_out_data(FILE *outfile, uint8_t *data, size_t len,
506		struct csum_state *css)
507{
508	errno = 0;
509
510	fwrite(data, len, 1, outfile);
511	if (errno) {
512		ERRS("unable to write output file");
513		return ERR_FATAL;
514	}
515
516	if (css) {
517		csum_update(data, len, css);
518	}
519
520	return 0;
521}
522
523
524int
525write_out_padding(FILE *outfile, size_t len, uint8_t padc,
526		 struct csum_state *css)
527{
528	uint8_t buf[512];
529	size_t buflen = sizeof(buf);
530	int err;
531
532	memset(buf, padc, buflen);
533	while (len > 0) {
534		if (len < buflen)
535			buflen = len;
536
537		err = write_out_data(outfile, buf, buflen, css);
538		if (err)
539			return err;
540
541		len -= buflen;
542	}
543
544	return 0;
545}
546
547
548int
549block_stat_file(struct csys_block *block)
550{
551	struct stat st;
552	int err;
553
554	if (block->file_name == NULL)
555		return 0;
556
557	err = stat(block->file_name, &st);
558	if (err){
559		ERRS("stat failed on %s", block->file_name);
560		return ERR_FATAL;
561	}
562
563	block->file_size = st.st_size;
564	return 0;
565}
566
567
568int
569block_writeout_hdr(FILE *outfile, struct csys_block *block)
570{
571	struct csys_header hdr;
572	int res;
573
574	if (block->size_hdr == 0)
575		return 0;
576
577	/* setup header fields */
578	memcpy(hdr.sig, block->sig, 4);
579	hdr.addr = HOST_TO_LE32(block->addr);
580	hdr.size = HOST_TO_LE32(block->size - block->size_hdr - block->size_csum);
581
582	DBG(1,"writing header for block");
583	res = write_out_data(outfile, (uint8_t *)&hdr, sizeof(hdr),NULL);
584	return res;
585
586}
587
588
589int
590block_writeout_file(FILE *outfile, struct csys_block *block)
591{
592	char buf[FILE_BUF_LEN];
593	size_t buflen = sizeof(buf);
594	FILE *f;
595	size_t len;
596	int res;
597
598	if (block->file_name == NULL)
599		return 0;
600
601	if (block->file_size == 0)
602		return 0;
603
604	errno = 0;
605	f = fopen(block->file_name,"r");
606	if (errno) {
607		ERRS("unable to open file: %s", block->file_name);
608		return ERR_FATAL;
609	}
610
611	len = block->file_size;
612	while (len > 0) {
613		if (len < buflen)
614			buflen = len;
615
616		/* read data from source file */
617		errno = 0;
618		fread(buf, buflen, 1, f);
619		if (errno != 0) {
620			ERRS("unable to read from file: %s", block->file_name);
621			res = ERR_FATAL;
622			break;
623		}
624
625		res = write_out_data(outfile, buf, buflen, block->css);
626		if (res)
627			break;
628
629		len -= buflen;
630	}
631
632	fclose(f);
633	return res;
634}
635
636
637int
638block_writeout_data(FILE *outfile, struct csys_block *block)
639{
640	int res;
641	size_t padlen;
642
643	res = block_writeout_file(outfile, block);
644	if (res)
645		return res;
646
647	/* write padding data if neccesary */
648	padlen = block->size_avail - block->file_size;
649	DBG(1,"padding block, length=%d", padlen);
650	res = write_out_padding(outfile, padlen, block->padc, block->css);
651
652	return res;
653}
654
655
656int
657block_writeout_csum(FILE *outfile, struct csys_block *block)
658{
659	uint16_t csum;
660	int res;
661
662	if (block->size_csum == 0)
663		return 0;
664
665	DBG(1,"writing checksum for block");
666	csum = HOST_TO_LE16(csum_get(block->css));
667	res = write_out_data(outfile, (uint8_t *)&csum, block->size_csum, NULL);
668
669	return res;
670}
671
672
673int
674block_writeout(FILE *outfile, struct csys_block *block)
675{
676	int res;
677	struct csum_state css;
678
679	res = 0;
680
681	if (block == NULL)
682		return res;
683
684	block->css = NULL;
685
686	DBG(2, "writing block, file=%s, file_size=%d, space=%d",
687		block->file_name, block->file_size, block->size_avail);
688	res = block_writeout_hdr(outfile, block);
689	if (res)
690		return res;
691
692	if (block->size_csum != 0) {
693		block->css = &css;
694		csum_init(&css, block->size_csum);
695	}
696
697	res = block_writeout_data(outfile, block);
698	if (res)
699		return res;
700
701	res = block_writeout_csum(outfile, block);
702	if (res)
703		return res;
704
705	return res;
706}
707
708
709int
710write_out_blocks(FILE *outfile)
711{
712	struct csys_block *block;
713	int i, res;
714
715	res = block_writeout(outfile, boot_block);
716	if (res)
717		return res;
718
719	res = block_writeout(outfile, conf_block);
720	if (res)
721		return res;
722
723	res = block_writeout(outfile, webp_block);
724	if (res)
725		return res;
726
727	res = block_writeout(outfile, code_block);
728	if (res)
729		return res;
730
731	res = 0;
732	for (i=0; i < num_blocks; i++) {
733		block = &blocks[i];
734
735		if (block->type != BLOCK_TYPE_XTRA)
736			continue;
737
738		res = block_writeout(outfile, block);
739		if (res)
740			break;
741	}
742
743	return res;
744}
745
746
747struct board_info *
748find_board(char *model)
749{
750	struct board_info *ret;
751	struct board_info *board;
752
753	ret = NULL;
754	for (board = boards; board->model != NULL; board++){
755		if (strcasecmp(model, board->model) == 0) {
756			ret = board;
757			break;
758		}
759	};
760
761	return ret;
762}
763
764
765int
766parse_opt_board(char ch, char *arg)
767{
768
769	DBG(1,"parsing board option: -%c %s", ch, arg);
770
771	if (board != NULL) {
772		ERR("only one board option allowed");
773		return ERR_FATAL;
774	}
775
776	if (required_arg(ch, arg))
777		return ERR_FATAL;
778
779	board = find_board(arg);
780	if (board == NULL){
781		ERR("invalid/unknown board specified: %s", arg);
782		return ERR_FATAL;
783	}
784
785	return 0;
786}
787
788
789int
790parse_opt_block(char ch, char *arg)
791{
792	char buf[MAX_ARG_LEN];
793	char *argv[MAX_ARG_COUNT];
794	int argc;
795	char *p;
796	struct csys_block *block;
797	int i;
798
799	if ( num_blocks > MAX_NUM_BLOCKS ) {
800		ERR("too many blocks specified");
801		return ERR_FATAL;
802	}
803
804	block = &blocks[num_blocks];
805
806	/* setup default field values */
807	block->need_file = 1;
808	block->padc = 0xFF;
809
810	switch (ch) {
811	case 'b':
812		if (boot_block) {
813			WARN("only one boot block allowed");
814			break;
815		}
816		block->type = BLOCK_TYPE_BOOT;
817		boot_block = block;
818		break;
819	case 'c':
820		if (conf_block) {
821			WARN("only one config block allowed");
822			break;
823		}
824		block->type = BLOCK_TYPE_CONF;
825		conf_block = block;
826		break;
827	case 'w':
828		if (webp_block) {
829			WARN("only one web block allowed");
830			break;
831		}
832		block->type = BLOCK_TYPE_WEBP;
833		block->size_hdr = sizeof(struct csys_header);
834		block->size_csum = CSUM_SIZE_8;
835		block->need_file = 0;
836		webp_block = block;
837		break;
838	case 'r':
839		if (code_block) {
840			WARN("only one runtime block allowed");
841			break;
842		}
843		block->type = BLOCK_TYPE_CODE;
844		block->size_hdr = sizeof(struct csys_header);
845		block->size_csum = CSUM_SIZE_16;
846		code_block = block;
847		break;
848	case 'x':
849		block->type = BLOCK_TYPE_XTRA;
850		break;
851	default:
852		ERR("unknown block type \"%c\"", ch);
853		return ERR_FATAL;
854	}
855
856	argc = parse_arg(arg, buf, argv);
857
858	i = 0;
859	p = argv[i++];
860	if (!is_empty_arg(p)) {
861		block->file_name = strdup(p);
862		if (block->file_name == NULL) {
863			ERR("not enough memory");
864			return ERR_FATAL;
865		}
866	} else if (block->need_file){
867		ERR("no file specified in %s", arg);
868		return ERR_FATAL;
869	}
870
871	if (block->size_hdr) {
872		p = argv[i++];
873		if (!is_empty_arg(p)) {
874			if (str2u32(p, &block->addr) != 0) {
875				ERR("invalid start address in %s", arg);
876				return ERR_FATAL;
877			}
878			block->addr_set = 1;
879		}
880	}
881
882	p = argv[i++];
883	if (!is_empty_arg(p)) {
884		if (str2u32(p, &block->align) != 0) {
885			ERR("invalid alignment value in %s", arg);
886			return ERR_FATAL;
887		}
888		block->align_set = 1;
889	}
890
891	p = argv[i++];
892	if (!is_empty_arg(p) && (str2u8(p, &block->padc) != 0)) {
893		ERR("invalid paddig character in %s", arg);
894		return ERR_FATAL;
895	}
896
897	num_blocks++;
898
899	return 0;
900}
901
902
903int
904process_blocks(void)
905{
906	struct csys_block *block;
907	uint32_t offs = 0;
908	int i;
909	int res;
910
911	res = 0;
912	/* collecting stats */
913	for (i = 0; i < num_blocks; i++) {
914		block = &blocks[i];
915		res = block_stat_file(block);
916		if (res)
917			return res;
918	}
919
920	/* bootloader */
921	block = boot_block;
922	if (block) {
923		block->size = board->boot_size;
924		if (block->file_size > board->boot_size) {
925			WARN("boot block is too big");
926			res = ERR_INVALID_IMAGE;
927		}
928	}
929	offs += board->boot_size;
930
931	/* configuration data */
932	block = conf_block;
933	if (block) {
934		block->size = board->conf_size;
935		if (block->file_size > board->conf_size) {
936			WARN("config block is too big");
937			res = ERR_INVALID_IMAGE;
938		}
939	}
940	offs += board->conf_size;
941
942	/* webpages */
943	block = webp_block;
944	if (block) {
945
946		memcpy(block->sig, board->sig_webp, 4);
947
948		if (block->addr_set == 0)
949			block->addr = board->addr_webp;
950
951		if (block->align_set == 0)
952			block->align = DEFAULT_BLOCK_ALIGN;
953
954		block->size = align(offs + block->file_size + block->size_hdr +
955				block->size_csum, block->align) - offs;
956
957		if (block->size > board->webp_size_max) {
958			WARN("webpages block is too big");
959			res = ERR_INVALID_IMAGE;
960		}
961
962		DBG(2,"webpages start at %08x, size=%08x", offs,
963				block->size);
964
965		offs += block->size;
966		if (offs > board->flash_size) {
967			WARN("webp block is too big");
968			res = ERR_INVALID_IMAGE;
969		}
970	}
971
972	/* runtime code */
973	block = code_block;
974	if (block) {
975		memcpy(code_block->sig, SIG_CSYS, 4);
976
977		if (block->addr_set == 0)
978			block->addr = board->addr_code;
979
980		if (block->align_set == 0)
981			block->align = DEFAULT_BLOCK_ALIGN;
982
983		block->size = align(offs + block->file_size +
984				block->size_hdr + block->size_csum,
985				block->align) - offs;
986
987		DBG(2,"code block start at %08x, size=%08x", offs,
988				block->size);
989
990		offs += block->size;
991		if (offs > board->flash_size) {
992			WARN("code block is too big");
993			res = ERR_INVALID_IMAGE;
994		}
995	}
996
997	for (i = 0; i < num_blocks; i++) {
998		block = &blocks[i];
999
1000		if (block->type != BLOCK_TYPE_XTRA)
1001			continue;
1002
1003		if (block->align_set == 0)
1004			block->align = DEFAULT_BLOCK_ALIGN;
1005
1006		block->size = align(offs + block->file_size,
1007				block->align) - offs;
1008
1009		DBG(2,"file %s start at %08x, size=%08x, align=%08x",
1010			block->file_name, offs, block->size, block->align);
1011
1012		offs += block->size;
1013		if (offs > board->flash_size) {
1014			WARN("file %s is too big, size=%d, avail=%d",
1015				block->file_name, block->file_size,
1016				board->flash_size - offs);
1017			res = ERR_INVALID_IMAGE;
1018		}
1019	}
1020
1021	for (i = 0; i < num_blocks; i++) {
1022		block = &blocks[i];
1023
1024		block->size_avail = block->size - block->size_hdr -
1025			block->size_csum;
1026
1027		if (block->size_avail < block->file_size) {
1028			WARN("file %s is too big, size=%d, avail=%d",
1029				block->file_name, block->file_size,
1030				block->size_avail);
1031			res = ERR_INVALID_IMAGE;
1032		}
1033	}
1034
1035	return res;
1036}
1037
1038
1039int
1040main(int argc, char *argv[])
1041{
1042	int optinvalid = 0;   /* flag for invalid option */
1043	int c;
1044	int res = ERR_FATAL;
1045
1046	FILE *outfile;
1047
1048	progname=basename(argv[0]);
1049
1050	opterr = 0;  /* could not print standard getopt error messages */
1051	while ( 1 ) {
1052		optinvalid = 0;
1053
1054		c = getopt(argc, argv, "b:B:c:dhkr:vw:x:");
1055		if (c == -1)
1056			break;
1057
1058		switch (c) {
1059		case 'b':
1060		case 'c':
1061		case 'r':
1062		case 'x':
1063			optinvalid = parse_opt_block(c,optarg);
1064			break;
1065		case 'w':
1066			if (optarg != NULL && *optarg == '-') {
1067				/* rollback */
1068				optind--;
1069				optarg = NULL;
1070			}
1071			optinvalid = parse_opt_block(c,optarg);
1072			break;
1073		case 'd':
1074			invalid_causes_error = 0;
1075			break;
1076		case 'k':
1077			keep_invalid_images = 1;
1078			break;
1079		case 'B':
1080			optinvalid = parse_opt_board(c,optarg);
1081			break;
1082		case 'v':
1083			verblevel++;
1084			break;
1085		case 'h':
1086			usage(EXIT_SUCCESS);
1087			break;
1088		default:
1089			optinvalid = 1;
1090			break;
1091		}
1092		if (optinvalid != 0 ){
1093			ERR("invalid option: -%c", optopt);
1094			goto out;
1095		}
1096	}
1097
1098	if (board == NULL) {
1099		ERR("no board specified");
1100		goto out;
1101	}
1102
1103	if (optind == argc) {
1104		ERR("no output file specified");
1105		goto out;
1106	}
1107
1108	ofname = argv[optind++];
1109
1110	if (optind < argc) {
1111		ERR("invalid option: %s", argv[optind]);
1112		goto out;
1113	}
1114
1115	res = process_blocks();
1116	if (res == ERR_FATAL)
1117		goto out;
1118
1119	if (res == ERR_INVALID_IMAGE) {
1120		if (invalid_causes_error)
1121			res = ERR_FATAL;
1122
1123		if (keep_invalid_images == 0) {
1124			WARN("generation of invalid images disabled", ofname);
1125			goto out;
1126		}
1127
1128		WARN("generating invalid image", ofname);
1129	}
1130
1131	outfile = fopen(ofname, "w");
1132	if (outfile == NULL) {
1133		ERRS("could not open \"%s\" for writing", ofname);
1134		res = ERR_FATAL;
1135		goto out;
1136	}
1137
1138	if (write_out_blocks(outfile) != 0) {
1139		res = ERR_FATAL;
1140		goto out_flush;
1141	}
1142
1143	DBG(1,"Image file %s completed.", ofname);
1144
1145out_flush:
1146	fflush(outfile);
1147	fclose(outfile);
1148	if (res == ERR_FATAL) {
1149		unlink(ofname);
1150	}
1151out:
1152	if (res == ERR_FATAL)
1153		return EXIT_FAILURE;
1154
1155	return EXIT_SUCCESS;
1156}
1157