b432cbc1d1ca2ea1c27f9b24128ae1c4d9b80e7d
[openwrt.git] / tools / firmware-utils / src / mkmylofw.c
1 /*
2  *  Copyright (C) 2006,2007 Gabor Juhos
3  *
4  *  This program is free software; you can redistribute  it and/or modify it
5  *  under  the terms of  the GNU General  Public License as published by the
6  *  Free Software Foundation;  either version 2 of the  License, or (at your
7  *  option) any later version.
8  *
9  */
10
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <stdint.h>
14 #include <string.h>
15 #include <unistd.h>     /* for unlink() */
16 #include <libgen.h>
17 #include <getopt.h>     /* for getopt() */
18 #include <stdarg.h>
19 #include <errno.h>
20 #include <sys/stat.h>
21 #include <endian.h>     /* for __BYTE_ORDER */
22
23 #if defined(__CYGWIN__)
24 #  include <byteswap.h>
25 #endif
26
27 #if (__BYTE_ORDER == __LITTLE_ENDIAN)
28 #  define HOST_TO_LE16(x)       (x)
29 #  define HOST_TO_LE32(x)       (x)
30 #else
31 #  define HOST_TO_LE16(x)       bswap_16(x)
32 #  define HOST_TO_LE32(x)       bswap_32(x)
33 #endif
34
35 #include "myloader.h"
36
37 #define MAX_FW_BLOCKS   32
38 #define MAX_ARG_COUNT   32
39 #define MAX_ARG_LEN     1024
40 #define FILE_BUF_LEN    (16*1024)
41
42 struct fw_block {
43         uint32_t        addr;
44         uint32_t        blocklen; /* length of the block */
45         uint32_t        flags;
46
47         char            *name;  /* name of the file */
48         uint32_t        size;   /* length of the file */
49         uint32_t        crc;    /* crc value of the file */
50 };
51
52 #define BLOCK_FLAG_HAVEHDR    0x0001
53
54 struct cpx_board {
55         char            *model; /* model number*/
56         char            *name;  /* model name*/
57         char            *desc;  /* description */
58         uint16_t        vid;    /* vendor id */
59         uint16_t        did;    /* device id */
60         uint16_t        svid;   /* sub vendor id */
61         uint16_t        sdid;   /* sub device id */
62         uint32_t        flash_size;     /* size of flash */
63 };
64
65 #define BOARD(_vid, _did, _svid, _sdid, _flash, _mod, _name, _desc) {           \
66         .model = _mod, .name = _name, .desc = _desc,                            \
67         .vid = _vid, .did = _did, .svid = _svid, .sdid = _sdid,         \
68         .flash_size = (_flash << 20)    }
69
70 #define CPX_BOARD(_did, _flash, _mod, _name, _desc) \
71         BOARD(VENID_COMPEX, _did, VENID_COMPEX, _did, _flash, _mod, _name, _desc)
72
73 char    *progname;
74 char    *ofname = NULL;
75
76 uint32_t flash_size = 0;
77 int     fw_num_partitions = 0;
78 int     fw_num_blocks = 0;
79 int     verblevel = 0;
80
81 struct mylo_fw_header fw_header;
82 struct mylo_partition fw_partitions[MYLO_MAX_PARTITIONS];
83 struct fw_block fw_blocks[MAX_FW_BLOCKS];
84
85 struct cpx_board boards[] = {
86         CPX_BOARD(DEVID_COMPEX_NP18A, 4,
87                 "NP18A", "Compex NetPassage 18A",
88                 "Dualband Wireless A+G Internet Gateway"),
89         CPX_BOARD(DEVID_COMPEX_NP26G8M, 2,
90                 "NP26G8M", "Compex NetPassage 26G (8M)",
91                 "Wireless-G Broadband Multimedia Gateway"),
92         CPX_BOARD(DEVID_COMPEX_NP26G16M, 4,
93                 "NP26G16M", "Compex NetPassage 26G (16M)",
94                 "Wireless-G Broadband Multimedia Gateway"),
95         CPX_BOARD(DEVID_COMPEX_NP27G, 4,
96                 "NP27G", "Compex NetPassage 27G",
97                 "Wireless-G 54Mbps eXtended Range Router"),
98         CPX_BOARD(DEVID_COMPEX_NP28G, 4,
99                 "NP28G", "Compex NetPassage 28G",
100                 "Wireless 108Mbps Super-G XR Multimedia Router with 4 USB Ports"),
101         CPX_BOARD(DEVID_COMPEX_NP28GHS, 4,
102                 "NP28GHS", "Compex NetPassage 28G (HotSpot)",
103                 "HotSpot Solution"),
104         CPX_BOARD(DEVID_COMPEX_WP18, 4,
105                 "WP18", "Compex NetPassage WP18",
106                 "Wireless-G 54Mbps A+G Dualband Access Point"),
107         CPX_BOARD(DEVID_COMPEX_WP54G, 4,
108                 "WP54G", "Compex WP54G",
109                 "Wireless-G 54Mbps XR Access Point"),
110         CPX_BOARD(DEVID_COMPEX_WP54Gv1C, 2,
111                 "WP54Gv1C", "Compex WP54G rev.1C",
112                 "Wireless-G 54Mbps XR Access Point"),
113         CPX_BOARD(DEVID_COMPEX_WP54AG, 4,
114                 "WP54AG", "Compex WP54AG",
115                 "Wireless-AG 54Mbps XR Access Point"),
116         CPX_BOARD(DEVID_COMPEX_WPP54G, 4,
117                 "WPP54G", "Compex WPP54G",
118                 "Outdoor Access Point"),
119         CPX_BOARD(DEVID_COMPEX_WPP54AG, 4,
120                 "WPP54AG", "Compex WPP54AG",
121                 "Outdoor Access Point"),
122         {.model = NULL}
123 };
124
125 void
126 errmsgv(int syserr, const char *fmt, va_list arg_ptr)
127 {
128         int save = errno;
129
130         fflush(0);
131         fprintf(stderr, "[%s] Error: ", progname);
132         vfprintf(stderr, fmt, arg_ptr);
133         if (syserr != 0) {
134                 fprintf(stderr, ": %s", strerror(save));
135         }
136         fprintf(stderr, "\n");
137 }
138
139 void
140 errmsg(int syserr, const char *fmt, ...)
141 {
142         va_list arg_ptr;
143         va_start(arg_ptr, fmt);
144         errmsgv(syserr, fmt, arg_ptr);
145         va_end(arg_ptr);
146 }
147
148 void
149 dbgmsg(int level, const char *fmt, ...)
150 {
151         va_list arg_ptr;
152         if (verblevel >= level) {
153                 fflush(0);
154                 va_start(arg_ptr, fmt);
155                 vfprintf(stderr, fmt, arg_ptr);
156                 fprintf(stderr, "\n");
157                 va_end(arg_ptr);
158         }
159 }
160
161
162 void
163 usage(int status)
164 {
165         FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout;
166         struct cpx_board *board;
167
168         fprintf(stream, "Usage: %s [OPTION...] <file>\n", progname);
169         fprintf(stream,
170 "\n"
171 "  <file>          write output to the <file>\n"
172 "\n"
173 "Options:\n"
174 "  -B <board>      create firmware for the board specified with <board>.\n"
175 "                  This option set vendor id, device id, subvendor id,\n"
176 "                  subdevice id, and flash size options to the right value.\n"
177 "                  valid <board> values:\n");
178         for (board = boards; board->model != NULL; board++){
179                 fprintf(stream,
180 "                      %-12s: %s\n",
181                  board->model, board->name);
182         };
183         fprintf(stream,
184 "  -i <vid>:<did>[:<svid>[:<sdid>]]\n"
185 "                  create firmware for board with vendor id <vid>, device\n"
186 "                  id <did>, subvendor id <svid> and subdevice id <sdid>.\n"
187 "  -r <rev>        set board revision to <rev>.\n"
188 "  -s <size>       set flash size to <size>\n"
189 "  -b <addr>:<len>[:[<flags>]:<file>]\n"
190 "                  define block at <addr> with length of <len>.\n"
191 "                  valid <flag> values:\n"
192 "                      h : add crc header before the file data.\n"
193 "  -p <addr>:<len>[:<flags>[:<param>[:<file>]]]\n"
194 "                  add partition at <addr>, with size of <len> to the\n"
195 "                  partition table, set partition flags to <flags> and\n"
196 "                  partition parameter to <param>. If the <file> is specified\n"
197 "                  content of the file is also added to the firmware image.\n"
198 "                  valid <flag> values:\n"
199 "                      a:  this is the active partition. The bootloader loads\n"
200 "                          the firmware from this partition.\n"
201 "                      h:  the partition data have a header.\n"
202 "                      p:  the bootloader loads data from this partition to\n"
203 "                          the RAM before decompress it.\n"
204 "  -h              show this screen\n"
205         );
206
207         exit(status);
208 }
209
210 /*
211  * Code to compute the CRC-32 table. Borrowed from
212  * gzip-1.0.3/makecrc.c.
213  */
214
215 static uint32_t crc_32_tab[256];
216
217 void
218 init_crc_table(void)
219 {
220         /* Not copyrighted 1990 Mark Adler      */
221
222         uint32_t c;      /* crc shift register */
223         uint32_t e;      /* polynomial exclusive-or pattern */
224         int i;           /* counter for all possible eight bit values */
225         int k;           /* byte being shifted into crc apparatus */
226
227         /* terms of polynomial defining this crc (except x^32): */
228         static const int p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26};
229
230         /* Make exclusive-or pattern from polynomial */
231         e = 0;
232         for (i = 0; i < sizeof(p)/sizeof(int); i++)
233                 e |= 1L << (31 - p[i]);
234
235         crc_32_tab[0] = 0;
236
237         for (i = 1; i < 256; i++) {
238                 c = 0;
239                 for (k = i | 256; k != 1; k >>= 1) {
240                         c = c & 1 ? (c >> 1) ^ e : c >> 1;
241                         if (k & 1)
242                                 c ^= e;
243                 }
244                 crc_32_tab[i] = c;
245         }
246 }
247
248
249 void
250 update_crc(uint8_t *p, uint32_t len, uint32_t *crc)
251 {
252         uint32_t t;
253
254         t = *crc ^ 0xFFFFFFFFUL;
255         while (len--) {
256                 t = crc_32_tab[(t ^ *p++) & 0xff] ^ (t >> 8);
257         }
258         *crc = t ^ 0xFFFFFFFFUL;
259 }
260
261
262 uint32_t
263 get_crc(uint8_t *p, uint32_t len)
264 {
265         uint32_t crc;
266         
267         crc = 0;
268         update_crc(p ,len , &crc);
269         return crc;
270 }
271
272
273 int
274 str2u32(char *arg, uint32_t *val)
275 {
276         char *err = NULL;
277         uint32_t t;
278         
279         errno=0;
280         t = strtoul(arg, &err, 0);
281         if (errno || (err==arg) || ((err != NULL) && *err)) {
282                 return -1;
283         }
284
285         *val = t;
286         return 0;
287 }
288
289
290 int
291 str2u16(char *arg, uint16_t *val)
292 {
293         char *err = NULL;
294         uint32_t t;
295
296         errno=0;
297         t = strtoul(arg, &err, 0);
298         if (errno || (err==arg) || ((err != NULL) && *err) || (t >= 0x10000)) {
299                 return -1;
300         }
301         
302         *val = t & 0xFFFF;
303         return 0;
304 }
305
306
307 struct cpx_board *
308 find_board(char *model){
309         struct cpx_board *board;
310         struct cpx_board *tmp;
311
312         board = NULL;
313         for (tmp = boards; tmp->model != NULL; tmp++){
314                 if (strcmp(model, tmp->model) == 0) {
315                         board = tmp;
316                         break;
317                 }
318         };
319
320         return board;
321 }
322
323
324 int
325 get_file_crc(struct fw_block *ff)
326 {
327         FILE *f;
328         uint8_t buf[FILE_BUF_LEN];
329         uint32_t readlen = sizeof(buf);
330         int res = -1;
331         size_t len;
332         
333         if ((ff->flags & BLOCK_FLAG_HAVEHDR) == 0) {
334                 res = 0;
335                 goto out;
336         }
337         
338         errno = 0;
339         f = fopen(ff->name,"r");
340         if (errno) {
341                 errmsg(1,"unable to open file %s", ff->name);
342                 goto out;
343         }
344         
345         ff->crc = 0;
346         len = ff->size;
347         while (len > 0) {
348                 if (len < readlen)
349                         readlen = len;
350
351                 errno = 0;
352                 fread(buf, readlen, 1, f);
353                 if (errno) {
354                         errmsg(1,"unable to read from file %s", ff->name);
355                         goto out_close;
356                 }
357
358                 update_crc(buf, readlen, &ff->crc);
359                 len -= readlen;
360         }
361
362         res = 0;
363         
364 out_close:
365         fclose(f);
366 out:
367         return res;
368 }
369
370
371 int
372 process_files(void)
373 {
374         struct fw_block *b;
375         struct stat st;
376         int i;
377
378         for (i = 0; i < fw_num_blocks; i++) {
379                 b = &fw_blocks[i];
380                 if ((b->addr + b->blocklen) > flash_size) {
381                         errmsg(0, "block at 0x%08X is too big", b->addr);
382                         return -1;
383                 }
384                 if (b->name == NULL)
385                         continue;
386
387                 if (stat(b->name, &st) < 0) {
388                         errmsg(0, "stat failed on %s",b->name);
389                         return -1;
390                 }
391                 if (b->blocklen == 0) {
392                         b->blocklen = flash_size - b->addr;
393                 }
394                 if (st.st_size > b->blocklen) {
395                         errmsg(0,"file %s is too big",b->name);
396                         return -1;
397                 }
398
399                 b->size = st.st_size;
400         }
401
402         return 0;
403 }
404
405
406 int
407 process_partitions(void)
408 {
409         struct mylo_partition *part;
410         int i;
411
412         for (i = 0; i < fw_num_partitions; i++) {
413                 part = &fw_partitions[i];
414
415                 if (part->addr > flash_size) {
416                         errmsg(0, "invalid partition at 0x%08X", part->addr);
417                         return -1;
418                 }
419
420                 if ((part->addr + part->size) > flash_size) {
421                         errmsg(0, "partition at 0x%08X is too big", part->addr);
422                         return -1;
423                 }
424         }
425
426         return 0;
427 }
428
429
430 /*
431  * routines to write data to the output file
432  */
433 int
434 write_out_data(FILE *outfile, uint8_t *data, size_t len, uint32_t *crc)
435 {
436         errno = 0;
437
438         fwrite(data, len, 1, outfile);
439         if (errno) {
440                 errmsg(1,"unable to write output file");
441                 return -1;
442         }
443
444         if (crc) {
445                 update_crc(data, len, crc);
446         }
447
448         return 0;
449 }
450
451
452 inline int
453 write_out_desc(FILE *outfile, struct mylo_fw_blockdesc *desc, uint32_t *crc)
454 {
455         return write_out_data(outfile, (uint8_t *)desc,
456                 sizeof(*desc), crc);
457 }
458
459
460 int
461 write_out_file(FILE *outfile, struct fw_block *block, uint32_t *crc)
462 {
463         char buff[FILE_BUF_LEN];
464         size_t  buflen = sizeof(buff);
465         FILE *f;
466         size_t len;
467
468         errno = 0;
469         
470         if (block->name == NULL) {
471                 return 0;
472         }
473         
474         if ((block->flags & BLOCK_FLAG_HAVEHDR) != 0) {
475                 struct mylo_partition_header ph;
476                 
477                 if (get_file_crc(block) != 0)
478                         return -1;
479                 
480                 ph.crc = HOST_TO_LE32(block->crc);
481                 ph.len = HOST_TO_LE32(block->size);
482                 
483                 if (write_out_data(outfile, (uint8_t *)&ph, sizeof(ph), crc) != 0)
484                         return -1;
485         }
486         
487         f = fopen(block->name,"r");
488         if (errno) {
489                 errmsg(1,"unable to open file: %s", block->name);
490                 return -1;
491         }
492
493         len = block->size;
494         while (len > 0) {
495                 if (len < buflen)
496                         buflen = len;
497
498                 // read data from source file
499                 errno = 0;
500                 fread(buff, buflen, 1, f);
501                 if (errno != 0) {
502                         errmsg(1,"unable to read from file: %s",block->name);
503                         return -1;
504                 }
505
506                 if (write_out_data(outfile, buff, buflen, crc) != 0)
507                         return -1;
508                         
509                 len -= buflen;
510         }
511
512         fclose(f);
513         
514         dbgmsg(1,"file %s written out", block->name);
515         return 0;
516 }
517
518
519 int
520 write_out_header(FILE *outfile, uint32_t *crc)
521 {
522         struct mylo_fw_header hdr;
523
524         memset(&hdr, 0, sizeof(hdr));
525
526         hdr.magic = HOST_TO_LE32(MYLO_MAGIC_FIRMWARE);
527         hdr.crc = HOST_TO_LE32(fw_header.crc);
528         hdr.vid = HOST_TO_LE16(fw_header.vid);
529         hdr.did = HOST_TO_LE16(fw_header.did);
530         hdr.svid = HOST_TO_LE16(fw_header.svid);
531         hdr.sdid = HOST_TO_LE16(fw_header.sdid);
532         hdr.rev = HOST_TO_LE32(fw_header.rev);
533         hdr.fwhi = HOST_TO_LE32(fw_header.fwhi);
534         hdr.fwlo = HOST_TO_LE32(fw_header.fwlo);
535         hdr.flags = HOST_TO_LE32(fw_header.flags);
536
537         if (fseek(outfile, 0, SEEK_SET) != 0) {
538                 errmsg(1,"fseek failed on output file");
539                 return -1;
540         }
541
542         return write_out_data(outfile, (uint8_t *)&hdr, sizeof(hdr), crc);
543 }
544
545
546 int
547 write_out_partitions(FILE *outfile, uint32_t *crc)
548 {
549         struct mylo_partition_table p;
550         struct mylo_partition *p1, *p2;
551         int i;
552
553         if (fw_num_partitions == 0)
554                 return 0;
555
556         memset(&p, 0, sizeof(p));
557
558         p.magic = HOST_TO_LE32(MYLO_MAGIC_PARTITIONS);
559         for (i = 0; i < fw_num_partitions; i++) {
560                 p1 = &p.partitions[i];
561                 p2 = &fw_partitions[i];
562                 p1->flags = HOST_TO_LE16(p2->flags);
563                 p1->type = HOST_TO_LE16(PARTITION_TYPE_USED);
564                 p1->addr = HOST_TO_LE32(p2->addr);
565                 p1->size = HOST_TO_LE32(p2->size);
566                 p1->param = HOST_TO_LE32(p2->param);
567         }
568
569         return write_out_data(outfile, (uint8_t *)&p, sizeof(p), crc);
570 }
571
572
573 int
574 write_out_blocks(FILE *outfile, uint32_t *crc)
575 {
576         struct mylo_fw_blockdesc desc;
577         struct fw_block *b;
578         int i;
579
580         /*
581          * if at least one partition specified, write out block descriptor
582          * for the partition table
583          */
584         if (fw_num_partitions > 0) {
585
586                 desc.type = HOST_TO_LE32(FW_DESC_TYPE_USED);
587                 desc.addr = HOST_TO_LE32(0x10000);
588                 desc.dlen = HOST_TO_LE32(sizeof(struct mylo_partition_table));
589                 desc.blen = HOST_TO_LE32(0x10000);
590
591                 if (write_out_desc(outfile, &desc, crc) != 0)
592                         return -1;
593         }
594
595         /*
596          * write out block descriptors for each files
597          */
598         for (i = 0; i < fw_num_blocks; i++) {
599                 b = &fw_blocks[i];
600                 desc.type = HOST_TO_LE32(FW_DESC_TYPE_USED);
601                 desc.addr = HOST_TO_LE32(b->addr);
602                 desc.blen = HOST_TO_LE32(b->blocklen);
603                 if ((b->flags & BLOCK_FLAG_HAVEHDR) != 0) {
604                         desc.dlen = HOST_TO_LE32(b->size +
605                                 sizeof(struct mylo_partition_header));
606                 } else {
607                         desc.dlen = HOST_TO_LE32(b->size);
608                 }
609                 if (write_out_desc(outfile, &desc, crc) != 0)
610                         return -1;
611         }
612
613         /*
614          * write out the null block descriptor
615          */
616         memset(&desc, 0, sizeof(desc));
617         if (write_out_desc(outfile, &desc, crc) != 0)
618                 return -1;
619
620         if (write_out_partitions(outfile, crc) != 0)
621                 return -1;
622
623         /*
624          * write out data for each blocks
625          */
626         for (i = 0; i < fw_num_blocks; i++) {
627                 b = &fw_blocks[i];
628                 if (write_out_file(outfile, b, crc) != 0)
629                         return -1;
630         }
631
632         return 0;
633 }
634
635
636 /*
637  * argument parsing
638  */
639 int
640 parse_arg(char *arg, char *buf, char *argv[])
641 {
642         int res = 0;
643         size_t argl;
644         char *tok;
645         char **ap = &buf;
646         int i;
647
648         if ((arg == NULL)) {
649                 /* invalid argument string */
650                 return -1;
651         }
652
653         argl = strlen(arg);
654         if (argl == 0) {
655                 /* no arguments */
656                 return res;
657         }
658
659         if (argl >= MAX_ARG_LEN) {
660                 /* argument is too long */
661                 argl = MAX_ARG_LEN-1;
662         }
663
664         memset(argv, 0, MAX_ARG_COUNT * sizeof(void *));
665         memcpy(buf, arg, argl);
666         buf[argl] = '\0';
667
668         for (i = 0; i < MAX_ARG_COUNT; i++) {
669                 tok = strsep(ap, ":");
670                 if (tok == NULL) {
671                         break;
672                 }
673 #if 0
674                 else if (tok[0] == '\0') {
675                         break;
676                 }
677 #endif
678                 argv[i] = tok;
679                 res++;
680         }
681
682         return res;
683 }
684
685
686 int
687 required_arg(char c, char *arg)
688 {
689         if ((optarg != NULL) && (*arg == '-')){
690                 errmsg(0,"option %c requires an argument\n", c);
691                 return -1;
692         }
693         
694         return 0;
695 }
696
697
698 int
699 is_empty_arg(char *arg)
700 {
701         int ret = 1;
702         if (arg != NULL) {
703                 if (*arg) ret = 0;
704         };
705         return ret;
706 }
707
708
709 int
710 parse_opt_flags(char ch, char *arg)
711 {
712         if (required_arg(ch, arg)) {
713                 goto err_out;
714         }
715
716         if (str2u32(arg, &fw_header.flags) != 0) {
717                 errmsg(0,"invalid firmware flags: %s", arg);
718                 goto err_out;
719         }
720         
721         dbgmsg(1, "firmware flags set to %X bytes", fw_header.flags);
722
723         return 0;
724
725 err_out:
726         return -1;
727 }
728
729
730 int
731 parse_opt_size(char ch, char *arg)
732 {
733         if (required_arg(ch, arg)) {
734                 goto err_out;
735         }
736
737         if (str2u32(arg, &flash_size) != 0) {
738                 errmsg(0,"invalid flash size: %s", arg);
739                 goto err_out;
740         }
741         
742         dbgmsg(1, "flash size set to %d bytes", flash_size);
743
744         return 0;
745
746 err_out:
747         return -1;
748 }
749
750
751 int
752 parse_opt_id(char ch, char *arg)
753 {
754         char buf[MAX_ARG_LEN];
755         char *argv[MAX_ARG_COUNT];
756         int argc;
757         char *p;
758
759         if (required_arg(ch, arg)) {
760                 goto err_out;
761         }
762
763         argc = parse_arg(arg, buf, argv);
764
765         /* processing vendor ID*/
766         p = argv[0];
767         if (is_empty_arg(p)) {
768                 errmsg(0,"vendor id is missing from -%c %s",ch, arg);
769                 goto err_out;
770         } else if (str2u16(p, &fw_header.vid) != 0) {
771                 errmsg(0,"invalid vendor id: %s", p);
772                 goto err_out;
773         }
774
775         dbgmsg(1, "vendor id is set to 0x%04X", fw_header.vid);
776
777         /* processing device ID*/
778         p = argv[1];
779         if (is_empty_arg(p)) {
780                 errmsg(0,"device id is missing from -%c %s",ch, arg);
781                 goto err_out;
782         } else if (str2u16(p, &fw_header.did) != 0) {
783                 errmsg(0,"invalid device id: %s", p);
784                 goto err_out;
785         }
786
787         dbgmsg(1, "device id is set to 0x%04X", fw_header.did);
788
789         /* processing sub vendor ID*/
790         p = argv[2];
791         if (is_empty_arg(p)) {
792                 fw_header.svid = fw_header.vid;
793         } else if (str2u16(p, &fw_header.svid) != 0) {
794                 errmsg(0,"invalid sub vendor id: %s", p);
795                 goto err_out;
796         }
797
798         dbgmsg(1, "sub vendor id is set to 0x%04X", fw_header.svid);
799
800         /* processing device ID*/
801         p = argv[3];
802         if (is_empty_arg(p)) {
803                 fw_header.sdid = fw_header.did;
804         } else if (str2u16(p, &fw_header.sdid) != 0) {
805                 errmsg(0,"invalid sub device id: %s", p);
806                 goto err_out;
807         }
808
809         dbgmsg(1, "sub device id is set to 0x%04X", fw_header.sdid);
810
811         /* processing revision */
812         p = argv[4];
813         if (is_empty_arg(p)) {
814                 fw_header.rev = 0;
815         } else if (str2u32(arg, &fw_header.rev) != 0) {
816                 errmsg(0,"invalid revision number: %s", p);
817                 goto err_out;
818         }
819
820         dbgmsg(1, "board revision is set to 0x%08X", fw_header.rev);
821
822         return 0;
823
824 err_out:
825         return -1;
826 }
827
828
829 int
830 parse_opt_block(char ch, char *arg)
831 {
832         char buf[MAX_ARG_LEN];
833         char *argv[MAX_ARG_COUNT];
834         int argc;
835         struct fw_block *b;
836         char *p;
837
838         if (required_arg(ch, arg)) {
839                 goto err_out;
840         }
841
842         if (fw_num_blocks >= MAX_FW_BLOCKS) {
843                 errmsg(0,"too many blocks specified");
844                 goto err_out;
845         }
846
847         argc = parse_arg(arg, buf, argv);
848         dbgmsg(1,"processing block option %s, count %d", arg, argc);
849
850         b = &fw_blocks[fw_num_blocks++];
851
852         /* processing block address */
853         p = argv[0];
854         if (is_empty_arg(p)) {
855                 errmsg(0,"no block address specified in %s", arg);
856                 goto err_out;
857         } else if (str2u32(p, &b->addr) != 0) {
858                 errmsg(0,"invalid block address: %s", p);
859                 goto err_out;
860         }
861
862         /* processing block length */
863         p = argv[1];
864         if (is_empty_arg(p)) {
865                 errmsg(0,"no block length specified in %s", arg);
866                 goto err_out;
867         } else if (str2u32(p, &b->blocklen) != 0) {
868                 errmsg(0,"invalid block length: %s", p);
869                 goto err_out;
870         }
871         
872         if (argc < 3) {
873                 dbgmsg(1,"empty block %s", arg);
874                 goto success;
875         }
876         
877         /* processing flags */
878         p = argv[2];
879         if (is_empty_arg(p) == 0) {
880                 for ( ; *p != '\0'; p++) {
881                         switch (*p) {
882                         case 'h':
883                                 b->flags |= BLOCK_FLAG_HAVEHDR;
884                                 break;
885                         default:
886                                 errmsg(0, "invalid block flag \"%c\"", *p);
887                                 goto err_out;
888                         }
889                 }
890         }
891
892         /* processing file name */
893         p = argv[3];
894         if (is_empty_arg(p)) {
895                 errmsg(0,"file name missing in %s", arg);
896                 goto err_out;
897         }
898         
899         b->name = strdup(p);
900         if (b->name == NULL) {
901                 errmsg(0,"not enough memory");
902                 goto err_out;
903         }
904         
905 success:
906
907         return 0;
908         
909 err_out:
910         return -1;
911 }
912
913
914 int
915 parse_opt_partition(char ch, char *arg)
916 {
917         char buf[MAX_ARG_LEN];
918         char *argv[MAX_ARG_COUNT];
919         int argc;
920         char *p;
921
922         struct mylo_partition *part;
923
924         if (required_arg(ch, arg)) {
925                 goto err_out;
926         }
927
928         if (fw_num_partitions >= MYLO_MAX_PARTITIONS) {
929                 errmsg(0, "too many partitions specified");
930                 goto err_out;
931         }
932         
933         part = &fw_partitions[fw_num_partitions++];
934
935         argc = parse_arg(arg, buf, argv);
936
937         /* processing partition address */
938         p = argv[0];
939         if (is_empty_arg(p)) {
940                 errmsg(0,"partition address missing in -%c %s",ch, arg);
941                 goto err_out;
942         } else if (str2u32(p, &part->addr) != 0) {
943                 errmsg(0,"invalid partition address: %s", p);
944                 goto err_out;
945         }
946
947         /* processing partition size */
948         p = argv[1];
949         if (is_empty_arg(p)) {
950                 errmsg(0,"partition size missing in -%c %s",ch, arg);
951                 goto err_out;
952         } else if (str2u32(p, &part->size) != 0) {
953                 errmsg(0,"invalid partition size: %s", p);
954                 goto err_out;
955         }
956
957         /* processing partition flags */
958         p = argv[2];
959         if (is_empty_arg(p) == 0) {
960                 for ( ; *p != '\0'; p++) {
961                         switch (*p) {
962                         case 'a':
963                                 part->flags |= PARTITION_FLAG_ACTIVE;
964                                 break;
965                         case 'p':
966                                 part->flags |= PARTITION_FLAG_PRELOAD;
967                                 break;
968                         case 'h':
969                                 part->flags |= PARTITION_FLAG_HAVEHDR;
970                                 break;
971                         default:
972                                 errmsg(0, "invalid partition flag \"%c\"", *p);
973                                 goto err_out;
974                         }
975                 }
976         }
977
978         /* processing partition parameter */
979         p = argv[3];
980         if (is_empty_arg(p)) {
981                 /* set default partition parameter */
982                 part->param = 0;
983         } else if (str2u32(p, &part->param) != 0) {
984                 errmsg(0,"invalid partition parameter: %s", p);
985                 goto err_out;
986         }
987
988 #if 1
989         if (part->size == 0) {
990                 part->size = flash_size - part->addr;
991         }
992
993         /* processing file parameter */
994         p = argv[4];
995         if (is_empty_arg(p) == 0) {
996                 struct fw_block *b;
997                 
998                 if (fw_num_blocks == MAX_FW_BLOCKS) {
999                         errmsg(0,"too many blocks specified", p);
1000                         goto err_out;
1001                 }
1002                 b = &fw_blocks[fw_num_blocks++];
1003                 b->name = strdup(p);
1004                 b->addr = part->addr;
1005                 b->blocklen = part->size;
1006                 if (part->flags & PARTITION_FLAG_HAVEHDR) {
1007                         b->flags |= BLOCK_FLAG_HAVEHDR;
1008                 }
1009         }
1010 #endif
1011
1012         return 0;
1013
1014 err_out:
1015         return -1;
1016 }
1017
1018
1019 int
1020 parse_opt_board(char ch, char *arg)
1021 {
1022         struct cpx_board *board;
1023         
1024         if (required_arg(ch, arg)) {
1025                 goto err_out;
1026         }
1027         
1028         board = find_board(arg);
1029         if (board == NULL){
1030                 errmsg(0,"invalid/unknown board specified: %s", arg);
1031                 goto err_out;
1032         }
1033         
1034         fw_header.vid = board->vid;
1035         fw_header.did = board->did;
1036         fw_header.svid = board->svid;
1037         fw_header.sdid = board->sdid;
1038         
1039         flash_size = board->flash_size;
1040
1041         return 0;
1042         
1043 err_out:
1044         return -1;
1045 }
1046
1047
1048 int
1049 parse_opt_rev(char ch, char *arg)
1050 {
1051         if (required_arg(ch, arg)) {
1052                 return -1;
1053         }
1054
1055         if (str2u32(arg, &fw_header.rev) != 0) {
1056                 errmsg(0,"invalid revision number: %s", arg);
1057                 return -1;
1058         }
1059
1060         return 0;
1061 }
1062
1063
1064 /*
1065  * main
1066  */
1067 int
1068 main(int argc, char *argv[])
1069 {
1070         int optinvalid = 0;   /* flag for invalid option */
1071         int c;
1072         int res = EXIT_FAILURE;
1073
1074         FILE  *outfile;
1075         uint32_t crc;
1076         
1077         progname=basename(argv[0]);
1078         
1079         memset(&fw_header, 0, sizeof(fw_header));
1080         
1081         /* init header defaults */
1082         fw_header.vid = VENID_COMPEX;
1083         fw_header.did = DEVID_COMPEX_WP54G;
1084         fw_header.svid = VENID_COMPEX;
1085         fw_header.sdid = DEVID_COMPEX_WP54G;
1086         fw_header.fwhi = 0x20000;
1087         fw_header.fwlo = 0x20000;
1088         fw_header.flags = 0;
1089
1090         opterr = 0;  /* could not print standard getopt error messages */
1091         while ((c = getopt(argc, argv, "b:B:f:hi:p:r:s:v")) != -1) {
1092                 optinvalid = 0;
1093                 switch (c) {
1094                 case 'b':
1095                         optinvalid = parse_opt_block(c,optarg);
1096                         break;
1097                 case 'B':
1098                         optinvalid = parse_opt_board(c,optarg);
1099                         break;
1100                 case 'f':
1101                         optinvalid = parse_opt_flags(c,optarg);
1102                         break;
1103                 case 'h':
1104                         usage(EXIT_SUCCESS);
1105                         break;
1106                 case 'i':
1107                         optinvalid = parse_opt_id(c,optarg);
1108                         break;
1109                 case 'p':
1110                         optinvalid = parse_opt_partition(c,optarg);
1111                         break;
1112                 case 'r':
1113                         optinvalid = parse_opt_rev(c,optarg);
1114                         break;
1115                 case 's':
1116                         optinvalid = parse_opt_size(c,optarg);
1117                         break;
1118                 case 'v':
1119                         verblevel++;
1120                         break;
1121                 default:
1122                         optinvalid = 1;
1123                         break;
1124                 }
1125                 if (optinvalid != 0 ){
1126                         errmsg(0, "invalid option: -%c", optopt);
1127                         goto out;
1128                 }
1129         }
1130         
1131         if (optind == argc) {
1132                 errmsg(0, "no output file specified");
1133                 goto out;
1134         }
1135
1136         ofname = argv[optind++];
1137         
1138         if (optind < argc) {
1139                 errmsg(0, "invalid option: %s", argv[optind]);
1140                 goto out;
1141         }
1142         
1143         if (flash_size == 0) {
1144                 errmsg(0, "no flash size specified");
1145                 goto out;
1146         }
1147         
1148         if (process_files() != 0) {
1149                 goto out;
1150         }
1151
1152         if (process_partitions() != 0) {
1153                 goto out;
1154         }
1155
1156         outfile = fopen(ofname, "w");
1157         if (outfile == NULL) {
1158                 errmsg(1, "could not open \"%s\" for writing", ofname);
1159                 goto out;
1160         }
1161
1162         crc = 0;
1163         init_crc_table();
1164
1165         if (write_out_header(outfile, &crc) != 0)
1166                 goto out_flush;
1167                 
1168         if (write_out_blocks(outfile, &crc) != 0)
1169                 goto out_flush;
1170
1171         fw_header.crc = crc;
1172         if (write_out_header(outfile, NULL) != 0)
1173                 goto out_flush;
1174
1175         dbgmsg(1,"Firmware file %s completed.", ofname);
1176
1177         res = EXIT_SUCCESS;
1178
1179 out_flush:
1180         fflush(outfile);
1181         fclose(outfile);
1182         if (res != EXIT_SUCCESS) {
1183                 unlink(ofname);
1184         }
1185 out:
1186         return res;
1187 }