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