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