update to a newer snapshot and m. buesch snapshots
[openwrt.git] / target / linux / package / bcm43xx-standalone / fwcutter / fwcutter.c
1 /*
2  * firmware cutter for broadcom 43xx wireless driver files
3  * 
4  * Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
5  *               2005 Michael Buesch <mbuesch@freenet.de>
6  *               2005 Alex Beregszaszi
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; see the file COPYING.  If not, write to
20  * the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  */
23
24
25
26 #include <stdlib.h>
27 #include <ctype.h>
28 #include <string.h>
29 #include <stdio.h>
30
31 typedef unsigned char byte;
32
33 #define DRIVER_UNSUPPORTED       0x01  /* no support for this driver file */
34 #define BYTE_ORDER_BIG_ENDIAN    0x02  /* ppc driver files */
35 #define BYTE_ORDER_LITTLE_ENDIAN 0x04  /* x86, mips driver files */
36
37 #define MISSING_INITVAL_08       0x10  /* initval 8 is missing */
38 #define MISSING_INITVAL_80211_A  0x20  /* initvals 3,7,9,10 (802.11a cards) are empty */
39
40 #define FIRMWARE_UCODE_OFFSET    100
41 #define FIRMWARE_UNDEFINED       0
42 #define FIRMWARE_PCM_4           4
43 #define FIRMWARE_PCM_5           5
44 #define FIRMWARE_UCODE_2         (FIRMWARE_UCODE_OFFSET + 2)
45 #define FIRMWARE_UCODE_4         (FIRMWARE_UCODE_OFFSET + 4)
46 #define FIRMWARE_UCODE_5         (FIRMWARE_UCODE_OFFSET + 5)
47 #define FIRMWARE_UCODE_11        (FIRMWARE_UCODE_OFFSET + 11)
48
49
50 #define fwcutter_stringify_1(x) #x
51 #define fwcutter_stringify(x)   fwcutter_stringify_1(x)
52 #define FWCUTTER_VERSION        fwcutter_stringify(FWCUTTER_VERSION_)
53
54 #include "md5.h"
55 #include "fwcutter_list.h"
56
57
58 struct cmdline_args {
59         const char *infile;
60         const char *postfix;
61         const char *target_dir;
62         int identify_only;
63 };
64
65 static struct cmdline_args cmdargs;
66 int big_endian_cpu;
67
68
69 static void write_little_endian(FILE *f, byte *buffer, int len) 
70 {
71         byte swapbuf[4];
72
73         while (len > 0) {
74                 swapbuf[0] = buffer[3]; swapbuf[1] = buffer[2];
75                 swapbuf[2] = buffer[1]; swapbuf[3] = buffer[0];
76                 fwrite(swapbuf, 4, 1, f);
77                 buffer = buffer + 4;
78                 len  = len - 4;
79         }
80 }
81
82 static void write_big_endian(FILE *f, byte *buffer, int len) 
83 {
84         while (len > 0) {
85                 fwrite(buffer, 4, 1, f);
86                 buffer = buffer + 4;
87                 len  = len - 4;
88         }
89 }
90
91 static void write_fw(const char *outfilename, uint8_t flags, byte *data, int len)
92 {
93         FILE* fw;
94         char outfile[2048];
95
96         snprintf(outfile, sizeof(outfile),
97                  "%s/%s", cmdargs.target_dir, outfilename);
98
99         fw = fopen(outfile, "w");
100         if (!fw) {
101                 perror(outfile);
102                 exit(1);
103         }
104
105         if (flags & BYTE_ORDER_LITTLE_ENDIAN)
106                 write_little_endian(fw, data, len);
107         else if (flags & BYTE_ORDER_BIG_ENDIAN)
108                 write_big_endian(fw, data, len);
109         else
110                 printf("unknown byteorder...\n");
111
112         fflush(fw);
113         fclose(fw);
114 }
115
116 static void write_iv(uint8_t flags, byte *data)
117 {
118         FILE* fw;
119         char ivfilename[2048];
120         int i;
121
122         for (i = 1; i <= 10; i++) {
123
124                 if ((flags & MISSING_INITVAL_08) && (i==8)) {
125                         printf("*****: Sorry, initval08 is not available in driver file \"%s\".\n", cmdargs.infile);
126                         printf("*****: Extracting firmware from an old driver is bad. Choose a more recent one.\n");
127                         printf("*****: Luckily bcm43xx driver doesn't include initval08 uploads at the moment.\n");
128                         printf("*****: But this can be added in the future...\n");
129                         i++;
130                 }
131
132                 snprintf(ivfilename, sizeof(ivfilename),
133                          "%s/bcm43xx_initval%02d%s.fw",
134                          cmdargs.target_dir, i, cmdargs.postfix);
135                 fw = fopen(ivfilename, "w");
136
137                 if (!fw) {
138                         perror(ivfilename);
139                         exit(1);
140                 }
141
142                 printf("extracting bcm43xx_initval%02d%s.fw ...\n", i, cmdargs.postfix);
143
144                 while (1) {
145
146                         if ((data[0]==0xff) && (data[1]==0xff) && (data[2]==0x00) && (data[3]==0x00)) {
147                                 data = data + 8;
148                                 break;
149                         }
150
151                         if (flags & BYTE_ORDER_LITTLE_ENDIAN)
152                                 fprintf(fw, "%c%c%c%c%c%c%c%c",
153                                         data[1], data[0],                       /* offset */
154                                         data[3], data[2],                       /* size */
155                                         data[7], data[6], data[5], data[4]);    /* value */
156                         else if (flags & BYTE_ORDER_BIG_ENDIAN)
157                                 fprintf(fw, "%c%c%c%c%c%c%c%c",
158                                         data[0], data[1],                       /* offset */
159                                         data[2], data[3],                       /* size */
160                                         data[4], data[5], data[6], data[7]);    /* value */
161                         else {
162                                 printf("unknown byteorder...\n");
163                                 exit(1);
164                         }
165
166                         data = data + 8;
167                 }
168                 fflush(fw);
169                 fclose(fw);
170         }
171 }
172
173 static byte* read_file(const char* filename)
174 {
175         FILE* file;
176         long len;
177         byte* data;
178
179         file = fopen(filename, "rb");
180         if (!file) {
181                 perror(filename);
182                 exit(1);
183         }
184         if (fseek(file, 0, SEEK_END)) {
185                 perror("cannot seek");
186                 exit(1);
187         }
188         len = ftell(file);
189         fseek(file, 0, SEEK_SET);
190         data = malloc(len);
191         if (!data) {
192                 fputs("out of memory\n", stderr);
193                 exit(1);
194         }
195         if (fread(data, 1, len, file) != len) {
196                 perror("cannot read");
197                 exit(1);
198         }
199         fclose(file);
200         return data;
201 }
202
203 static void extract_fw(uint8_t fwtype, uint8_t flags, uint32_t pos, uint32_t length)
204 {
205         byte* filedata;
206         char outfile[1024];
207
208         switch (fwtype) {
209         case FIRMWARE_UCODE_2:
210         case FIRMWARE_UCODE_4:
211         case FIRMWARE_UCODE_5:
212         case FIRMWARE_UCODE_11:
213                 snprintf(outfile, sizeof(outfile), "bcm43xx_microcode%i%s.fw", 
214                          fwtype - FIRMWARE_UCODE_OFFSET, cmdargs.postfix);
215                 break;
216         case FIRMWARE_PCM_4:
217         case FIRMWARE_PCM_5:
218                 snprintf(outfile, sizeof(outfile), "bcm43xx_pcm%i%s.fw", 
219                          fwtype, cmdargs.postfix);
220                 break;
221         default:
222                 snprintf(outfile, sizeof(outfile), "bcm43xx_unknown.fw");
223         }
224
225         if (length > 0) {
226                 printf("extracting %s ...\n", outfile);
227                 filedata = read_file(cmdargs.infile);
228                 write_fw(outfile, flags, filedata + pos, length);
229                 free(filedata);
230         } else {
231                 printf("*****: Sorry, it's not posible to extract \"%s\".\n", outfile);
232                 printf("*****: Extracting firmware from an old driver is bad. Choose a more recent one.\n");
233
234                 switch (fwtype) {
235                 case FIRMWARE_UCODE_2:
236                         printf("*****: bcm43xx driver will not work with with core revision 2.\n");
237                         break;
238                 case FIRMWARE_UCODE_4:
239                         printf("*****: bcm43xx driver will not work with with core revision 4.\n");
240                         break;
241                 case FIRMWARE_UCODE_5:
242                         printf("*****: bcm43xx driver will not work with with core revision 5 or higher.\n");
243                         break;
244                 case FIRMWARE_UCODE_11:
245                         printf("*****: Luckily bcm43xx driver doesn't include microcode11 uploads at the moment.\n");
246                         printf("*****: But this can be added in the future...\n");
247                         break;
248                 case FIRMWARE_PCM_4:
249                         printf("*****: bcm43xx driver will not work with with core revision 4 or smaller.\n");
250                         break;
251                 case FIRMWARE_PCM_5:
252                         printf("*****: bcm43xx driver will not work with with core revision 5 or higher.\n");
253                         break;
254                 }
255         }
256 }
257
258 static void extract_iv(uint8_t flags, uint32_t pos)
259 {
260         byte* filedata;
261
262         if (pos > 0) {
263                 filedata = read_file(cmdargs.infile);
264                 write_iv(flags, filedata + pos);
265                 free(filedata);
266         }
267 }
268
269 static void print_banner(void)
270 {
271         printf("fwcutter " FWCUTTER_VERSION "\n");
272 }
273
274 static void print_file(const struct file *file)
275 {
276         printf("%s\t", file->name);
277         if (strlen(file->name) < 8)
278                 printf("\t");
279
280         printf("%s\t", file->version);
281         if (strlen(file->version) < 8)
282                 printf("\t");
283         if (strlen(file->version) < 16)
284                 printf("\t");
285
286         if (!(file->flags & DRIVER_UNSUPPORTED)) {
287                 if (file->flags & MISSING_INITVAL_80211_A)
288                         printf("b/g  ");
289                 else
290                         printf("a/b/g");
291         }
292
293         printf("  %s", file->md5);
294         printf("\n");
295 }
296
297 static void print_supported_files(void)
298 {
299         int i;
300
301         print_banner();
302         printf("\nExtracting firmware is possible from these binary driver files:\n\n");
303         printf("<filename>\t<version>\t       <802.11><MD5 checksum>\n\n");
304         for (i = 0; i < FILES; i++) {
305                 if (files[i].flags & DRIVER_UNSUPPORTED)
306                         continue;
307                 print_file(&files[i]);
308         }
309         printf("\n\nExtracting firmware is IMPOSSIBLE from these binary driver files:\n\n");
310         printf("<filename>\t<version>\t          <MD5 checksum>\n\n");
311         for (i = 0; i < FILES; i++) {
312                 if (!(files[i].flags & DRIVER_UNSUPPORTED))
313                         continue;
314                 print_file(&files[i]);
315         }
316 }
317
318 static const struct file * find_file(FILE *fd)
319 {
320         unsigned char buffer[16384], signature[16];
321         struct MD5Context md5c;
322         char md5sig[33];
323         int i;
324
325         MD5Init(&md5c);
326         while ((i = (int) fread(buffer, 1, sizeof(buffer), fd)) > 0)
327                 MD5Update(&md5c, buffer, (unsigned) i);
328         MD5Final(signature, &md5c);
329
330         snprintf(md5sig, sizeof(md5sig),
331                  "%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x",
332                  signature[0], signature[1], signature[2], signature[3],
333                  signature[4], signature[5], signature[6], signature[7],
334                  signature[8], signature[9], signature[10], signature[11],
335                  signature[12], signature[13], signature[14], signature[15]);
336
337         for (i = 0; i < FILES; ++i) {
338                 if (strcasecmp(md5sig, files[i].md5) == 0) {
339                         if (files[i].flags & DRIVER_UNSUPPORTED) {
340                                 printf("Extracting firmware from this file is IMPOSSIBLE. (too old)\n");
341                                 return 0;
342                         }
343                         printf("fwcutter can cut the firmware out of %s\n", cmdargs.infile);
344                         printf("  filename :  %s\n", files[i].name);
345                         printf("  version  :  %s\n", files[i].version);
346                         printf("  MD5      :  %s\n\n", files[i].md5);
347                         if (files[i].flags & MISSING_INITVAL_80211_A) {
348                                 printf("WARNING! This firmware doesn't include support for 802.11a cards.\n");
349                                 printf("WARNING! Use this firmware only for 802.11b/g cards.\n\n");
350                         }
351                         return &(files[i]);
352                 }
353         }
354         printf("Sorry, the input file is either wrong or not supported by fwcutter.\n");
355         printf("I can't find the MD5sum %s :(\n", md5sig);
356
357         return 0;
358 }
359
360 static void get_endianess(void)
361 {
362         const unsigned char x[] = { 0xde, 0xad, 0xbe, 0xef, };
363         const uint32_t *p = (uint32_t *)x;
364
365         if (*p == 0xdeadbeef) {
366                 big_endian_cpu = 1;
367         } else if (*p == 0xefbeadde) {
368                 big_endian_cpu = 0;
369         } else {
370                 printf("Confused: NUXI endian machine??\n");
371                 exit(-1);
372         }
373 }
374
375 static void print_usage(int argc, char *argv[])
376 {
377         print_banner();
378         printf("\nUsage: %s [OPTION] [driver.sys]\n", argv[0]);
379         printf("  -l|--list             List supported driver versions\n");
380         printf("  -i|--identify         Only identify the driver file (don't extract)\n");
381         printf("  -w|--target-dir DIR   Extract and write firmware to DIR\n");
382         printf("  -p|--postfix \".FOO\"   Postfix for firmware filenames (.FOO.fw)\n");
383         printf("  -v|--version          Print fwcutter version\n");
384         printf("  -h|--help             Print this help\n");
385         printf("\nExample: %s bcmwl5.sys\n"
386                "         to extract the firmware blobs from bcmwl5.sys\n", argv[0]);
387 }
388
389 #define ARG_MATCH       0
390 #define ARG_NOMATCH     1
391 #define ARG_ERROR       -1
392
393 static int do_cmp_arg(char **argv, int *pos,
394                       const char *template,
395                       int allow_merged,
396                       char **param)
397 {
398         char *arg;
399         char *next_arg;
400         size_t arg_len, template_len;
401
402         arg = argv[*pos];
403         next_arg = argv[*pos + 1];
404         arg_len = strlen(arg);
405         template_len = strlen(template);
406
407         if (param) {
408                 /* Maybe we have a merged parameter here.
409                  * A merged parameter is "-pfoobar" for example.
410                  */
411                 if (allow_merged && arg_len > template_len) {
412                         if (memcmp(arg, template, template_len) == 0) {
413                                 *param = arg + template_len;
414                                 return ARG_MATCH;
415                         }
416                         return ARG_NOMATCH;
417                 } else if (arg_len != template_len)
418                         return ARG_NOMATCH;
419                 *param = next_arg;
420         }
421         if (strcmp(arg, template) == 0) {
422                 if (param) {
423                         /* Skip the parameter on the next iteration. */
424                         (*pos)++;
425                         if (*param == 0) {
426                                 printf("%s needs a parameter\n", arg);
427                                 return ARG_ERROR;
428                         }
429                 }
430                 return ARG_MATCH;
431         }
432
433         return ARG_NOMATCH;
434 }
435
436 /* Simple and lean command line argument parsing. */
437 static int cmp_arg(char **argv, int *pos,
438                    const char *long_template,
439                    const char *short_template,
440                    char **param)
441 {
442         int err;
443
444         if (long_template) {
445                 err = do_cmp_arg(argv, pos, long_template, 0, param);
446                 if (err == ARG_MATCH || err == ARG_ERROR)
447                         return err;
448         }
449         err = ARG_NOMATCH;
450         if (short_template)
451                 err = do_cmp_arg(argv, pos, short_template, 1, param);
452         return err;
453 }
454
455 static int parse_args(int argc, char *argv[])
456 {
457         int i, res;
458         char *param;
459
460         if (argc < 2)
461                 goto out_usage;
462         for (i = 1; i < argc; i++) {
463                 res = cmp_arg(argv, &i, "--list", "-l", 0);
464                 if (res == ARG_MATCH) {
465                         print_supported_files();
466                         return 1;
467                 } else if (res == ARG_ERROR)
468                         goto out;
469
470                 res = cmp_arg(argv, &i, "--version", "-v", 0);
471                 if (res == ARG_MATCH) {
472                         print_banner();
473                         return 1;
474                 } else if (res == ARG_ERROR)
475                         goto out;
476
477                 res = cmp_arg(argv, &i, "--help", "-h", 0);
478                 if (res == ARG_MATCH)
479                         goto out_usage;
480                 else if (res == ARG_ERROR)
481                         goto out;
482
483                 res = cmp_arg(argv, &i, "--identify", "-i", 0);
484                 if (res == ARG_MATCH) {
485                         cmdargs.identify_only = 1;
486                         continue;
487                 } else if (res == ARG_ERROR)
488                         goto out;
489
490                 res = cmp_arg(argv, &i, "--target-dir", "-w", &param);
491                 if (res == ARG_MATCH) {
492                         cmdargs.target_dir = param;
493                         continue;
494                 } else if (res == ARG_ERROR)
495                         goto out;
496
497                 res = cmp_arg(argv, &i, "--postfix", "-p", &param);
498                 if (res == ARG_MATCH) {
499                         cmdargs.postfix = param;
500                         continue;
501                 } else if (res == ARG_ERROR)
502                         goto out;
503
504                 cmdargs.infile = argv[i];
505                 break;
506         }
507
508         if (!cmdargs.infile)
509                 goto out_usage;
510         return 0;
511
512 out_usage:
513         print_usage(argc, argv);
514 out:
515         return -1;      
516 }
517
518 int main(int argc, char *argv[])
519 {
520         FILE *fd;
521         const struct file *file;
522         int err;
523
524         get_endianess();
525
526         cmdargs.target_dir = ".";
527         cmdargs.postfix = "";
528         err = parse_args(argc, argv);
529         if (err == 1)
530                 return 0;
531         else if (err != 0)
532                 return err;
533
534         fd = fopen(cmdargs.infile, "rb");
535         if (!fd) {
536                 fprintf(stderr, "Cannot open input file %s\n", cmdargs.infile);
537                 return 2;
538         }
539
540         err = -1;
541         file = find_file(fd);
542         if (!file)
543                 goto out_close;
544         if (cmdargs.identify_only) {
545                 err = 0;
546                 goto out_close;
547         }
548
549         extract_fw(FIRMWARE_UCODE_2, file->flags, file->uc2_pos, file->uc2_length);
550         extract_fw(FIRMWARE_UCODE_4, file->flags, file->uc4_pos, file->uc4_length);
551         extract_fw(FIRMWARE_UCODE_5, file->flags, file->uc5_pos, file->uc5_length);
552         extract_fw(FIRMWARE_UCODE_11, file->flags, file->uc11_pos, file->uc11_length);
553         extract_fw(FIRMWARE_PCM_4, file->flags, file->pcm4_pos, file->pcm4_length);
554         extract_fw(FIRMWARE_PCM_5, file->flags, file->pcm5_pos, file->pcm5_length);
555         extract_iv(file->flags, file->iv_pos);
556
557         err = 0;
558 out_close:
559         fclose(fd);
560
561         return err;
562 }