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