Initial commit.
authorMichael Buesch <mb@bu3sch.de>
Sat, 10 Nov 2007 22:12:26 +0000 (23:12 +0100)
committerMichael Buesch <mb@bu3sch.de>
Sat, 10 Nov 2007 22:12:26 +0000 (23:12 +0100)
Signed-off-by: Michael Buesch <mb@bu3sch.de>
38 files changed:
assembler/COPYING [new file with mode: 0644]
assembler/Makefile [new file with mode: 0644]
assembler/README [new file with mode: 0644]
assembler/args.c [new file with mode: 0644]
assembler/args.h [new file with mode: 0644]
assembler/bcm43xx-asm [new file with mode: 0755]
assembler/initvals.c [new file with mode: 0644]
assembler/initvals.h [new file with mode: 0644]
assembler/list.h [new file with mode: 0644]
assembler/main.c [new file with mode: 0644]
assembler/main.h [new file with mode: 0644]
assembler/parser.y [new file with mode: 0644]
assembler/scanner.l [new file with mode: 0644]
assembler/test.asm [new file with mode: 0644]
assembler/util.c [new file with mode: 0644]
assembler/util.h [new file with mode: 0644]
disassembler/COPYING [new file with mode: 0644]
disassembler/Makefile [new file with mode: 0644]
disassembler/bcm43xx-dasm [new file with mode: 0755]
disassembler/list.h [new file with mode: 0644]
disassembler/main.c [new file with mode: 0644]
disassembler/util.c [new file with mode: 0644]
disassembler/util.h [new file with mode: 0644]
fwcutter/Makefile [new file with mode: 0644]
fwcutter/README [new file with mode: 0644]
fwcutter/b43-fwcutter.1 [new file with mode: 0644]
fwcutter/fwcutter.c [new file with mode: 0644]
fwcutter/fwcutter.h [new file with mode: 0644]
fwcutter/fwcutter_list.h [new file with mode: 0644]
fwcutter/md5.c [new file with mode: 0644]
fwcutter/md5.h [new file with mode: 0644]
ssb_sprom/COPYING [new file with mode: 0644]
ssb_sprom/Makefile [new file with mode: 0644]
ssb_sprom/README [new file with mode: 0644]
ssb_sprom/ssb_sprom.c [new file with mode: 0644]
ssb_sprom/ssb_sprom.h [new file with mode: 0644]
ssb_sprom/utils.c [new file with mode: 0644]
ssb_sprom/utils.h [new file with mode: 0644]

diff --git a/assembler/COPYING b/assembler/COPYING
new file mode 100644 (file)
index 0000000..d511905
--- /dev/null
@@ -0,0 +1,339 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/assembler/Makefile b/assembler/Makefile
new file mode 100644 (file)
index 0000000..b9c3e66
--- /dev/null
@@ -0,0 +1,47 @@
+CC = gcc
+LEX = flex
+YACC = bison
+PREFIX = /usr/local
+CFLAGS = -std=c99 -O2 -fomit-frame-pointer -Wall -D_BSD_SOURCE -D_GNU_SOURCE
+LDFLAGS = -lfl
+
+BINARY = bcm43xx-asm.bin
+OBJECTS = parser.o scanner.o main.o initvals.o util.o args.o
+
+# YACC related CFLAGS
+CFLAGS += -DYYSTYPE="void *" -DYYERROR_VERBOSE -DYYDEBUG -Wno-unused
+
+all: $(BINARY)
+
+scanner.c: scanner.l parser.c main.h
+       $(LEX) -o scanner.c --header-file=scanner.h scanner.l
+
+scanner.o: scanner.c
+       $(CC) $(CFLAGS) -c -o scanner.o scanner.c
+
+parser.c: parser.y main.h util.h
+       $(YACC) --defines -o parser.c parser.y
+
+parser.o: parser.c
+       $(CC) $(CFLAGS) -c -o parser.o parser.c
+
+main.o: parser.c main.h list.h util.h args.h initvals.h
+
+initvals.o: initvals.h main.h list.h util.h args.h
+
+util.o: util.h
+
+args.o: args.h main.h util.h
+
+$(BINARY): $(OBJECTS)
+       $(CC) $(CFLAGS) -o $(BINARY) $(OBJECTS) $(LDFLAGS)
+
+install: all
+       -install -o 0 -g 0 -m 755 $(BINARY) $(PREFIX)/bin/
+       -cp bcm43xx-asm bcm43xx-asm.inst
+       -sed -i -e 's/installed=0/installed=1/' bcm43xx-asm.inst
+       -install -o 0 -g 0 -m 755 bcm43xx-asm.inst $(PREFIX)/bin/bcm43xx-asm
+       -rm -f bcm43xx-asm.inst
+
+clean:
+       -rm -f *~ *.o *.orig *.rej $(BINARY) scanner.c scanner.h parser.c parser.h
diff --git a/assembler/README b/assembler/README
new file mode 100644 (file)
index 0000000..6658803
--- /dev/null
@@ -0,0 +1,83 @@
+===============================================================================
+==        Instruction Set Summary for the core revision >= 5 microcode       ==
+===============================================================================
+
+
+===============================================================================
+Mnemonics | Operands   | Description                     | Operation
+===============================================================================
+
+Arithmetic and logic instructions:
+add       | A,B,rD     | Add                             | rD=A+B
+add.      | A,B,rD     | Add, set Carry                  | rD=A+B SaveCarry
+addc      | A,B,rD     | Add with Carry                  | rD=A+B+Carry
+addc.     | A,B,rD     | Add with Carry, set Carry       | rD=A+B+C SaveCarry
+sub       | A,B,rD     | Subtract                        | rD=A-B
+sub.      | A,B,rD     | Sub, set Carry                  | rD=A-B SaveCarry
+subc      | A,B,rD     | Sub with Carry                  | rD=A-B-Carry
+subc.     | A,B,rD     | Sub with Carry, set Carry       | rD=A-B-C SaveCarry
+
+Branch instructions:
+jmp       | l          | Unconditional jump              | PC=l
+jand      | A,B,l      | Jump if binary AND              | if(A&B) PC=l
+jnand     | A,B,l      | Jump if not binary AND          | if(!(A&B)) PC=l
+js        | A,B,l      | Jump if all bits set            | if((A&B)==A) PC=l
+jns       | A,B,l      | Jump if not all bits set        | if((A&B)!=A) PC=l
+je        | A,B,l      | Jump if equal                   | if(A==B) PC=l
+jne       | A,B,l      | Jump if not equal               | if(A!=B) PC=l
+jls       | A,B,l      | Jump if less (signed)           | if(A<B) PC=l
+jges      | A,B,l      | Jump if greater or equal (sign.)| if(A>=B) PC=l
+jgs       | A,B,l      | Jump if greater (signed)        | if(A>B) PC=l
+jles      | A,B,l      | Jump if less or equal (signed)  | if(A<=B) PC=l
+jl        | A,B,l      | Jump if less                    | if(A<B) PC=l
+jge       | A,B,l      | Jump if greater or equal        | if(A>=B) PC=l
+jg        | A,B,l      | Jump if greater                 | if(A>B) PC=l
+jle       | A,B,l      | Jump if less or equal           | if(A<=B) PC=l
+call      | lrX,l      | Store PC, call function         | lrX=PC; PC=l
+ret       | lrX,lrY    | Store PC, ret from func         | lrX=PC; PC=lrY
+jzx       | M,S,A,B,l  | Jump if zero after shift + mask |
+jnzx      | M,S,A,B,l  | Jump if nonzero after shift+msk |
+jext      | E,A,B,l    | Jump if External Condition true | if(E) PC=l
+jnext     | E,A,B,l    | Jump if External Condition false| if(!E) PC=l
+
+Data transfer instructions:
+mov       | A,rD       | Copy data                       | rD=A
+tkiph     | A,rD       | TKIP S-Box lookup high          | rD=SBOX[hi8(A)]
+tkiphs    | A,rD       | TKIP S-Box lkup hi swap'd       | rD=byteswap(tkiph)
+tkipl     | A,rD       | TKIP S-Box lookup low           | rD=SBOX[lo8(A)]
+tkipls    | A,rD       | TKIP S-Box lkup lo swap'd       | rD=byteswap(tkipl)
+
+Bitwise instructions:
+sra       | A,B,rD     | Arithmetic rightshift           | rD=A>>B fillup sign
+or        | A,B,rD     | Bitwise OR                      | rD=A|B
+and       | A,B,rD     | Bitwise AND                     | rD=A&B
+xor       | A,B,rD     | Bitwise XOR                     | rD=A^B
+sr        | A,B,rD     | Rightshift                      | rD=A>>B
+sl        | A,B,rD     | Leftshift                       | rD=A<<B
+srx       | M,S,A,B,rD | Extended right shift            |
+rl        | A,B,rD     | Rotate left                     | rD=lrot(A, B bits)
+rr        | A,B,rD     | Rotate right                    | rD=rrot(A, B bits)
+nand      | A,B,rD     | Clear bits (notmask+and)        | rD=A&(~B)
+orx       | M,S,A,B,rD | Extended bitwise OR             |
+
+Other instructions:
+nap       | none       | Sleep until event               |
+
+
+
+Description of Operands:
+
+rD     = Destination Register (GPR or SPR)
+A      = Source Register (GPR or SPR) or Immediate
+B      = Source Register (GPR or SPR) or Immediate
+M      = Mask
+S      = Shift
+E      = External Condition
+l      = Branch label or address
+lrX    = Link Register (lr0 - lr3)
+lrY    = Link Register (lr0 - lr3)
+
+
+Raw instruction format:
+@bitcode_in_hexadecimal
+Example: @1C0 @C11,@C22,r3
diff --git a/assembler/args.c b/assembler/args.c
new file mode 100644 (file)
index 0000000..52aa7c1
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ *   Copyright (C) 2006-2007  Michael Buesch <mb@bu3sch.de>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License version 2
+ *   as published by the Free Software Foundation.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ */
+
+#include "args.h"
+#include "main.h"
+#include "util.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+
+int _debug;
+
+#define ARG_MATCH              0
+#define ARG_NOMATCH            1
+#define ARG_ERROR              -1
+
+static int do_cmp_arg(char **argv, int *pos,
+                     const char *template,
+                     int allow_merged,
+                     char **param)
+{
+       char *arg;
+       char *next_arg;
+       size_t arg_len, template_len;
+
+       arg = argv[*pos];
+       next_arg = argv[*pos + 1];
+       arg_len = strlen(arg);
+       template_len = strlen(template);
+
+       if (param) {
+               /* Maybe we have a merged parameter here.
+                * A merged parameter is "-pfoobar" for example.
+                */
+               if (allow_merged && arg_len > template_len) {
+                       if (memcmp(arg, template, template_len) == 0) {
+                               *param = arg + template_len;
+                               return ARG_MATCH;
+                       }
+                       return ARG_NOMATCH;
+               } else if (arg_len != template_len)
+                       return ARG_NOMATCH;
+               *param = next_arg;
+       }
+       if (strcmp(arg, template) == 0) {
+               if (param) {
+                       /* Skip the parameter on the next iteration. */
+                       (*pos)++;
+                       if (*param == 0) {
+                               fprintf(stderr, "%s needs a parameter\n", arg);
+                               return ARG_ERROR;
+                       }
+               }
+               return ARG_MATCH;
+       }
+
+       return ARG_NOMATCH;
+}
+
+/* Simple and lean command line argument parsing. */
+static int cmp_arg(char **argv, int *pos,
+                  const char *long_template,
+                  const char *short_template,
+                  char **param)
+{
+       int err;
+
+       if (long_template) {
+               err = do_cmp_arg(argv, pos, long_template, 0, param);
+               if (err == ARG_MATCH || err == ARG_ERROR)
+                       return err;
+       }
+       err = ARG_NOMATCH;
+       if (short_template)
+               err = do_cmp_arg(argv, pos, short_template, 1, param);
+       return err;
+}
+
+static void usage(int argc, char **argv)
+{
+       fprintf(stderr, "Usage: %s INPUT_FILE OUTPUT_FILE [OPTIONS]\n", argv[0]);
+       fprintf(stderr, "  -h|--help           Print this help\n");
+       fprintf(stderr, "  -d|--debug          Print verbose debugging info\n");
+       fprintf(stderr, "                      Repeat for more verbose debugging\n");
+}
+
+int parse_args(int argc, char **argv)
+{
+       int i;
+       int res;
+
+       if (argc < 3)
+               goto out_usage;
+       infile_name = argv[1];
+       outfile_name = argv[2];
+
+       for (i = 3; i < argc; i++) {
+               if ((res = cmp_arg(argv, &i, "--help", "-h", 0)) == ARG_MATCH) {
+                       goto out_usage;
+               } else if ((res = cmp_arg(argv, &i, "--debug", "-d", 0)) == ARG_MATCH) {
+                       _debug++;
+               } else {
+                       fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
+                       goto out_usage;
+               }
+       }
+       return 0;
+out_usage:
+       usage(argc, argv);
+       return -1;
+}
+
+int open_input_file(void)
+{
+       int fd;
+       int err;
+
+       if (strcmp(infile_name, "-") == 0) {
+               /* infile == stdin */
+               fd = STDIN_FILENO;
+       } else {
+               fd = open(infile_name, O_RDONLY);
+               if (fd < 0) {
+                       fprintf(stderr, "Could not open INPUT_FILE %s\n",
+                               infile_name);
+                       return -1;
+               }
+               err = dup2(fd, STDIN_FILENO);
+               if (err) {
+                       fprintf(stderr, "Could not dup INPUT_FILE %s "
+                               "to STDIN\n", infile_name);
+                       close(fd);
+                       return -1;
+               }
+       }
+       infile.fd = fd;
+
+       return 0;
+}
+
+void close_input_file(void)
+{
+       if (strcmp(infile_name, "-") != 0)
+               close(infile.fd);
+}
diff --git a/assembler/args.h b/assembler/args.h
new file mode 100644 (file)
index 0000000..8bebeac
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef BCM43xx_ASM_ARGS_H_
+#define BCM43xx_ASM_ARGS_H_
+
+int parse_args(int argc, char **argv);
+int open_input_file(void);
+void close_input_file(void);
+
+extern int _debug;
+
+#define IS_DEBUG               (_debug > 0)
+#define IS_VERBOSE_DEBUG       (_debug > 1)
+#define IS_INSANE_DEBUG                (_debug > 2)
+
+#endif /* BCM43xx_ASM_ARGS_H_ */
diff --git a/assembler/bcm43xx-asm b/assembler/bcm43xx-asm
new file mode 100755 (executable)
index 0000000..de730c7
--- /dev/null
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+installed=0
+
+if [ -z "$BCM43xx_ASM" ]; then
+       if [ $installed -eq 0 ] && [ -x "./bcm43xx-asm.bin" ]; then
+               BCM43xx_ASM="./bcm43xx-asm.bin"
+       else
+               BCM43xx_ASM="bcm43xx-asm.bin"
+       fi
+fi
+
+if [ -z "$CPP" ]; then
+       CPP="cpp"
+fi
+
+if [ $# -lt 2 ]; then
+       $BCM43xx_ASM --help
+       exit 1
+fi
+
+infile="$1"
+shift
+outfile="$1"
+shift
+
+cat "$infile" | $CPP -traditional-cpp | $BCM43xx_ASM "-" "$outfile" $@
diff --git a/assembler/initvals.c b/assembler/initvals.c
new file mode 100644 (file)
index 0000000..eb2634d
--- /dev/null
@@ -0,0 +1,331 @@
+/*
+ *   Copyright (C) 2007  Michael Buesch <mb@bu3sch.de>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License version 2
+ *   as published by the Free Software Foundation.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ */
+
+#include "initvals.h"
+#include "list.h"
+#include "util.h"
+#include "args.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+struct initval {
+       unsigned int offset;
+       unsigned int size;
+#define SIZE_16BIT     2
+#define SIZE_32BIT     4
+       unsigned int value;
+
+       struct list_head list;
+};
+
+/* The IV in the binary file */
+struct initval_raw {
+       be16_t offset_size;
+       union {
+               be16_t d16;
+               be32_t d32;
+       } data __attribute__((__packed__));
+} __attribute__((__packed__));
+
+#define FW_IV_OFFSET_MASK      0x7FFF
+#define FW_IV_32BIT            0x8000
+
+
+struct ivals_context {
+       /* Pointer to the parsed section structure */
+       const struct initvals_sect *sect;
+       /* List of struct initval */
+       struct list_head ivals;
+       /* Number of initvals. */
+       unsigned int ivals_count;
+};
+
+#define _msg_helper(type, ctx, msg, x...)      do {            \
+       fprintf(stderr, "InitVals " type);                      \
+       fprintf(stderr, " (Section \"%s\")", ctx->sect->name);  \
+       fprintf(stderr, ":\n  " msg "\n" ,##x);                 \
+                                               } while (0)
+
+#define iv_error(ctx, msg, x...)       do {            \
+       _msg_helper("ERROR", ctx, msg ,##x);            \
+       exit(1);                                        \
+                                       } while (0)
+
+#define iv_warn(ctx, msg, x...)        \
+       _msg_helper("warning", ctx, msg ,##x)
+
+#define iv_info(ctx, msg, x...)        \
+       _msg_helper("info", ctx, msg ,##x)
+
+
+static void assemble_write_mmio(struct ivals_context *ctx,
+                               unsigned int offset,
+                               unsigned int size,
+                               unsigned int value)
+{
+       struct initval *iv;
+
+       iv = xmalloc(sizeof(struct initval));
+       iv->offset = offset;
+       iv->size = size;
+       iv->value = value;
+       INIT_LIST_HEAD(&iv->list);
+       list_add_tail(&iv->list, &ctx->ivals);
+       ctx->ivals_count++;
+}
+
+static void assemble_write_phy(struct ivals_context *ctx,
+                              unsigned int offset,
+                              unsigned int value)
+{
+       assemble_write_mmio(ctx, 0x3FC, SIZE_16BIT, offset);
+       assemble_write_mmio(ctx, 0x3FE, SIZE_16BIT, value);
+}
+
+static void assemble_write_radio(struct ivals_context *ctx,
+                                unsigned int offset,
+                                unsigned int value)
+{
+       assemble_write_mmio(ctx, 0x3F6, SIZE_16BIT, offset);
+       assemble_write_mmio(ctx, 0x3FA, SIZE_16BIT, value);
+}
+
+static void shm_control_word(struct ivals_context *ctx,
+                            unsigned int routing,
+                            unsigned int offset)
+{
+       unsigned int control;
+
+       control = (routing & 0xFFFF);
+       control <<= 16;
+       control |= (offset & 0xFFFF);
+       assemble_write_mmio(ctx, 0x160, SIZE_32BIT, control);
+}
+
+static void shm_write32(struct ivals_context *ctx,
+                       unsigned int routing,
+                       unsigned int offset,
+                       unsigned int value)
+{
+       if ((routing & 0xFF) == 0x01) {
+               /* Is SHM Shared-memory */
+//TODO         assert((offset & 0x0001) == 0);
+               if (offset & 0x0003) {
+                       /* Unaligned access */
+                       shm_control_word(ctx, routing, offset >> 2);
+                       assemble_write_mmio(ctx, 0x166, SIZE_16BIT,
+                                           (value >> 16) & 0xFFFF);
+                       shm_control_word(ctx, routing, (offset >> 2) + 1);
+                       assemble_write_mmio(ctx, 0x164, SIZE_16BIT,
+                                           (value & 0xFFFF));
+                       return;
+               }
+               offset >>= 2;
+       }
+       shm_control_word(ctx, routing, offset);
+       assemble_write_mmio(ctx, 0x164, SIZE_32BIT, value);
+}
+
+static void shm_write16(struct ivals_context *ctx,
+                       unsigned int routing,
+                       unsigned int offset,
+                       unsigned int value)
+{
+       if ((routing & 0xFF) == 0x01) {
+               /* Is SHM Shared-memory */
+//TODO         assert((offset & 0x0001) == 0);
+               if (offset & 0x0003) {
+                       /* Unaligned access */
+                       shm_control_word(ctx, routing, offset >> 2);
+                       assemble_write_mmio(ctx, 0x166, SIZE_16BIT,
+                                           value);
+                       return;
+               }
+               offset >>= 2;
+       }
+       shm_control_word(ctx, routing, offset);
+       assemble_write_mmio(ctx, 0x164, SIZE_16BIT, value);
+}
+
+static void assemble_write_shm(struct ivals_context *ctx,
+                              unsigned int routing,
+                              unsigned int offset,
+                              unsigned int value,
+                              unsigned int size)
+{
+       switch (routing & 0xFF) {
+       case 0: case 1: case 2: case 3: case 4:
+               break;
+       default:
+               //TODO error
+               break;
+       }
+       //TODO check offset
+
+       //TODO check value
+       switch (size) {
+       case SIZE_16BIT:
+               shm_write16(ctx, routing, offset, value);
+               break;
+       case SIZE_32BIT:
+               shm_write32(ctx, routing, offset, value);
+               break;
+       default:
+               fprintf(stderr, "Internal assembler BUG. SHMwrite invalid size\n");
+               exit(1);
+       }
+}
+
+static void assemble_ival_section(struct ivals_context *ctx,
+                                 const struct initvals_sect *sect)
+{
+       struct initval_op *op;
+
+       ctx->sect = sect;
+       if (list_empty(&sect->ops)) {
+               //TODO warning
+               return;
+       }
+       list_for_each_entry(op, &sect->ops, list) {
+               switch (op->type) {
+               case IVAL_W_MMIO16:
+                       assemble_write_mmio(ctx, op->args[1],
+                                           SIZE_16BIT,
+                                           op->args[0]);
+                       break;
+               case IVAL_W_MMIO32:
+                       assemble_write_mmio(ctx, op->args[1],
+                                           SIZE_32BIT,
+                                           op->args[0]);
+                       break;
+               case IVAL_W_PHY:
+                       assemble_write_phy(ctx, op->args[1],
+                                          op->args[0]);
+                       break;
+               case IVAL_W_RADIO:
+                       assemble_write_radio(ctx, op->args[1],
+                                            op->args[0]);
+                       break;
+               case IVAL_W_SHM16:
+                       assemble_write_shm(ctx, op->args[1],
+                                          op->args[2],
+                                          op->args[0],
+                                          SIZE_16BIT);
+                       break;
+               case IVAL_W_SHM32:
+                       assemble_write_shm(ctx, op->args[1],
+                                          op->args[2],
+                                          op->args[0],
+                                          SIZE_32BIT);
+                       break;
+               }
+       }
+}
+
+static unsigned int initval_to_raw(struct ivals_context *ctx,
+                                  struct initval_raw *raw,
+                                  const struct initval *iv)
+{
+       unsigned int size;
+
+       memset(raw, 0, sizeof(*raw));
+       if (iv->offset & ~FW_IV_OFFSET_MASK) {
+               iv_error(ctx, "Initval offset 0x%04X too big. "
+                        "Offset must be <= 0x%04X",
+                        iv->offset, FW_IV_OFFSET_MASK);
+       }
+       raw->offset_size = cpu_to_be16(iv->offset);
+
+       switch (iv->size) {
+       case SIZE_16BIT:
+               raw->data.d16 = cpu_to_be16(iv->value);
+               size = sizeof(be16_t) + sizeof(be16_t);
+               break;
+       case SIZE_32BIT:
+               raw->data.d32 = cpu_to_be32(iv->value);
+               raw->offset_size |= cpu_to_be16(FW_IV_32BIT);
+               size = sizeof(be16_t) + sizeof(be32_t);
+               break;
+       default:
+               iv_error(ctx, "Internal error. initval_to_raw invalid size.");
+               break;
+       }
+
+       return size;
+}
+
+static void emit_ival_section(struct ivals_context *ctx)
+{
+       FILE *fd;
+       char *fn;
+       size_t fn_len;
+       struct initval *iv;
+       struct initval_raw raw;
+       struct fw_header hdr;
+       unsigned int size;
+
+       memset(&hdr, 0, sizeof(hdr));
+       hdr.type = FW_TYPE_IV;
+       hdr.ver = FW_HDR_VER;
+       hdr.size = cpu_to_be32(ctx->ivals_count);
+
+       fn_len = strlen(outfile_name) + 512;
+       fn = xmalloc(fn_len);
+       snprintf(fn, fn_len, "%s.%s.initval", outfile_name, ctx->sect->name);
+       fd = fopen(fn, "w+");
+       if (!fd) {
+               fprintf(stderr, "Could not open initval output file \"%s\"\n", fn);
+               free(fn);
+               exit(1);
+       }
+
+       if (fwrite(&hdr, sizeof(hdr), 1, fd) != 1) {
+               fprintf(stderr, "Could not write initvals outfile\n");
+               exit(1);
+       }
+
+       if (IS_VERBOSE_DEBUG)
+               fprintf(stderr, "\nInitvals \"%s\":\n", ctx->sect->name);
+       list_for_each_entry(iv, &ctx->ivals, list) {
+               if (IS_VERBOSE_DEBUG) {
+                       fprintf(stderr, "%04X %u %08X\n",
+                               iv->offset,
+                               iv->size,
+                               iv->value);
+               }
+               size = initval_to_raw(ctx, &raw, iv);
+               if (fwrite(&raw, size, 1, fd) != 1) {
+                       fprintf(stderr, "Could not write initvals outfile\n");
+                       exit(1);
+               }
+       }
+       fclose(fd);
+       free(fn);
+}
+
+void assemble_initvals(void)
+{
+       struct ivals_context ctx;
+       struct initvals_sect *sect;
+
+       list_for_each_entry(sect, &infile.ivals, list) {
+               memset(&ctx, 0, sizeof(ctx));
+               INIT_LIST_HEAD(&ctx.ivals);
+
+               assemble_ival_section(&ctx, sect);
+               emit_ival_section(&ctx);
+       }
+}
diff --git a/assembler/initvals.h b/assembler/initvals.h
new file mode 100644 (file)
index 0000000..0afecf2
--- /dev/null
@@ -0,0 +1,35 @@
+#ifndef BCM43xx_ASM_INITVALS_H_
+#define BCM43xx_ASM_INITVALS_H_
+
+#include "main.h"
+
+
+struct initval_op {
+       enum {
+               IVAL_W_MMIO16,
+               IVAL_W_MMIO32,
+               IVAL_W_PHY,
+               IVAL_W_RADIO,
+               IVAL_W_SHM16,
+               IVAL_W_SHM32,
+       } type;
+       unsigned int args[3];
+
+       struct lineinfo info;
+
+       struct list_head list;
+};
+
+struct initvals_sect {
+       /* The name string for this initvals section */
+       const char *name;
+       /* List of initval operations */
+       struct list_head ops;
+
+       struct list_head list;
+};
+
+
+extern void assemble_initvals(void);
+
+#endif /* BCM43xx_ASM_INITVALS_H_ */
diff --git a/assembler/list.h b/assembler/list.h
new file mode 100644 (file)
index 0000000..7846424
--- /dev/null
@@ -0,0 +1,296 @@
+/*
+ * Copied from the Linux kernel source tree, version 2.6.0-test1.
+ *
+ * Licensed under the GPL v2 as per the whole kernel source tree.
+ *
+ */
+
+#ifndef _LIST_H
+#define _LIST_H
+
+#ifndef __GNUC__
+# error "Need GNU GCC"
+#endif
+
+#define typeof         __typeof__
+#define offsetof(type, member)  __builtin_offsetof (type, member)
+
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ *
+ * @ptr:       the pointer to the member.
+ * @type:      the type of the container struct this is embedded in.
+ * @member:    the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({                     \
+       const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
+       (type *)( (char *)__mptr - offsetof(type,member) );})
+
+/*
+ * These are non-NULL pointers that will result in page faults
+ * under normal circumstances, used to verify that nobody uses
+ * non-initialized list entries.
+ */
+#define LIST_POISON1  ((void *) 0x00100100)
+#define LIST_POISON2  ((void *) 0x00200200)
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+       struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+       struct list_head name = LIST_HEAD_INIT(name)
+
+#define INIT_LIST_HEAD(ptr) do { \
+       (ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/*
+ * Insert a new entry between two known consecutive entries. 
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add(struct list_head *new,
+                             struct list_head *prev,
+                             struct list_head *next)
+{
+       next->prev = new;
+       new->next = next;
+       new->prev = prev;
+       prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+       __list_add(new, head, head->next);
+}
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+       __list_add(new, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+       next->prev = prev;
+       prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void list_del(struct list_head *entry)
+{
+       __list_del(entry->prev, entry->next);
+       entry->next = LIST_POISON1;
+       entry->prev = LIST_POISON2;
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static inline void list_del_init(struct list_head *entry)
+{
+       __list_del(entry->prev, entry->next);
+       INIT_LIST_HEAD(entry); 
+}
+
+/**
+ * list_move - delete from one list and add as another's head
+ * @list: the entry to move
+ * @head: the head that will precede our entry
+ */
+static inline void list_move(struct list_head *list, struct list_head *head)
+{
+       __list_del(list->prev, list->next);
+       list_add(list, head);
+}
+
+/**
+ * list_move_tail - delete from one list and add as another's tail
+ * @list: the entry to move
+ * @head: the head that will follow our entry
+ */
+static inline void list_move_tail(struct list_head *list,
+                                 struct list_head *head)
+{
+       __list_del(list->prev, list->next);
+       list_add_tail(list, head);
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static inline int list_empty(const struct list_head *head)
+{
+       return head->next == head;
+}
+
+static inline void __list_splice(struct list_head *list,
+                                struct list_head *head)
+{
+       struct list_head *first = list->next;
+       struct list_head *last = list->prev;
+       struct list_head *at = head->next;
+
+       first->prev = head;
+       head->next = first;
+
+       last->next = at;
+       at->prev = last;
+}
+
+/**
+ * list_splice - join two lists
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static inline void list_splice(struct list_head *list, struct list_head *head)
+{
+       if (!list_empty(list))
+               __list_splice(list, head);
+}
+
+/**
+ * list_splice_init - join two lists and reinitialise the emptied list.
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ *
+ * The list at @list is reinitialised
+ */
+static inline void list_splice_init(struct list_head *list,
+                                   struct list_head *head)
+{
+       if (!list_empty(list)) {
+               __list_splice(list, head);
+               INIT_LIST_HEAD(list);
+       }
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr:       the &struct list_head pointer.
+ * @type:      the type of the struct this is embedded in.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+       container_of(ptr, type, member)
+
+/**
+ * list_for_each       -       iterate over a list
+ * @pos:       the &struct list_head to use as a loop counter.
+ * @head:      the head for your list.
+ */
+#define list_for_each(pos, head) \
+       for (pos = (head)->next; pos != (head); \
+               pos = pos->next)
+
+/**
+ * __list_for_each     -       iterate over a list
+ * @pos:       the &struct list_head to use as a loop counter.
+ * @head:      the head for your list.
+ *
+ * This variant differs from list_for_each() in that it's the
+ * simplest possible list iteration code.
+ * Use this for code that knows the list to be very short (empty
+ * or 1 entry) most of the time.
+ */
+#define __list_for_each(pos, head) \
+       for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_prev  -       iterate over a list backwards
+ * @pos:       the &struct list_head to use as a loop counter.
+ * @head:      the head for your list.
+ */
+#define list_for_each_prev(pos, head) \
+       for (pos = (head)->prev; pos != (head); pos = pos->prev)
+
+/**
+ * list_for_each_safe  -       iterate over a list safe against removal of list entry
+ * @pos:       the &struct list_head to use as a loop counter.
+ * @n:         another &struct list_head to use as temporary storage
+ * @head:      the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+       for (pos = (head)->next, n = pos->next; pos != (head); \
+               pos = n, n = pos->next)
+
+/**
+ * list_for_each_entry -       iterate over list of given type
+ * @pos:       the type * to use as a loop counter.
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_for_each_entry(pos, head, member)                         \
+       for (pos = list_entry((head)->next, typeof(*pos), member);      \
+            &pos->member != (head);                                    \
+            pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_reverse - iterate backwards over list of given type.
+ * @pos:       the type * to use as a loop counter.
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_reverse(pos, head, member)                 \
+       for (pos = list_entry((head)->prev, typeof(*pos), member);      \
+            &pos->member != (head);                                    \
+            pos = list_entry(pos->member.prev, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos:       the type * to use as a loop counter.
+ * @n:         another type * to use as temporary storage
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_safe(pos, n, head, member)                 \
+       for (pos = list_entry((head)->next, typeof(*pos), member),      \
+               n = list_entry(pos->member.next, typeof(*pos), member); \
+            &pos->member != (head);                                    \
+            pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+#endif /* _LIST_H */
diff --git a/assembler/main.c b/assembler/main.c
new file mode 100644 (file)
index 0000000..bda23d0
--- /dev/null
@@ -0,0 +1,1068 @@
+/*
+ *   Copyright (C) 2006-2007  Michael Buesch <mb@bu3sch.de>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License version 2
+ *   as published by the Free Software Foundation.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ */
+
+#include "main.h"
+#include "list.h"
+#include "util.h"
+#include "parser.h"
+#include "args.h"
+#include "initvals.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+extern int yyparse(void);
+extern int yydebug;
+
+struct file infile;
+const char *infile_name;
+const char *outfile_name;
+
+
+struct out_operand {
+       enum {
+               OUTOPER_NORMAL,
+               OUTOPER_LABELREF,
+       } type;
+
+       union {
+               unsigned int operand; /* For NORMAL */
+               struct label *label; /* For LABELREF */
+       } u;
+};
+
+struct code_output {
+       enum {
+               OUT_INSN,
+               OUT_LABEL,
+       } type;
+
+       unsigned int opcode;
+       struct out_operand operands[3];
+
+       /* The absolute address of this instruction.
+        * Only used in resolve_labels(). */
+       unsigned int address;
+
+       const char *labelname; /* only for OUT_LABEL */
+       /* Set to 1, if this is the %start instruction. */
+       int is_start_insn;
+
+       struct list_head list;
+};
+
+struct assembler_context {
+       int arch;
+       struct label *start_label;
+
+       /* Tracking stuff */
+       struct statement *cur_stmt;
+
+       struct list_head output;
+};
+
+
+#define for_each_statement(ctx, s)                     \
+       list_for_each_entry(s, &infile.sl, list) {      \
+               ctx->cur_stmt = s;
+
+#define for_each_statement_end(ctx, s)                 \
+       } do { ctx->cur_stmt = NULL; } while (0)
+
+#define _msg_helper(type, stmt, msg, x...)     do {            \
+       fprintf(stderr, "Assembler " type);                     \
+       if (stmt) {                                             \
+               fprintf(stderr, " (file \"%s\", line %u)",      \
+                       stmt->info.file,                        \
+                       stmt->info.lineno);                     \
+       }                                                       \
+       fprintf(stderr, ":\n  " msg "\n" ,##x);                 \
+                                               } while (0)
+
+#define asm_error(ctx, msg, x...)      do {                    \
+       _msg_helper("ERROR", (ctx)->cur_stmt, msg ,##x);        \
+       exit(1);                                                \
+                                       } while (0)
+
+#define asm_warn(ctx, msg, x...)       \
+       _msg_helper("warning", (ctx)->cur_stmt, msg ,##x)
+
+#define asm_info(ctx, msg, x...)       \
+       _msg_helper("info", (ctx)->cur_stmt, msg ,##x)
+
+
+static void eval_directives(struct assembler_context *ctx)
+{
+       struct statement *s;
+       struct asmdir *ad;
+       struct label *l;
+       int have_start_label = 0;
+       int have_arch = 0;
+
+       for_each_statement(ctx, s) {
+               if (s->type == STMT_ASMDIR) {
+                       ad = s->u.asmdir;
+                       switch (ad->type) {
+                       case ADIR_ARCH:
+                               if (have_arch)
+                                       asm_error(ctx, "Multiple %%arch definitions");
+                               ctx->arch = ad->u.arch;
+                               have_arch = 1;
+                               break;
+                       case ADIR_START:
+                               if (have_start_label)
+                                       asm_error(ctx, "Multiple %%start definitions");
+                               ctx->start_label = ad->u.start;
+                               have_start_label = 1;
+                               break;
+                       default:
+                               asm_error(ctx, "Unknown ASM directive");
+                       }
+               }
+       } for_each_statement_end(ctx, s);
+
+       if (!have_arch)
+               asm_error(ctx, "No %%arch defined");
+       if (ctx->arch != NEWWORLD)
+               asm_error(ctx, "TODO: Only NEWWORLD arch supported, yet");
+       if (!have_start_label)
+               asm_info(ctx, "Using start address 0");
+}
+
+static int is_possible_imm(unsigned int imm)
+{
+       unsigned int mask;
+
+       /* Immediates are only possible up to 16bit (wordsize). */
+       mask = ~0;
+       mask <<= 16;
+       if (imm & (1 << 15)) {
+               if ((imm & mask) != mask &&
+                   (imm & mask) != 0)
+                       return 0;
+       } else {
+               if ((imm & mask) != 0)
+                       return 0;
+       }
+
+       return 1;
+}
+
+static int is_valid_imm(unsigned int imm)
+{
+       unsigned int mask;
+
+       /* This function checks if the immediate value is representable
+        * as a native immediate operand.
+        *
+        * The value itself is 10bit long, signed.
+        * We also honor sign-extension, so we allow values
+        * of 0xFFFF, for example.
+        */
+
+       if (!is_possible_imm(imm))
+               return 0;
+       imm &= 0xFFFF;
+
+       /* assert sign extension */
+       mask = 0xFC00;
+       if (imm & (1 << 9)) {
+               /* sign-extended */
+               if ((imm & mask) != mask)
+                       return 0;
+       } else {
+               if (imm & mask)
+                       return 0;
+       }
+
+       return 1;
+}
+
+static int is_contiguous_bitmask(unsigned int mask)
+{
+       int bit;
+       int only_zero_now = 0;
+
+       /* This checks if the mask is contiguous.
+        * A contiguous mask is:
+        *   0b0001111110000
+        * A non-contiguous mask is:
+        *   0b0001101110000
+        */
+
+       bit = ffs(mask);
+       if (!bit)
+               return 1;
+       if (bit > 16)
+               return 1;
+       bit--;
+       for ( ; bit < 16; bit++) {
+               if (mask & (1 << bit)) {
+                       if (only_zero_now)
+                               return 0;
+               } else
+                       only_zero_now = 1;
+       }
+
+       return 1;
+}
+
+static unsigned int generate_imm_operand(struct assembler_context *ctx,
+                                        const struct immediate *imm)
+{
+       unsigned int val, tmp;
+       unsigned int mask;
+       int too_long = 0;
+
+       /* format: 0b11ii iiii iiii */
+
+       val = 0xC00;
+       tmp = imm->imm;
+
+       if (!is_valid_imm(tmp)) {
+               asm_warn(ctx, "IMMEDIATE 0x%X (%d) too long "
+                             "(> 9 bits + sign). Did you intend to "
+                             "use implicit sign extension?",
+                        tmp, (int)tmp);
+       }
+
+       tmp &= 0x3FF;
+       val |= tmp;
+
+       return val;
+}
+
+static unsigned int generate_reg_operand(struct assembler_context *ctx,
+                                        const struct registr *reg)
+{
+       unsigned int val = 0;
+
+       switch (reg->type) {
+       case GPR:
+               /* format: 0b1011 11rr rrrr */
+               val |= 0xBC0;
+               if (reg->nr & ~0x3F)
+                       asm_error(ctx, "GPR-nr too big");
+               val |= reg->nr;
+               break;
+       case SPR:
+               /* format: 0b100. .... .... */
+               val |= 0x800;
+               if (reg->nr & ~0x1FF)
+                       asm_error(ctx, "SPR-nr too big");
+               val |= reg->nr;
+               break;
+       case OFFR:
+               /* format: 0b1000 0110 0rrr */
+               val |= 0x860;
+               if (reg->nr & ~0x7)
+                       asm_error(ctx, "OFFR-nr too big");
+               val |= reg->nr;
+               break;
+       default:
+               asm_error(ctx, "generate_reg_operand() regtype");
+       }
+
+       return val;
+}
+
+static unsigned int generate_mem_operand(struct assembler_context *ctx,
+                                        const struct memory *mem)
+{
+       unsigned int val = 0, off, reg;
+
+       switch (mem->type) {
+       case MEM_DIRECT:
+               /* format: 0b0mmm mmmm mmmm */
+               off = mem->offset;
+               if (off & ~0x7FF) {
+                       asm_warn(ctx, "DIRECT memoffset 0x%X too long (> 11 bits)", off);
+                       off &= 0x7FF;
+               }
+               val |= off;
+               break;
+       case MEM_INDIRECT:
+               /* format: 0b101r rroo oooo */
+               off = mem->offset;
+               reg = mem->offr_nr;
+               val |= 0xA00;
+               if (off & ~0x3F) {
+                       asm_warn(ctx, "INDIRECT memoffset 0x%X too long (> 6 bits)", off);
+                       off &= 0x3F;
+               }
+               if (reg & ~0x7)
+                       asm_error(ctx, "OFFR-nr too big");
+               val |= off;
+               val |= (reg << 6);
+               break;
+       default:
+               asm_error(ctx, "generate_mem_operand() memtype");
+       }
+
+       return val;
+}
+
+static void generate_operand(struct assembler_context *ctx,
+                            const struct operand *oper,
+                            struct out_operand *out)
+{
+       out->type = OUTOPER_NORMAL;
+
+       switch (oper->type) {
+       case OPER_IMM:
+               out->u.operand = generate_imm_operand(ctx, oper->u.imm);
+               break;
+       case OPER_REG:
+               out->u.operand = generate_reg_operand(ctx, oper->u.reg);
+               break;
+       case OPER_MEM:
+               out->u.operand = generate_mem_operand(ctx, oper->u.mem);
+               break;
+       case OPER_LABEL:
+               out->type = OUTOPER_LABELREF;
+               out->u.label = oper->u.label;
+               break;
+       case OPER_ADDR:
+               out->u.operand = oper->u.addr->addr;
+               break;
+       case OPER_RAW:
+               out->u.operand = oper->u.raw;
+               break;
+       default:
+               asm_error(ctx, "generate_operand() operstate");
+       }
+}
+
+static void do_assemble_insn(struct assembler_context *ctx,
+                            struct instruction *insn,
+                            unsigned int opcode)
+{
+       int i;
+       struct operlist *ol;
+       int nr_oper = 0;
+       uint64_t code = 0;
+       struct code_output *out;
+       struct label *labelref = NULL;
+       struct operand *oper;
+       int have_spr_operand = 0;
+       int have_mem_operand = 0;
+
+       out = xmalloc(sizeof(*out));
+       INIT_LIST_HEAD(&out->list);
+       out->opcode = opcode;
+
+       ol = insn->operands;
+       if (ARRAY_SIZE(out->operands) > ARRAY_SIZE(ol->oper))
+               asm_error(ctx, "Internal operand array confusion");
+
+       for (i = 0; i < ARRAY_SIZE(out->operands); i++) {
+               oper = ol->oper[i];
+               if (!oper)
+                       continue;
+
+               /* If this is an INPUT operand (first or second), we must
+                * make sure that not both are accessing SPR or MEMORY.
+                * The device only supports one SPR or MEMORY operand in
+                * the input operands. */
+               if ((i == 0) || (i == 1)) {
+                       if ((oper->type == OPER_REG) &&
+                           (oper->u.reg->type == SPR)) {
+                               if (have_spr_operand)
+                                       asm_error(ctx, "Multiple SPR input operands in one instruction");
+                               have_spr_operand = 1;
+                       }
+                       if (oper->type == OPER_MEM) {
+                               if (have_mem_operand)
+                                       asm_error(ctx, "Multiple MEMORY input operands in on instruction");
+                               have_mem_operand = 1;
+                       }
+               }
+
+               generate_operand(ctx, oper, &out->operands[i]);
+               nr_oper++;
+       }
+       if (nr_oper != 3)
+               asm_error(ctx, "Internal error: nr_oper at "
+                              "lowlevel do_assemble_insn");
+
+       list_add_tail(&out->list, &ctx->output);
+}
+
+static unsigned int merge_ext_into_opcode(struct assembler_context *ctx,
+                                         unsigned int opbase,
+                                         struct instruction *insn)
+{
+       struct operlist *ol;
+       unsigned int opcode;
+       unsigned int mask, shift;
+
+       ol = insn->operands;
+       opcode = opbase;
+       mask = ol->oper[0]->u.raw;
+       if (mask & ~0xF)
+               asm_error(ctx, "opcode MASK extension too big (> 0xF)");
+       shift = ol->oper[1]->u.raw;
+       if (shift & ~0xF)
+               asm_error(ctx, "opcode SHIFT extension too big (> 0xF)");
+       opcode |= (mask << 4);
+       opcode |= shift;
+       ol->oper[0] = ol->oper[2];
+       ol->oper[1] = ol->oper[3];
+       ol->oper[2] = ol->oper[4];
+
+       return opcode;
+}
+
+static unsigned int merge_external_jmp_into_opcode(struct assembler_context *ctx,
+                                                  unsigned int opbase,
+                                                  struct instruction *insn)
+{
+       struct operlist *ol;
+       unsigned int cond;
+       unsigned int opcode;
+
+       ol = insn->operands;
+       opcode = opbase;
+       cond = ol->oper[0]->u.raw;
+       if (cond & ~0xFF)
+               asm_error(ctx, "External jump condition value too big (> 0xFF)");
+       opcode |= cond;
+       ol->oper[0] = ol->oper[1];
+       ol->oper[1] = ol->oper[2];
+       ol->oper[2] = ol->oper[3];
+
+       return opcode;
+}
+
+static void assemble_instruction(struct assembler_context *ctx,
+                                struct instruction *insn);
+
+static void emulate_mov_insn(struct assembler_context *ctx,
+                            struct instruction *insn)
+{
+       struct instruction em_insn;
+       struct operlist em_ol;
+       struct operand em_op_shift;
+       struct operand em_op_mask;
+       struct operand em_op_x;
+       struct operand em_op_y;
+       struct immediate em_imm_x;
+       struct immediate em_imm_y;
+
+       struct operand *in, *out;
+       unsigned int tmp;
+
+       /* This is a pseudo-OP. We emulate it by OR or ORX */
+
+       in = insn->operands->oper[0];
+       out = insn->operands->oper[1];
+
+       em_insn.op = OP_OR;
+       em_ol.oper[0] = in;
+       em_imm_x.imm = 0;
+       em_op_x.type = OPER_IMM;
+       em_op_x.u.imm = &em_imm_x;
+       em_ol.oper[1] = &em_op_x;
+       em_ol.oper[2] = out;
+
+       if (in->type == OPER_IMM) {
+               tmp = in->u.imm->imm;
+               if (!is_possible_imm(tmp))
+                       asm_error(ctx, "MOV operand 0x%X > 16bit", tmp);
+               if (!is_valid_imm(tmp)) {
+                       /* Immediate too big for plain OR */
+                       em_insn.op = OP_ORX;
+
+                       em_op_mask.type = OPER_RAW;
+                       em_op_mask.u.raw = 0x7;
+                       em_op_shift.type = OPER_RAW;
+                       em_op_shift.u.raw = 0x8;
+
+                       em_imm_x.imm = (tmp & 0xFF00) >> 8;
+                       em_op_x.type = OPER_IMM;
+                       em_op_x.u.imm = &em_imm_x;
+
+                       em_imm_y.imm = (tmp & 0x00FF);
+                       em_op_y.type = OPER_IMM;
+                       em_op_y.u.imm = &em_imm_y;
+
+                       em_ol.oper[0] = &em_op_mask;
+                       em_ol.oper[1] = &em_op_shift;
+                       em_ol.oper[2] = &em_op_x;
+                       em_ol.oper[3] = &em_op_y;
+                       em_ol.oper[4] = out;
+               }
+       }
+
+       em_insn.operands = &em_ol;
+       assemble_instruction(ctx, &em_insn); /* recurse */
+}
+
+static void emulate_jmp_insn(struct assembler_context *ctx,
+                            struct instruction *insn)
+{
+       struct instruction em_insn;
+       struct operlist em_ol;
+       struct operand em_op;
+       struct immediate em_imm;
+
+       /* This is a pseudo-OP. We emulate it by JE */
+
+       em_insn.op = OP_JE;
+       em_imm.imm = 1;
+       em_op.type = OPER_IMM;
+       em_op.u.imm = &em_imm;
+       em_ol.oper[0] = &em_op;
+       em_ol.oper[1] = &em_op;
+       em_ol.oper[2] = insn->operands->oper[0];
+       em_insn.operands = &em_ol;
+       assemble_instruction(ctx, &em_insn); /* recurse */
+}
+
+static void emulate_jand_insn(struct assembler_context *ctx,
+                             struct instruction *insn,
+                             int inverted)
+{
+       struct instruction em_insn;
+       struct operlist em_ol;
+       struct operand em_op_shift;
+       struct operand em_op_mask;
+       struct operand em_op_y;
+       struct immediate em_imm;
+
+       struct operand *oper0, *oper1, *oper2;
+       struct operand *imm_oper = NULL;
+       unsigned int tmp;
+       int first_bit, last_bit;
+
+       oper0 = insn->operands->oper[0];
+       oper1 = insn->operands->oper[1];
+       oper2 = insn->operands->oper[2];
+
+       if (oper0->type == OPER_IMM)
+               imm_oper = oper0;
+       if (oper1->type == OPER_IMM)
+               imm_oper = oper1;
+       if (oper0->type == OPER_IMM && oper1->type == OPER_IMM)
+               imm_oper = NULL;
+
+       if (imm_oper) {
+               /* We have a single immediate operand.
+                * Check if it's representable by a normal JAND insn.
+                */
+               tmp = imm_oper->u.imm->imm;
+               if (!is_valid_imm(tmp)) {
+                       /* Nope, this must be emulated by JZX/JNZX */
+                       if (!is_contiguous_bitmask(tmp)) {
+                               asm_error(ctx, "Long bitmask 0x%X is not contiguous",
+                                         tmp);
+                       }
+
+                       first_bit = ffs(tmp);
+                       last_bit = ffs(~(tmp >> (first_bit - 1))) - 1 + first_bit - 1;
+
+                       if (inverted)
+                               em_insn.op = OP_JZX;
+                       else
+                               em_insn.op = OP_JNZX;
+                       em_op_shift.type = OPER_RAW;
+                       em_op_shift.u.raw = first_bit - 1;
+                       em_op_mask.type = OPER_RAW;
+                       em_op_mask.u.raw = last_bit - first_bit;
+
+                       em_imm.imm = 0;
+                       em_op_y.type = OPER_IMM;
+                       em_op_y.u.imm = &em_imm;
+
+                       em_ol.oper[0] = &em_op_mask;
+                       em_ol.oper[1] = &em_op_shift;
+                       if (oper0->type != OPER_IMM)
+                               em_ol.oper[2] = oper0;
+                       else
+                               em_ol.oper[2] = oper1;
+                       em_ol.oper[3] = &em_op_y;
+                       em_ol.oper[4] = oper2;
+
+                       em_insn.operands = &em_ol;
+
+                       assemble_instruction(ctx, &em_insn); /* recurse */
+                       return;
+               }
+       }
+
+       /* Do a normal JAND/JNAND instruction */
+       if (inverted)
+               do_assemble_insn(ctx, insn, 0x040 | 0x1);
+       else
+               do_assemble_insn(ctx, insn, 0x040);
+}
+
+static void assemble_instruction(struct assembler_context *ctx,
+                                struct instruction *insn)
+{
+       unsigned int opcode;
+
+       switch (insn->op) {
+       case OP_ADD:
+               do_assemble_insn(ctx, insn, 0x1C0);
+               break;
+       case OP_ADDSC:
+               do_assemble_insn(ctx, insn, 0x1C2);
+               break;
+       case OP_ADDC:
+               do_assemble_insn(ctx, insn, 0x1C1);
+               break;
+       case OP_ADDSCC:
+               do_assemble_insn(ctx, insn, 0x1C3);
+               break;
+       case OP_SUB:
+               do_assemble_insn(ctx, insn, 0x1D0);
+               break;
+       case OP_SUBSC:
+               do_assemble_insn(ctx, insn, 0x1D2);
+               break;
+       case OP_SUBC:
+               do_assemble_insn(ctx, insn, 0x1D1);
+               break;
+       case OP_SUBSCC:
+               do_assemble_insn(ctx, insn, 0x1D3);
+               break;
+       case OP_SRA:
+               do_assemble_insn(ctx, insn, 0x130);
+               break;
+       case OP_OR:
+               do_assemble_insn(ctx, insn, 0x160);
+               break;
+       case OP_AND:
+               do_assemble_insn(ctx, insn, 0x140);
+               break;
+       case OP_XOR:
+               do_assemble_insn(ctx, insn, 0x170);
+               break;
+       case OP_SR:
+               do_assemble_insn(ctx, insn, 0x120);
+               break;
+       case OP_SRX:
+               opcode = merge_ext_into_opcode(ctx, 0x200, insn);
+               do_assemble_insn(ctx, insn, opcode);
+               break;
+       case OP_SL:
+               do_assemble_insn(ctx, insn, 0x110);
+               break;
+       case OP_RL:
+               do_assemble_insn(ctx, insn, 0x1A0);
+               break;
+       case OP_RR:
+               do_assemble_insn(ctx, insn, 0x1B0);
+               break;
+       case OP_NAND:
+               do_assemble_insn(ctx, insn, 0x150);
+               break;
+       case OP_ORX:
+               opcode = merge_ext_into_opcode(ctx, 0x300, insn);
+               do_assemble_insn(ctx, insn, opcode);
+               break;
+       case OP_MOV:
+               emulate_mov_insn(ctx, insn);
+               return;
+       case OP_JMP:
+               emulate_jmp_insn(ctx, insn);
+               return;
+       case OP_JAND:
+               emulate_jand_insn(ctx, insn, 0);
+               return;
+       case OP_JNAND:
+               emulate_jand_insn(ctx, insn, 1);
+               return;
+       case OP_JS:
+               do_assemble_insn(ctx, insn, 0x050);
+               break;
+       case OP_JNS:
+               do_assemble_insn(ctx, insn, 0x050 | 0x1);
+               break;
+       case OP_JE:
+               do_assemble_insn(ctx, insn, 0x0D0);
+               break;
+       case OP_JNE:
+               do_assemble_insn(ctx, insn, 0x0D0 | 0x1);
+               break;
+       case OP_JLS:
+               do_assemble_insn(ctx, insn, 0x0D2);
+               break;
+       case OP_JGES:
+               do_assemble_insn(ctx, insn, 0x0D2 | 0x1);
+               break;
+       case OP_JGS:
+               do_assemble_insn(ctx, insn, 0x0D4);
+               break;
+       case OP_JLES:
+               do_assemble_insn(ctx, insn, 0x0D4 | 0x1);
+               break;
+       case OP_JL:
+               do_assemble_insn(ctx, insn, 0x0DA);
+               break;
+       case OP_JGE:
+               do_assemble_insn(ctx, insn, 0x0DA | 0x1);
+               break;
+       case OP_JG:
+               do_assemble_insn(ctx, insn, 0x0DC);
+               break;
+       case OP_JLE:
+               do_assemble_insn(ctx, insn, 0x0DC | 0x1);
+               break;
+       case OP_JZX:
+               opcode = merge_ext_into_opcode(ctx, 0x400, insn);
+               do_assemble_insn(ctx, insn, opcode);
+               break;
+       case OP_JNZX:
+               opcode = merge_ext_into_opcode(ctx, 0x500, insn);
+               do_assemble_insn(ctx, insn, opcode);
+               break;
+       case OP_JEXT:
+               opcode = merge_external_jmp_into_opcode(ctx, 0x700, insn);
+               do_assemble_insn(ctx, insn, opcode);
+               break;
+       case OP_JNEXT:
+               opcode = merge_external_jmp_into_opcode(ctx, 0x600, insn);
+               do_assemble_insn(ctx, insn, opcode);
+               break;
+       case OP_CALL:
+               do_assemble_insn(ctx, insn, 0x002);
+               break;
+       case OP_RET:
+               do_assemble_insn(ctx, insn, 0x003);
+               break;
+       case OP_TKIPH:
+       case OP_TKIPHS:
+       case OP_TKIPL:
+       case OP_TKIPLS:
+               do_assemble_insn(ctx, insn, 0x1E0);
+               break;
+       case OP_NAP:
+               do_assemble_insn(ctx, insn, 0x001);
+               break;
+       case RAW_CODE:
+               do_assemble_insn(ctx, insn, insn->opcode);
+               break;
+       default:
+               asm_error(ctx, "Unknown op");
+       }
+}
+
+static void assemble_instructions(struct assembler_context *ctx)
+{
+       struct statement *s;
+       struct instruction *insn;
+       struct code_output *out;
+
+       if (ctx->start_label) {
+               /* Generate a jump instruction at offset 0 to
+                * jump to the code start.
+                */
+               struct instruction sjmp;
+               struct operlist ol;
+               struct operand oper;
+
+               oper.type = OPER_LABEL;
+               oper.u.label = ctx->start_label;
+               ol.oper[0] = &oper;
+               sjmp.op = OP_JMP;
+               sjmp.operands = &ol;
+
+               assemble_instruction(ctx, &sjmp);
+               out = list_entry(ctx->output.next, struct code_output, list);
+               out->is_start_insn = 1;
+       }
+
+       for_each_statement(ctx, s) {
+               switch (s->type) {
+               case STMT_INSN:
+                       ctx->cur_stmt = s;
+                       insn = s->u.insn;
+                       assemble_instruction(ctx, insn);
+                       break;
+               case STMT_LABEL:
+                       out = xmalloc(sizeof(*out));
+                       INIT_LIST_HEAD(&out->list);
+                       out->type = OUT_LABEL;
+                       out->labelname = s->u.label->name;
+
+                       list_add_tail(&out->list, &ctx->output);
+                       break;
+               case STMT_ASMDIR:
+                       break;
+               }
+       } for_each_statement_end(ctx, s);
+}
+
+/* Resolve a label reference to the address it points to. */
+static int get_labeladdress(struct assembler_context *ctx,
+                           struct code_output *this_insn,
+                           struct label *labelref)
+{
+       struct code_output *c;
+       bool found = 0;
+       int address = -1;
+
+       switch (labelref->direction) {
+       case LABELREF_ABSOLUTE:
+               list_for_each_entry(c, &ctx->output, list) {
+                       if (c->type != OUT_LABEL)
+                               continue;
+                       if (strcmp(c->labelname, labelref->name) != 0)
+                               continue;
+                       if (found) {
+                               asm_error(ctx, "Ambiguous label reference \"%s\"",
+                                         labelref->name);
+                       }
+                       found = 1;
+                       address = c->address;
+               }
+               break;
+       case LABELREF_RELATIVE_BACK:
+               for (c = list_entry(this_insn->list.prev, typeof(*c), list);
+                    &c->list != &ctx->output;
+                    c = list_entry(c->list.prev, typeof(*c), list)) {
+                       if (c->type != OUT_LABEL)
+                               continue;
+                       if (strcmp(c->labelname, labelref->name) == 0) {
+                               /* Found */
+                               address = c->address;
+                               break;
+                       }
+               }
+               break;
+       case LABELREF_RELATIVE_FORWARD:
+               for (c = list_entry(this_insn->list.next, typeof(*c), list);
+                    &c->list != &ctx->output;
+                    c = list_entry(c->list.next, typeof(*c), list)) {
+                       if (c->type != OUT_LABEL)
+                               continue;
+                       if (strcmp(c->labelname, labelref->name) == 0) {
+                               /* Found */
+                               address = c->address;
+                               break;
+                       }
+               }
+               break;
+       }
+
+       return address;
+}
+
+static void resolve_labels(struct assembler_context *ctx)
+{
+       struct code_output *c;
+       int addr;
+       int i;
+       unsigned int current_address;
+
+       /* Calculate the absolute addresses for each instruction. */
+recalculate_addresses:
+       current_address = 0;
+       list_for_each_entry(c, &ctx->output, list) {
+               switch (c->type) {
+               case OUT_INSN:
+                       c->address = current_address;
+                       current_address++;
+                       break;
+               case OUT_LABEL:
+                       c->address = current_address;
+                       break;
+               }
+       }
+
+       /* Resolve the symbolic label references. */
+       list_for_each_entry(c, &ctx->output, list) {
+               switch (c->type) {
+               case OUT_INSN:
+                       if (c->is_start_insn) {
+                               /* If the first %start-jump jumps to 001, we can
+                                * optimize it away, as it's unneeded.
+                                */
+                               i = 2;
+                               if (c->operands[i].type != OUTOPER_LABELREF)
+                                       asm_error(ctx, "Internal error, %%start insn oper 2 not labelref");
+                               if (c->operands[i].u.label->direction != LABELREF_ABSOLUTE)
+                                       asm_error(ctx, "%%start label reference not absolute");
+                               addr = get_labeladdress(ctx, c, c->operands[i].u.label);
+                               if (addr < 0)
+                                       goto does_not_exist;
+                               if (addr == 1) {
+                                       list_del(&c->list); /* Kill it */
+                                       goto recalculate_addresses;
+                               }
+                       }
+
+                       for (i = 0; i < ARRAY_SIZE(c->operands); i++) {
+                               if (c->operands[i].type != OUTOPER_LABELREF)
+                                       continue;
+                               addr = get_labeladdress(ctx, c, c->operands[i].u.label);
+                               if (addr < 0)
+                                       goto does_not_exist;
+                               c->operands[i].u.operand = addr;
+                               if (i != 2) /* Is not a jump target */
+                                       c->operands[i].u.operand |= 0xC00; /* Make it be an immediate */
+                       }
+                       break;
+               case OUT_LABEL:
+                       break;
+               }
+       }
+
+       return;
+does_not_exist:
+       asm_error(ctx, "Label \"%s\" does not exist",
+                 c->operands[i].u.label->name);
+}
+
+static void emit_code(struct assembler_context *ctx)
+{
+       FILE *fd;
+       char *fn;
+       size_t fn_len;
+       struct code_output *c;
+       uint64_t code;
+       unsigned char outbuf[8];
+       unsigned int insn_count = 0;
+       struct fw_header hdr;
+
+       fn_len = strlen(outfile_name) + 20;
+       fn = xmalloc(fn_len);
+       snprintf(fn, fn_len, "%s.ucode", outfile_name);
+       fd = fopen(fn, "w+");
+       if (!fd) {
+               fprintf(stderr, "Could not open microcode output file \"%s\"\n", fn);
+               free(fn);
+               exit(1);
+       }
+       if (IS_VERBOSE_DEBUG)
+               fprintf(stderr, "\nCode:\n");
+
+       list_for_each_entry(c, &ctx->output, list) {
+               switch (c->type) {
+               case OUT_INSN:
+                       insn_count++;
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       memset(&hdr, 0, sizeof(hdr));
+       hdr.type = FW_TYPE_UCODE;
+       hdr.ver = FW_HDR_VER;
+       hdr.size = cpu_to_be32(8 * insn_count);
+       if (fwrite(&hdr, sizeof(hdr), 1, fd) != 1) {
+               fprintf(stderr, "Could not write microcode outfile\n");
+               exit(1);
+       }
+
+       if (insn_count > NUM_INSN_LIMIT)
+               asm_warn(ctx, "Generating more than %d instructions. This "
+                             "will overflow the device microcode memory.",
+                        NUM_INSN_LIMIT);
+
+       list_for_each_entry(c, &ctx->output, list) {
+               switch (c->type) {
+               case OUT_INSN:
+                       if (IS_VERBOSE_DEBUG) {
+                               fprintf(stderr, "%03X %03X,%03X,%03X\n",
+                                       c->opcode,
+                                       c->operands[0].u.operand,
+                                       c->operands[1].u.operand,
+                                       c->operands[2].u.operand);
+                       }
+                       code = 0;
+
+                       /* Instruction binary format is: xxyy yzzz  0000 oooX
+                        * Big-Endian, X is the most significant part of Xxx.
+                        */
+                       code |= (c->opcode << 4);
+
+                       code |= (((uint64_t)c->operands[0].u.operand & 0xF00) >> 8);
+                       code |= (((uint64_t)c->operands[0].u.operand & 0x0FF) << 56);
+
+                       code |= ((uint64_t)c->operands[1].u.operand << 44);
+
+                       code |= ((uint64_t)c->operands[2].u.operand << 32);
+
+                       outbuf[7] = (code & 0x00000000000000FFULL);
+                       outbuf[6] = (code & 0x000000000000FF00ULL) >> 8;
+                       outbuf[5] = (code & 0x0000000000FF0000ULL) >> 16;
+                       outbuf[4] = (code & 0x00000000FF000000ULL) >> 24;
+                       outbuf[3] = (code & 0x000000FF00000000ULL) >> 32;
+                       outbuf[2] = (code & 0x0000FF0000000000ULL) >> 40;
+                       outbuf[1] = (code & 0x00FF000000000000ULL) >> 48;
+                       outbuf[0] = (code & 0xFF00000000000000ULL) >> 56;
+
+                       if (fwrite(&outbuf, ARRAY_SIZE(outbuf), 1, fd) != 1) {
+                               fprintf(stderr, "Could not write microcode outfile\n");
+                               exit(1);
+                       }
+                       break;
+               case OUT_LABEL:
+                       break;
+               }
+       }
+       fclose(fd);
+       free(fn);
+}
+
+static void assemble(void)
+{
+       struct assembler_context ctx;
+
+       memset(&ctx, 0, sizeof(ctx));
+       INIT_LIST_HEAD(&ctx.output);
+
+       eval_directives(&ctx);
+       assemble_instructions(&ctx);
+       resolve_labels(&ctx);
+       emit_code(&ctx);
+}
+
+static void initialize(void)
+{
+       INIT_LIST_HEAD(&infile.sl);
+       INIT_LIST_HEAD(&infile.ivals);
+#ifdef YYDEBUG
+       if (IS_INSANE_DEBUG)
+               yydebug = 1;
+       else
+               yydebug = 0;
+#endif /* YYDEBUG */
+}
+
+int main(int argc, char **argv)
+{
+       int err, res = 1;
+
+       err = parse_args(argc, argv);
+       if (err)
+               goto out;
+       err = open_input_file();
+       if (err)
+               goto out;
+       initialize();
+       yyparse();
+       assemble();
+       assemble_initvals();
+       close_input_file();
+       res = 0;
+out:
+       /* Lazyman simply leaks all allocated memory. */
+       return res;
+}
diff --git a/assembler/main.h b/assembler/main.h
new file mode 100644 (file)
index 0000000..d309577
--- /dev/null
@@ -0,0 +1,154 @@
+#ifndef BCM43xx_ASM_MAIN_H_
+#define BCM43xx_ASM_MAIN_H_
+
+#include <stdint.h>
+
+#include "list.h"
+#include "util.h"
+
+
+/* The header that fwcutter also puts in to every .fw file */
+struct fw_header {
+       /* Type of the firmware data */
+       uint8_t type;
+       /* Version number of the firmware data format */
+       uint8_t ver;
+       uint8_t __padding[2];
+       /* Size of the data. For ucode and PCM this is in bytes.
+        * For IV this is in number-of-ivs. (big-endian!) */
+       be32_t size;
+} __attribute__ ((__packed__));
+
+/* struct fw_header -> type */
+#define FW_TYPE_UCODE  'u'
+#define FW_TYPE_PCM    'p'
+#define FW_TYPE_IV     'i'
+/* struct fw_header -> ver */
+#define FW_HDR_VER     0x01
+
+
+/* Maximum number of allowed instructions in the code output.
+ * This is what device memory can hold at maximum.
+ */
+#define NUM_INSN_LIMIT 4096
+
+
+struct lineinfo {
+       char file[64];
+       char linecopy[128];
+       unsigned int lineno;
+       unsigned int column;
+};
+extern struct lineinfo cur_lineinfo;
+
+
+struct immediate {
+       unsigned int imm;
+};
+
+struct address {
+       unsigned int addr;
+};
+
+struct registr {
+       int type; /* SPR, GPR or OFFR */
+       unsigned int nr;
+};
+
+struct memory {
+       enum {
+               MEM_DIRECT,
+               MEM_INDIRECT,
+       } type;
+       /* Offset immediate */
+       unsigned int offset;
+       /* Offset Register number (only MEM_INDIRECT) */
+       unsigned int offr_nr;
+};
+
+struct label {
+       const char *name;
+
+       /* direction is only used for label references. */
+       enum {
+               LABELREF_ABSOLUTE,
+               LABELREF_RELATIVE_BACK,
+               LABELREF_RELATIVE_FORWARD,
+       } direction;
+};
+
+struct operand {
+       enum {
+               OPER_IMM,
+               OPER_REG,
+               OPER_MEM,
+               OPER_LABEL,
+               OPER_ADDR,
+               OPER_RAW,
+       } type;
+       union {
+               struct immediate *imm;
+               struct registr *reg;
+               struct memory *mem;
+               struct label *label;
+               struct address *addr;
+               unsigned int raw;
+       } u;
+};
+
+struct operlist {
+       struct operand *oper[5];
+};
+
+struct instruction {
+       int op;
+       struct operlist *operands;
+       unsigned int opcode; /* only for RAW */
+};
+
+struct asmdir {
+       enum {
+               ADIR_ARCH,
+               ADIR_START,
+       } type;
+       union {
+               enum {
+                       OLDWORLD,
+                       NEWWORLD,
+               } arch;
+               struct label *start;
+       } u;
+};
+
+struct statement {
+       enum {
+               STMT_INSN,
+               STMT_LABEL,
+               STMT_ASMDIR,
+       } type;
+       union {
+               struct instruction *insn;
+               struct label *label;
+               struct asmdir *asmdir;
+       } u;
+       struct lineinfo info;
+
+       struct list_head list;
+};
+
+
+struct file {
+       /* The (microcode) statement list */
+       struct list_head sl;
+       /* The initvals sections list */
+       struct list_head ivals;
+       /* The file descriptor */
+       int fd;
+};
+
+
+extern struct file infile;
+extern const char *infile_name;
+extern const char *outfile_name;
+
+#endif /* BCM43xx_ASM_MAIN_H_ */
diff --git a/assembler/parser.y b/assembler/parser.y
new file mode 100644 (file)
index 0000000..753be08
--- /dev/null
@@ -0,0 +1,1314 @@
+%{
+
+/*
+ *   Copyright (C) 2006-2007  Michael Buesch <mb@bu3sch.de>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License version 2
+ *   as published by the Free Software Foundation.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ */
+
+#include "main.h"
+#include "initvals.h"
+#include "util.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+extern char *yytext;
+extern void yyerror(char *);
+extern int yyparse(void);
+extern int yylex(void);
+
+static struct operand * store_oper_sanity(struct operand *oper);
+
+/* The current .section */
+extern int section;
+/* Pointer to the current initvals section data structure. */
+extern struct initvals_sect *cur_initvals_sect;
+
+%}
+
+%token SECTION_TEXT SECTION_IVALS
+
+%token ASM_ARCH ASM_START SPR GPR OFFR LR COMMA BRACK_OPEN BRACK_CLOSE PAREN_OPEN PAREN_CLOSE HEXNUM DECNUM ARCH_NEWWORLD ARCH_OLDWORLD LABEL IDENT LABELREF
+
+%token PLUS MINUS MULTIPLY DIVIDE BITW_OR BITW_AND BITW_XOR BITW_NOT LEFTSHIFT RIGHTSHIFT
+
+%token OP_ADD OP_ADDSC OP_ADDC OP_ADDSCC OP_SUB OP_SUBSC OP_SUBC OP_SUBSCC OP_SRA OP_OR OP_AND OP_XOR OP_SR OP_SRX OP_SL OP_RL OP_RR OP_NAND OP_ORX OP_MOV OP_JMP OP_JAND OP_JNAND OP_JS OP_JNS OP_JE OP_JNE OP_JLS OP_JGES OP_JGS OP_JLES OP_JL OP_JGE OP_JG OP_JLE OP_JZX OP_JNZX OP_JEXT OP_JNEXT OP_CALL OP_RET OP_TKIPH OP_TKIPHS OP_TKIPL OP_TKIPLS OP_NAP RAW_CODE
+
+%token IVAL_MMIO16 IVAL_MMIO32 IVAL_PHY IVAL_RADIO IVAL_SHM16 IVAL_SHM32
+
+%start line
+
+%%
+
+line   : /* empty */
+       | line statement {
+               struct statement *s = $2;
+               if (section != SECTION_TEXT)
+                       yyerror("Microcode text instruction in non .text section");
+               memcpy(&s->info, &cur_lineinfo, sizeof(struct lineinfo));
+               list_add_tail(&s->list, &infile.sl);
+         }
+       | line section_switch {
+         }
+       | line ivals_write {
+               struct initval_op *io = $2;
+               if (section != SECTION_IVALS)
+                       yyerror("InitVals write in non .initvals section");
+               memcpy(&io->info, &cur_lineinfo, sizeof(struct lineinfo));
+               INIT_LIST_HEAD(&io->list);
+               list_add_tail(&io->list, &cur_initvals_sect->ops);
+         }
+       ;
+
+section_switch : SECTION_TEXT {
+                       section = SECTION_TEXT;
+                 }
+               | SECTION_IVALS PAREN_OPEN identifier PAREN_CLOSE {
+                       const char *sectname = $3;
+                       struct initvals_sect *s;
+                       cur_initvals_sect = NULL;
+                       /* Search if there already is a section by that name. */
+                       list_for_each_entry(s, &infile.ivals, list) {
+                               if (strcmp(sectname, s->name) == 0)
+                                       cur_initvals_sect = s;
+                       }
+                       if (!cur_initvals_sect) {
+                               /* Not found, create a new one. */
+                               s = xmalloc(sizeof(struct initvals_sect));
+                               s->name = sectname;
+                               INIT_LIST_HEAD(&s->ops);
+                               INIT_LIST_HEAD(&s->list);
+                               list_add_tail(&s->list, &infile.ivals);
+                               cur_initvals_sect = s;
+                       }
+                       section = SECTION_IVALS;
+                 }
+               ;
+
+ivals_write    : IVAL_MMIO16 hexnum_decnum COMMA hexnum_decnum {
+                       struct initval_op *iop = xmalloc(sizeof(struct initval_op));
+                       iop->type = IVAL_W_MMIO16;
+                       iop->args[0] = (unsigned int)(unsigned long)$2;
+                       iop->args[1] = (unsigned int)(unsigned long)$4;
+                       $$ = iop;
+                 }
+               | IVAL_MMIO32 hexnum_decnum COMMA hexnum_decnum {
+                       struct initval_op *iop = xmalloc(sizeof(struct initval_op));
+                       iop->type = IVAL_W_MMIO32;
+                       iop->args[0] = (unsigned int)(unsigned long)$2;
+                       iop->args[1] = (unsigned int)(unsigned long)$4;
+                       $$ = iop;
+                 }
+               | IVAL_PHY hexnum_decnum COMMA hexnum_decnum {
+                       struct initval_op *iop = xmalloc(sizeof(struct initval_op));
+                       iop->type = IVAL_W_PHY;
+                       iop->args[0] = (unsigned int)(unsigned long)$2;
+                       iop->args[1] = (unsigned int)(unsigned long)$4;
+                       $$ = iop;
+                 }
+               | IVAL_RADIO hexnum_decnum COMMA hexnum_decnum {
+                       struct initval_op *iop = xmalloc(sizeof(struct initval_op));
+                       iop->type = IVAL_W_RADIO;
+                       iop->args[0] = (unsigned int)(unsigned long)$2;
+                       iop->args[1] = (unsigned int)(unsigned long)$4;
+                       $$ = iop;
+                 }
+               | IVAL_SHM16 hexnum_decnum COMMA hexnum_decnum COMMA hexnum_decnum {
+                       struct initval_op *iop = xmalloc(sizeof(struct initval_op));
+                       iop->type = IVAL_W_SHM16;
+                       iop->args[0] = (unsigned int)(unsigned long)$2;
+                       iop->args[1] = (unsigned int)(unsigned long)$4;
+                       iop->args[2] = (unsigned int)(unsigned long)$6;
+                       $$ = iop;
+                 }
+               | IVAL_SHM32 hexnum_decnum COMMA hexnum_decnum COMMA hexnum_decnum {
+                       struct initval_op *iop = xmalloc(sizeof(struct initval_op));
+                       iop->type = IVAL_W_SHM32;
+                       iop->args[0] = (unsigned int)(unsigned long)$2;
+                       iop->args[1] = (unsigned int)(unsigned long)$4;
+                       iop->args[2] = (unsigned int)(unsigned long)$6;
+                       $$ = iop;
+                 }
+               ;
+
+statement      : asmdir {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_ASMDIR;
+                       s->u.asmdir = $1;
+                       $$ = s;
+                 }
+               | label {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_LABEL;
+                       s->u.label = $1;
+                       $$ = s;
+                 }
+               | insn_add {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_addsc {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_addc {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_addscc {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_sub {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_subsc {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_subc {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_subscc {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_sra {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_or {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_and {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_xor {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_sr {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_srx {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_sl {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_rl {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_rr {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_nand {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_orx {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_mov {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_jmp {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_jand {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_jnand {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_js {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_jns {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_je {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_jne {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_jls {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_jges {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_jgs {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_jles {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_jl {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_jge {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_jg {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_jle {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_jzx {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_jnzx {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_jext {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_jnext {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_call {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_ret {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_tkiph {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_tkiphs {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_tkipl {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_tkipls {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_nap {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               | insn_raw {
+                       struct statement *s = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&s->list);
+                       s->type = STMT_INSN;
+                       s->u.insn = $1;
+                       $$ = s;
+                 }
+               ;
+
+/* ASM directives */
+asmdir         : asmarch {
+                       $$ = $1;
+                 }
+               | ASM_START identifier {
+                       struct asmdir *ad = xmalloc(sizeof(struct asmdir));
+                       struct label *label = xmalloc(sizeof(struct label));
+                       label->name = $2;
+                       label->direction = LABELREF_ABSOLUTE;
+                       ad->type = ADIR_START;
+                       ad->u.start = label;
+                       $$ = ad;
+                 }
+               ;
+
+asmarch                : ASM_ARCH ARCH_NEWWORLD {
+                       struct asmdir *ad = xmalloc(sizeof(struct asmdir));
+                       ad->type = ADIR_ARCH;
+                       ad->u.arch = NEWWORLD;
+                       $$ = ad;
+                 }
+               | ASM_ARCH ARCH_OLDWORLD {
+                       struct asmdir *ad = xmalloc(sizeof(struct asmdir));
+                       ad->type = ADIR_ARCH;
+                       ad->u.arch = OLDWORLD;
+                       $$ = ad;
+                 }
+               ;
+
+label          : LABEL {
+                       struct label *label = xmalloc(sizeof(struct label));
+                       char *l;
+                       l = xstrdup(yytext);
+                       l[strlen(l) - 1] = '\0';
+                       label->name = l;
+                       $$ = label;
+                 }
+               ;
+
+/* add */
+insn_add       : OP_ADD operlist_3 {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       insn->op = OP_ADD;
+                       insn->operands = $2;
+                       $$ = insn;
+                 }
+               ;
+
+/* add. */
+insn_addsc     : OP_ADDSC operlist_3 {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       insn->op = OP_ADDSC;
+                       insn->operands = $2;
+                       $$ = insn;
+                 }
+               ;
+
+/* addc */
+insn_addc      : OP_ADDC operlist_3 {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       insn->op = OP_ADDC;
+                       insn->operands = $2;
+                       $$ = insn;
+                 }
+               ;
+
+/* addc. */
+insn_addscc    : OP_ADDSCC operlist_3 {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       insn->op = OP_ADDSCC;
+                       insn->operands = $2;
+                       $$ = insn;
+                 }
+               ;
+
+/* sub */
+insn_sub       : OP_SUB operlist_3 {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       insn->op = OP_SUB;
+                       insn->operands = $2;
+                       $$ = insn;
+                 }
+               ;
+
+/* sub. */
+insn_subsc     : OP_SUBSC operlist_3 {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       insn->op = OP_SUBSC;
+                       insn->operands = $2;
+                       $$ = insn;
+                 }
+               ;
+
+/* subc */
+insn_subc      : OP_SUBC operlist_3 {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       insn->op = OP_SUBC;
+                       insn->operands = $2;
+                       $$ = insn;
+                 }
+               ;
+
+/* subc. */
+insn_subscc    : OP_SUBSCC operlist_3 {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       insn->op = OP_SUBSCC;
+                       insn->operands = $2;
+                       $$ = insn;
+                 }
+               ;
+
+insn_sra       : OP_SRA operlist_3 {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       insn->op = OP_SRA;
+                       insn->operands = $2;
+                       $$ = insn;
+                 }
+               ;
+
+insn_or                : OP_OR operlist_3 {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       insn->op = OP_OR;
+                       insn->operands = $2;
+                       $$ = insn;
+                 }
+               ;
+
+insn_and       : OP_AND operlist_3 {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       insn->op = OP_AND;
+                       insn->operands = $2;
+                       $$ = insn;
+                 }
+               ;
+
+insn_xor       : OP_XOR operlist_3 {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       insn->op = OP_XOR;
+                       insn->operands = $2;
+                       $$ = insn;
+                 }
+               ;
+
+insn_sr                : OP_SR operlist_3 {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       insn->op = OP_SR;
+                       insn->operands = $2;
+                       $$ = insn;
+                 }
+               ;
+
+insn_srx       : OP_SRX extended_operlist {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       insn->op = OP_SRX;
+                       insn->operands = $2;
+                       $$ = insn;
+                 }
+               ;
+
+insn_sl                : OP_SL operlist_3 {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       insn->op = OP_SL;
+                       insn->operands = $2;
+                       $$ = insn;
+                 }
+               ;
+
+insn_rl                : OP_RL operlist_3 {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       insn->op = OP_RL;
+                       insn->operands = $2;
+                       $$ = insn;
+                 }
+               ;
+
+insn_rr                : OP_RR operlist_3 {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       insn->op = OP_RR;
+                       insn->operands = $2;
+                       $$ = insn;
+                 }
+               ;
+
+insn_nand      : OP_NAND operlist_3 {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       insn->op = OP_NAND;
+                       insn->operands = $2;
+                       $$ = insn;
+                 }
+               ;
+
+insn_orx       : OP_ORX extended_operlist {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       insn->op = OP_ORX;
+                       insn->operands = $2;
+                       $$ = insn;
+                 }
+               ;
+
+insn_mov       : OP_MOV operlist_2 {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       insn->op = OP_MOV;
+                       insn->operands = $2;
+                       $$ = insn;
+                 }
+               ;
+
+insn_jmp       : OP_JMP labelref {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       struct operlist *ol = xmalloc(sizeof(struct operlist));
+                       ol->oper[0] = $2;
+                       insn->op = OP_JMP;
+                       insn->operands = ol;
+                       $$ = insn;
+                 }
+               ;
+
+insn_jand      : OP_JAND operlist_3 {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       insn->op = OP_JAND;
+                       insn->operands = $2;
+                       $$ = insn;
+                 }
+               ;
+
+insn_jnand     : OP_JNAND operlist_3 {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       insn->op = OP_JNAND;
+                       insn->operands = $2;
+                       $$ = insn;
+                 }
+               ;
+
+insn_js                : OP_JS operlist_3 {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       insn->op = OP_JS;
+                       insn->operands = $2;
+                       $$ = insn;
+                 }
+               ;
+
+insn_jns       : OP_JNS operlist_3 {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       insn->op = OP_JNS;
+                       insn->operands = $2;
+                       $$ = insn;
+                 }
+               ;
+
+insn_je                : OP_JE operlist_3 {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       insn->op = OP_JE;
+                       insn->operands = $2;
+                       $$ = insn;
+                 }
+               ;
+
+insn_jne       : OP_JNE operlist_3 {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       insn->op = OP_JNE;
+                       insn->operands = $2;
+                       $$ = insn;
+                 }
+               ;
+
+insn_jls       : OP_JLS operlist_3 {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       insn->op = OP_JLS;
+                       insn->operands = $2;
+                       $$ = insn;
+                 }
+               ;
+
+insn_jges      : OP_JGES operlist_3 {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       insn->op = OP_JGES;
+                       insn->operands = $2;
+                       $$ = insn;
+                 }
+               ;
+
+insn_jgs       : OP_JGS operlist_3 {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       insn->op = OP_JGS;
+                       insn->operands = $2;
+                       $$ = insn;
+                 }
+               ;
+
+insn_jles      : OP_JLES operlist_3 {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       insn->op = OP_JLES;
+                       insn->operands = $2;
+                       $$ = insn;
+                 }
+               ;
+
+insn_jl                : OP_JL operlist_3 {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       insn->op = OP_JL;
+                       insn->operands = $2;
+                       $$ = insn;
+                 }
+               ;
+
+insn_jge       : OP_JGE operlist_3 {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       insn->op = OP_JGE;
+                       insn->operands = $2;
+                       $$ = insn;
+                 }
+               ;
+
+insn_jg                : OP_JG operlist_3 {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       insn->op = OP_JG;
+                       insn->operands = $2;
+                       $$ = insn;
+                 }
+               ;
+
+insn_jle       : OP_JLE operlist_3 {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       insn->op = OP_JLE;
+                       insn->operands = $2;
+                       $$ = insn;
+                 }
+               ;
+
+insn_jzx       : OP_JZX extended_operlist {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       insn->op = OP_JZX;
+                       insn->operands = $2;
+                       $$ = insn;
+                 }
+               ;
+
+insn_jnzx      : OP_JNZX extended_operlist {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       insn->op = OP_JNZX;
+                       insn->operands = $2;
+                       $$ = insn;
+                 }
+               ;
+
+insn_jext      : OP_JEXT external_jump_operands {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       insn->op = OP_JEXT;
+                       insn->operands = $2;
+                       $$ = insn;
+                 }
+               ;
+
+insn_jnext     : OP_JNEXT external_jump_operands {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       insn->op = OP_JNEXT;
+                       insn->operands = $2;
+                       $$ = insn;
+                 }
+               ;
+
+linkreg                : LR regnr {
+                       $$ = $2;
+                 }
+               ;
+
+insn_call      : OP_CALL linkreg COMMA labelref {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       struct operlist *ol = xmalloc(sizeof(struct operlist));
+                       struct operand *oper_lr = xmalloc(sizeof(struct operand));
+                       struct operand *oper_zero = xmalloc(sizeof(struct operand));
+                       oper_zero->type = OPER_RAW;
+                       oper_zero->u.raw = 0;
+                       oper_lr->type = OPER_RAW;
+                       oper_lr->u.raw = (unsigned long)$2;
+                       ol->oper[0] = oper_lr;
+                       ol->oper[1] = oper_zero;
+                       ol->oper[2] = $4;
+                       insn->op = OP_CALL;
+                       insn->operands = ol;
+                       $$ = insn;
+                 }
+               ;
+
+insn_ret       : OP_RET linkreg COMMA linkreg {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       struct operlist *ol = xmalloc(sizeof(struct operlist));
+                       struct operand *oper_lr0 = xmalloc(sizeof(struct operand));
+                       struct operand *oper_lr1 = xmalloc(sizeof(struct operand));
+                       struct operand *oper_zero = xmalloc(sizeof(struct operand));
+                       oper_zero->type = OPER_RAW;
+                       oper_zero->u.raw = 0;
+                       oper_lr0->type = OPER_RAW;
+                       oper_lr0->u.raw = (unsigned long)$2;
+                       oper_lr1->type = OPER_RAW;
+                       oper_lr1->u.raw = (unsigned long)$4;
+                       ol->oper[0] = oper_lr0;
+                       ol->oper[1] = oper_zero;
+                       ol->oper[2] = oper_lr1;
+                       insn->op = OP_RET;
+                       insn->operands = ol;
+                       $$ = insn;
+                 }
+               ;
+
+insn_tkiph     : OP_TKIPH operlist_2 {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       struct operlist *ol = $2;
+                       struct operand *flags = xmalloc(sizeof(struct operand));
+                       struct immediate *imm = xmalloc(sizeof(struct immediate));
+                       imm->imm = 0x1;
+                       flags->type = OPER_IMM;
+                       flags->u.imm = imm;
+                       ol->oper[2] = ol->oper[1];
+                       ol->oper[1] = flags;
+                       insn->op = OP_TKIPH;
+                       insn->operands = ol;
+                       $$ = insn;
+                 }
+               ;
+
+insn_tkiphs    : OP_TKIPHS operlist_2 {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       struct operlist *ol = $2;
+                       struct operand *flags = xmalloc(sizeof(struct operand));
+                       struct immediate *imm = xmalloc(sizeof(struct immediate));
+                       imm->imm = 0x1 | 0x2;
+                       flags->type = OPER_IMM;
+                       flags->u.imm = imm;
+                       ol->oper[2] = ol->oper[1];
+                       ol->oper[1] = flags;
+                       insn->op = OP_TKIPH;
+                       insn->operands = ol;
+                       $$ = insn;
+                 }
+               ;
+
+insn_tkipl     : OP_TKIPL operlist_2 {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       struct operlist *ol = $2;
+                       struct operand *flags = xmalloc(sizeof(struct operand));
+                       struct immediate *imm = xmalloc(sizeof(struct immediate));
+                       imm->imm = 0x0;
+                       flags->type = OPER_IMM;
+                       flags->u.imm = imm;
+                       ol->oper[2] = ol->oper[1];
+                       ol->oper[1] = flags;
+                       insn->op = OP_TKIPH;
+                       insn->operands = ol;
+                       $$ = insn;
+                 }
+               ;
+
+insn_tkipls    : OP_TKIPLS operlist_2 {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       struct operlist *ol = $2;
+                       struct operand *flags = xmalloc(sizeof(struct operand));
+                       struct immediate *imm = xmalloc(sizeof(struct immediate));
+                       imm->imm = 0x0 | 0x2;
+                       flags->type = OPER_IMM;
+                       flags->u.imm = imm;
+                       ol->oper[2] = ol->oper[1];
+                       ol->oper[1] = flags;
+                       insn->op = OP_TKIPH;
+                       insn->operands = ol;
+                       $$ = insn;
+                 }
+               ;
+
+insn_nap       : OP_NAP {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       struct operlist *ol = xmalloc(sizeof(struct operlist));
+                       struct operand *regop = xmalloc(sizeof(struct operand));
+                       struct operand *zeroop = xmalloc(sizeof(struct operand));
+                       struct registr *r0 = xmalloc(sizeof(struct registr));
+                       r0->type = GPR;
+                       r0->nr = 0;
+                       regop->type = OPER_REG;
+                       regop->u.reg = r0;
+                       zeroop->type = OPER_RAW;
+                       zeroop->u.raw = 0x000;
+                       ol->oper[0] = regop;
+                       ol->oper[1] = regop;
+                       ol->oper[2] = zeroop;
+                       insn->op = OP_NAP;
+                       insn->operands = ol;
+                       $$ = insn;
+                 }
+               ;
+
+insn_raw       : raw_code operlist_3 {
+                       struct instruction *insn = xmalloc(sizeof(struct instruction));
+                       insn->op = RAW_CODE;
+                       insn->operands = $2;
+                       insn->opcode = (unsigned long)$1;
+                       $$ = insn;
+                 }
+               ;
+
+raw_code       : RAW_CODE {
+                       yytext++; /* skip @ */
+                       $$ = (void *)(unsigned long)strtoul(yytext, NULL, 16);
+                 }
+               ;
+
+extended_operlist : decnum COMMA decnum COMMA operand COMMA operand COMMA operand {
+                       struct operlist *ol = xmalloc(sizeof(struct operlist));
+                       struct operand *mask_oper = xmalloc(sizeof(struct operand));
+                       struct operand *shift_oper = xmalloc(sizeof(struct operand));
+                       mask_oper->type = OPER_RAW;
+                       mask_oper->u.raw = (unsigned long)$1;
+                       shift_oper->type = OPER_RAW;
+                       shift_oper->u.raw = (unsigned long)$3;
+                       ol->oper[0] = mask_oper;
+                       ol->oper[1] = shift_oper;
+                       ol->oper[2] = $5;
+                       ol->oper[3] = $7;
+                       ol->oper[4] = store_oper_sanity($9);
+                       $$ = ol;
+                 }
+               ;
+
+external_jump_operands : hexnum COMMA operand COMMA operand COMMA labelref {
+                       struct operlist *ol = xmalloc(sizeof(struct operlist));
+                       struct operand *cond = xmalloc(sizeof(struct operand));
+                       cond->type = OPER_RAW;
+                       cond->u.raw = (unsigned long)$1;
+                       ol->oper[0] = cond;
+                       ol->oper[1] = $3;
+                       ol->oper[2] = $5;
+                       ol->oper[3] = $7;
+                       $$ = ol;
+                 }
+               ;
+
+operlist_2     : operand COMMA operand {
+                       struct operlist *ol = xmalloc(sizeof(struct operlist));
+                       ol->oper[0] = $1;
+                       ol->oper[1] = store_oper_sanity($3);
+                       $$ = ol;
+                 }
+               ;
+
+operlist_3     : operand COMMA operand COMMA operand {
+                       struct operlist *ol = xmalloc(sizeof(struct operlist));
+                       ol->oper[0] = $1;
+                       ol->oper[1] = $3;
+                       ol->oper[2] = store_oper_sanity($5);
+                       $$ = ol;
+                 }
+               ;
+
+operand                : reg {
+                       struct operand *oper = xmalloc(sizeof(struct operand));
+                       oper->type = OPER_REG;
+                       oper->u.reg = $1;
+                       $$ = oper;
+                 }
+               | mem {
+                       struct operand *oper = xmalloc(sizeof(struct operand));
+                       oper->type = OPER_MEM;
+                       oper->u.mem = $1;
+                       $$ = oper;
+                 }
+               | raw_code {
+                       struct operand *oper = xmalloc(sizeof(struct operand));
+                       oper->type = OPER_RAW;
+                       oper->u.raw = (unsigned long)$1;
+                       $$ = oper;
+                 }
+               | imm {
+                       struct operand *oper = xmalloc(sizeof(struct operand));
+                       oper->type = OPER_IMM;
+                       oper->u.imm = $1;
+                       $$ = oper;
+                 }
+               | labelref {
+                       $$ = $1;
+                 }
+               ;
+
+reg            : GPR regnr {
+                       struct registr *reg = xmalloc(sizeof(struct registr));
+                       reg->type = GPR;
+                       reg->nr = (unsigned long)$2;
+                       $$ = reg;
+                 }
+               | SPR {
+                       struct registr *reg = xmalloc(sizeof(struct registr));
+                       reg->type = SPR;
+                       yytext += 3; /* skip "spr" */
+                       reg->nr = strtoul(yytext, NULL, 16);
+                       $$ = reg;
+                 }
+               | OFFR regnr {
+                       struct registr *reg = xmalloc(sizeof(struct registr));
+                       reg->type = OFFR;
+                       reg->nr = (unsigned long)$2;
+                       $$ = reg;
+                 }
+               ;
+
+mem            : BRACK_OPEN hexnum_decnum BRACK_CLOSE {
+                       struct memory *mem = xmalloc(sizeof(struct memory));
+                       mem->type = MEM_DIRECT;
+                       mem->offset = (unsigned long)$2;
+                       $$ = mem;
+                 }
+               | BRACK_OPEN hexnum_decnum COMMA OFFR regnr BRACK_CLOSE {
+                       struct memory *mem = xmalloc(sizeof(struct memory));
+                       mem->type = MEM_INDIRECT;
+                       mem->offset = (unsigned long)$2;
+                       mem->offr_nr = (unsigned long)$5;
+                       $$ = mem;
+                 }
+               ;
+
+imm            : hexnum {
+                       struct immediate *imm = xmalloc(sizeof(struct immediate));
+                       imm->imm = (unsigned long)$1;
+                       $$ = imm;
+                 }
+               | decnum {
+                       struct immediate *imm = xmalloc(sizeof(struct immediate));
+                       imm->imm = (unsigned long)$1;
+                       $$ = imm;
+                 }
+               | complex_imm {
+                       struct immediate *imm = xmalloc(sizeof(struct immediate));
+                       imm->imm = (unsigned long)$1;
+                       $$ = imm;
+                 }
+               ;
+
+complex_imm    : PAREN_OPEN complex_imm_arg complex_imm_oper complex_imm_arg PAREN_CLOSE {
+                       unsigned long a = (unsigned long)$2;
+                       unsigned long b = (unsigned long)$4;
+                       unsigned long operation = (unsigned long)$3;
+                       unsigned long res = 31337;
+                       switch (operation) {
+                       case PLUS:
+                               res = a + b;
+                               break;
+                       case MINUS:
+                               res = a - b;
+                               break;
+                       case MULTIPLY:
+                               res = a * b;
+                               break;
+                       case DIVIDE:
+                               res = a / b;
+                               break;
+                       case BITW_OR:
+                               res = a | b;
+                               break;
+                       case BITW_AND:
+                               res = a & b;
+                               break;
+                       case BITW_XOR:
+                               res = a ^ b;
+                               break;
+                       case LEFTSHIFT:
+                               res = a << b;
+                               break;
+                       case RIGHTSHIFT:
+                               res = a >> b;
+                               break;
+                       default:
+                               yyerror("Internal parser BUG. complex_imm oper unknown");
+                       }
+                       $$ = (void *)res;
+                 }
+               | PAREN_OPEN complex_imm PAREN_CLOSE {
+                       $$ = $2;
+                 }
+               | PAREN_OPEN BITW_NOT complex_imm PAREN_CLOSE {
+                       unsigned long n = (unsigned long)$3;
+                       n = ~n;
+                       $$ = (void *)n;
+                 }
+               | PAREN_OPEN complex_imm_const PAREN_CLOSE {
+                       $$ = $2;
+                 }
+               ;
+
+complex_imm_oper : PLUS {
+                       $$ = (void *)(unsigned long)PLUS;
+                 }
+               | MINUS {
+                       $$ = (void *)(unsigned long)MINUS;
+                 }
+               | MULTIPLY {
+                       $$ = (void *)(unsigned long)MULTIPLY;
+                 }
+               | DIVIDE {
+                       $$ = (void *)(unsigned long)DIVIDE;
+                 }
+               | BITW_OR {
+                       $$ = (void *)(unsigned long)BITW_OR;
+                 }
+               | BITW_AND {
+                       $$ = (void *)(unsigned long)BITW_AND;
+                 }
+               | BITW_XOR {
+                       $$ = (void *)(unsigned long)BITW_XOR;
+                 }
+               | LEFTSHIFT {
+                       $$ = (void *)(unsigned long)LEFTSHIFT;
+                 }
+               | RIGHTSHIFT {
+                       $$ = (void *)(unsigned long)RIGHTSHIFT;
+                 }
+               ;
+
+complex_imm_arg        : complex_imm_const {
+                       $$ = $1;
+                 }
+               | complex_imm {
+                       $$ = $1;
+                 }
+               ;
+
+complex_imm_const : hexnum_decnum {
+                       $$ = $1;
+                 }
+               | BITW_NOT hexnum_decnum {
+                       unsigned long n = (unsigned long)$2;
+                       n = ~n;
+                       $$ = (void *)n;
+                 }
+               ;
+
+hexnum         : HEXNUM {
+                       while (yytext[0] != 'x') {
+                               if (yytext[0] == '\0')
+                                       yyerror("Internal HEXNUM parser error");
+                               yytext++;
+                       }
+                       yytext++;
+                       $$ = (void *)(unsigned long)strtoul(yytext, NULL, 16);
+                 }
+               ;
+
+decnum         : DECNUM {
+                       $$ = (void *)(unsigned long)strtol(yytext, NULL, 10);
+                 }
+               ;
+
+hexnum_decnum  : hexnum {
+                       $$ = $1;
+                 }
+               | decnum {
+                       $$ = $1;
+                 }
+               ;
+
+labelref       : identifier {
+                       struct operand *oper = xmalloc(sizeof(struct operand));
+                       struct label *label = xmalloc(sizeof(struct label));
+                       label->name = $1;
+                       label->direction = LABELREF_ABSOLUTE;
+                       oper->type = OPER_LABEL;
+                       oper->u.label = label;
+                       $$ = oper;
+                 }
+               | identifier MINUS {
+                       struct operand *oper = xmalloc(sizeof(struct operand));
+                       struct label *label = xmalloc(sizeof(struct label));
+                       label->name = $1;
+                       label->direction = LABELREF_RELATIVE_BACK;
+                       oper->type = OPER_LABEL;
+                       oper->u.label = label;
+                       $$ = oper;
+                 }
+               | identifier PLUS {
+                       struct operand *oper = xmalloc(sizeof(struct operand));
+                       struct label *label = xmalloc(sizeof(struct label));
+                       label->name = $1;
+                       label->direction = LABELREF_RELATIVE_FORWARD;
+                       oper->type = OPER_LABEL;
+                       oper->u.label = label;
+                       $$ = oper;
+                 }
+               ;
+
+regnr          : DECNUM {
+                       $$ = (void *)(unsigned long)strtoul(yytext, NULL, 10);
+                 }
+               ;
+
+identifier     : IDENT {
+                       $$ = xstrdup(yytext);
+                 }
+               ;
+
+%%
+
+int section = SECTION_TEXT; /* default to .text section */
+struct initvals_sect *cur_initvals_sect;
+
+void yyerror(char *str)
+{
+       unsigned int i;
+
+       fprintf(stderr,
+               "Parser ERROR (file \"%s\", line %u, col %u):\n",
+               cur_lineinfo.file,
+               cur_lineinfo.lineno,
+               cur_lineinfo.column);
+       fprintf(stderr, "%s\n", cur_lineinfo.linecopy);
+       for (i = 0; i < cur_lineinfo.column - 1; i++)
+               fprintf(stderr, " ");
+       fprintf(stderr, "^\n");
+       fprintf(stderr, "%s\n", str);
+       exit(1);
+}
+
+static struct operand * store_oper_sanity(struct operand *oper)
+{
+       if (oper->type == OPER_IMM &&
+           oper->u.imm->imm != 0) {
+               yyerror("Only 0x000 Immediate is allowed for "
+                       "Output operands");
+       }
+       return oper;
+}
diff --git a/assembler/scanner.l b/assembler/scanner.l
new file mode 100644 (file)
index 0000000..34559c6
--- /dev/null
@@ -0,0 +1,226 @@
+%{
+
+/*
+ *   Copyright (C) 2006-2007  Michael Buesch <mb@bu3sch.de>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License version 2
+ *   as published by the Free Software Foundation.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ */
+
+#include "parser.h"
+#include "main.h"
+
+#include <stdio.h>
+
+#undef min
+#define min(x,y)       ((x) < (y) ? (x) : (y))
+
+
+static void interpret_cppinfo(const char *);
+static void update_lineinfo(void);
+
+static inline void log_current_line(void)
+{
+       size_t len;
+
+       len = min(sizeof(cur_lineinfo.linecopy) - 1, strlen(yytext));
+       strncpy(cur_lineinfo.linecopy, yytext, len);
+       cur_lineinfo.linecopy[len] = '\0';
+}
+
+%}
+
+IDENTIFIER     ([a-zA-Z_][0-9a-zA-Z_]*)
+WS             ([ \t])
+NEWLINE                ((\r)|(\n)|(\r\n))
+
+%%
+
+^.*$                                   { log_current_line(); REJECT; }
+#\ [0-9]+\ \".*\"[ ]*[0-9]*{NEWLINE}   { interpret_cppinfo(yytext); }
+
+
+{WS}+                  { update_lineinfo(); /* whitespace */ }
+{NEWLINE}              { cur_lineinfo.lineno++; update_lineinfo(); }
+;.*$                   { update_lineinfo(); /* comment */ }
+
+^{WS}*"%"{WS}*arch     { update_lineinfo(); return ASM_ARCH; }
+^{WS}*"%"{WS}*start    { update_lineinfo(); return ASM_START; }
+
+^{WS}*\.text{WS}*$                             { update_lineinfo(); return SECTION_TEXT; }
+^{WS}*\.initvals/\({IDENTIFIER}\){WS}*{NEWLINE}        { update_lineinfo(); return SECTION_IVALS; }
+
+spr[0-9a-fA-F]{3,3}    { update_lineinfo(); return SPR; }
+r/([0-9]|([1-5][0-9])|(6[0-3]))        { update_lineinfo(); return GPR; }
+off/[0-6]              { update_lineinfo(); return OFFR; }
+lr/[0-3]               { update_lineinfo(); return LR; }
+
+,                      { update_lineinfo(); return COMMA; }
+\[                     { update_lineinfo(); return BRACK_OPEN; }
+\]                     { update_lineinfo(); return BRACK_CLOSE; }
+\(                     { update_lineinfo(); return PAREN_OPEN; }
+\)                     { update_lineinfo(); return PAREN_CLOSE; }
+
+\+                     { update_lineinfo(); return PLUS; }
+\-                     { update_lineinfo(); return MINUS; }
+\*                     { update_lineinfo(); return MULTIPLY; }
+\/                     { update_lineinfo(); return DIVIDE; }
+\|                     { update_lineinfo(); return BITW_OR; }
+\&                     { update_lineinfo(); return BITW_AND; }
+\^                     { update_lineinfo(); return BITW_XOR; }
+\~                     { update_lineinfo(); return BITW_NOT; }
+\<\<                   { update_lineinfo(); return LEFTSHIFT; }
+\>\>                   { update_lineinfo(); return RIGHTSHIFT; }
+
+add                    { update_lineinfo(); return OP_ADD; }
+add\.                  { update_lineinfo(); return OP_ADDSC; }
+addc                   { update_lineinfo(); return OP_ADDC; }
+addc\.                 { update_lineinfo(); return OP_ADDSCC; }
+
+sub                    { update_lineinfo(); return OP_SUB; }
+sub\.                  { update_lineinfo(); return OP_SUBSC; }
+subc                   { update_lineinfo(); return OP_SUBC; }
+subc\.                 { update_lineinfo(); return OP_SUBSCC; }
+
+sra                    { update_lineinfo(); return OP_SRA; }
+or                     { update_lineinfo(); return OP_OR; }
+and                    { update_lineinfo(); return OP_AND; }
+xor                    { update_lineinfo(); return OP_XOR; }
+sr                     { update_lineinfo(); return OP_SR; }
+srx                    { update_lineinfo(); return OP_SRX; }
+sl                     { update_lineinfo(); return OP_SL; }
+rl                     { update_lineinfo(); return OP_RL; }
+rr                     { update_lineinfo(); return OP_RR; }
+nand                   { update_lineinfo(); return OP_NAND; }
+orx                    { update_lineinfo(); return OP_ORX; }
+mov                    { update_lineinfo(); return OP_MOV; }
+
+jmp                    { update_lineinfo(); return OP_JMP; }
+jand                   { update_lineinfo(); return OP_JAND; }
+jnand                  { update_lineinfo(); return OP_JNAND; }
+js                     { update_lineinfo(); return OP_JS; }
+jns                    { update_lineinfo(); return OP_JNS; }
+je                     { update_lineinfo(); return OP_JE; }
+jne                    { update_lineinfo(); return OP_JNE; }
+jls                    { update_lineinfo(); return OP_JLS; }
+jges                   { update_lineinfo(); return OP_JGES; }
+jgs                    { update_lineinfo(); return OP_JGS; }
+jles                   { update_lineinfo(); return OP_JLES; }
+jl                     { update_lineinfo(); return OP_JL; }
+jge                    { update_lineinfo(); return OP_JGE; }
+jg                     { update_lineinfo(); return OP_JG; }
+jle                    { update_lineinfo(); return OP_JLE; }
+jzx                    { update_lineinfo(); return OP_JZX; }
+jnzx                   { update_lineinfo(); return OP_JNZX; }
+jext                   { update_lineinfo(); return OP_JEXT; }
+jnext                  { update_lineinfo(); return OP_JNEXT; }
+
+call                   { update_lineinfo(); return OP_CALL; }
+ret                    { update_lineinfo(); return OP_RET; }
+
+tkiph                  { update_lineinfo(); return OP_TKIPH; }
+tkiphs                 { update_lineinfo(); return OP_TKIPHS; }
+tkipl                  { update_lineinfo(); return OP_TKIPL; }
+tkipls                 { update_lineinfo(); return OP_TKIPLS; }
+
+nap                    { update_lineinfo(); return OP_NAP; }
+
+mmio16                 { update_lineinfo(); return IVAL_MMIO16; }
+mmio32                 { update_lineinfo(); return IVAL_MMIO32; }
+phy                    { update_lineinfo(); return IVAL_PHY; }
+radio                  { update_lineinfo(); return IVAL_RADIO; }
+shm16                  { update_lineinfo(); return IVAL_SHM16; }
+shm32                  { update_lineinfo(); return IVAL_SHM32; }
+
+@[0-9a-fA-F]{3,3}      { update_lineinfo(); return RAW_CODE; }
+
+0x[0-9a-fA-F]+         { update_lineinfo(); return HEXNUM; }
+-?[0-9]+               { update_lineinfo(); return DECNUM; }
+
+bcm43xx_newworld       { update_lineinfo(); return ARCH_NEWWORLD; }
+bcm43xx_oldworld       { update_lineinfo(); return ARCH_OLDWORLD; }
+
+{IDENTIFIER}:          { update_lineinfo(); return LABEL; }
+{IDENTIFIER}           { update_lineinfo(); return IDENT; }
+
+%%
+
+struct lineinfo cur_lineinfo;
+
+static void interpret_cppinfo(const char *str)
+{
+       const char * const orig = str;
+       char tmp[64];
+       char *found;
+
+       /* This will interpret lines added by CPP.
+        * They look like:
+        *     # 3 "file.asm" 1
+        */
+
+       if (*str == '\0')
+               goto error;
+       str++; /* skip # character */
+       if (*str == '\0')
+               goto error;
+       str++; /* skip whitespace */
+
+       /* Line number */
+       found = strchr(str, ' ');
+       if (!found)
+               goto error;
+       memset(tmp, 0, sizeof(tmp));
+       memcpy(tmp, str, min(sizeof(tmp) - 1,
+                            (int)(found - str)));
+       cur_lineinfo.lineno = strtoul(tmp, NULL, 10);
+       str = found;
+       str++;
+
+       /* File name */
+       if (*str != '\"')
+               goto error;
+       str++;
+       if (*str == '\0')
+               goto error;
+       found = strchr(str, '\"');
+       if (!found)
+               goto error;
+       memset(cur_lineinfo.file, 0, sizeof(cur_lineinfo.file));
+       memcpy(cur_lineinfo.file, str,
+              min(sizeof(cur_lineinfo.file) - 1,
+                  (int)(found - str)));
+
+       if (strcmp(cur_lineinfo.file, "<stdin>") == 0)
+               strcpy(cur_lineinfo.file, "Input File");
+
+       return;
+error:
+       fprintf(stderr, "Invalid CPP line directive:  %s\n", orig);
+       exit(1);
+}
+
+static void update_lineinfo(void)
+{
+       int i = 0;
+
+       while (yytext[i] != '\0') {
+               switch (yytext[i]) {
+               case '\r':
+               case '\n':
+                       cur_lineinfo.column = 0;
+                       break;
+               case '\t':
+                       cur_lineinfo.column += 8 - (cur_lineinfo.column % 8);
+                       break;
+               default:
+                       cur_lineinfo.column++;
+               }
+               i++;
+       }
+}
diff --git a/assembler/test.asm b/assembler/test.asm
new file mode 100644 (file)
index 0000000..0edadfc
--- /dev/null
@@ -0,0 +1,177 @@
+; This is a bcm43xx microcode assembly example.
+;
+; In this example file, r0 and r1 are always input
+; registers and r2 is output.
+; For input we can always have constant values or (one) memory
+; operand instead of the input registers shown here.
+;
+; Registers:
+;      GPRs:                   r0 - r63
+;      Offset Registers:       off0 - off5
+;      SPRs:                   spr000
+;
+; To access memory, two methods can be used. Examples follow.
+; Direct linear:
+;      mov r0,[0xCA]
+; Indirect through Offset Register (pointer):
+;      mov r0,[0xCA,off0]
+;
+
+
+%arch  bcm43xx_newworld        ; or bcm43xx-oldworld
+%start testlabel
+
+#define        PSM_BRC         spr848
+
+#define ECOND_MAC_ON   0x24
+
+
+.text
+
+label:
+       ; ADD instructions
+       add     r0,r1,r2        ; add
+       add.    r0,r1,r2        ; add, set carry
+       addc    r0,r1,r2        ; add with carry
+       addc.   r0,r1,r2        ; add with carry, set carry
+
+testlabel:
+       ; SUB instructions
+       sub     r0,r1,r2        ; sub
+       sub.    r0,r1,r2        ; sub, set carry
+       subc    r0,r1,r2        ; sub with carry
+       subc.   r0,r1,r2        ; sub with carry, set carry
+
+       sra     r0,r1,r2        ; arithmetic rightshift
+
+       ; Logical instructions
+       or      r0,r1,r2        ; bitwise OR
+       and     r0,r1,r2        ; bitwise AND
+       xor     r0,r1,r2        ; bitwise XOR
+       sr      r0,r1,r2        ; rightshift
+       sl      r0,r1,r2        ; leftshift
+
+       srx     7,8,r0,r1,r2    ; eXtended right shift (two input regs)
+
+       rl      r0,r1,r2        ; rotate left
+       rr      r0,r1,r2        ; rotate right
+       nand    r0,r1,r2        ; clear bits (notmask + and)
+
+       orx     7,8,r0,r1,r2    ; eXtended OR
+
+       ; Copy instruction. This is a virtual instruction
+       ; translated to more lowlevel stuff like OR.
+       mov     r0,r2           ; copy data
+
+       ; Jumps
+       jmp     label           ; unconditional jump
+       jand    r0,r1,label     ; jump if binary AND
+       jnand   r0,r1,label     ; jump if not binary AND
+       js      r0,r1,label     ; jump if all bits set
+       jns     r0,r1,label     ; jump if not all bits set
+       je      r0,r1,label     ; jump if equal
+       jne     r0,r1,label     ; jump if not equal
+       jls     r0,r1,label     ; jump if less (signed)
+       jges    r0,r1,label     ; jump if greater or equal (signed)
+       jgs     r0,r1,label     ; jump if greater (signed)
+       jles    r0,r1,label     ; jump if less or equal (signed)
+       jl      r0,r1,label     ; jump if less
+       jge     r0,r1,label     ; jump if greater or equal
+       jg      r0,r1,label     ; jump if greater
+       jle     r0,r1,label     ; jump if less or equal
+
+       jzx     7,8,r0,r1,label ; Jump if zero after shift and mask
+       jnzx    7,8,r0,r1,label ; Jump if nonzero after shift and mask
+
+       ; jump on external conditions
+       jext    ECOND_MAC_ON,r0,r0,label  ; jump if external condition is TRUE
+       jnext   ECOND_MAC_ON,r0,r0,label  ; jump if external condition is FALSE
+
+       ; Subroutines
+       call    lr0,label       ; store PC in lr0, call func at label
+       ret     lr0,lr1         ; store PC in lr0, return to lr1
+                               ; Both link registers can be the same
+                               ; and don't interfere.
+
+       ; TKIP sbox lookup
+       tkiph   r0,r2           ; Lookup high
+       tkiphs  r0,r2           ; Lookup high, byteswap
+       tkipl   r0,r2           ; Lookup low
+       tkipls  r0,r2           ; Lookup low, byteswap
+
+       nap                     ; sleep until event
+
+       ; raw instruction
+       @160    r0,r1,r2        ; equivalent to  or r0,r1,r2
+       @1C0    @C11, @C22, @BC3
+
+
+       ; Support for directional jumps.
+       ; Directional jumps can be used to conveniently jump inside of
+       ; functions without using function specific label prefixes. Note
+       ; that this does not establish a sub-namespace, though. "loop"
+       ; and "out" are still in the global namespace and can't be used
+       ; anymore for absolute jumps (Assembler will warn about duplication).
+function_a:
+       jl r0, r1, out+
+ loop:
+       nap
+       jmp loop-
+ out:
+       ret lr0, lr1
+
+function_b:
+       jl r0, r1, out+
+ loop:
+       nap
+       jmp loop-
+ out:
+       ret lr0, lr1
+
+
+; The assembler has support for fancy assemble-time
+; immediate constant expansion. This is called "complex immediates".
+; Complex immediates are _always_ clamped by parentheses. There is no
+; operator precedence. You must use parentheses to tell precedence.
+       mov     (2 + 3),r0
+       mov     (6 - 2),r0
+       mov     (2 * 3),r0
+       mov     (10 / 5),r0
+       mov     (1 | 2),r0
+       mov     (3 & 2),r0
+       mov     (3 ^ 2),r0
+       mov     (~1),r0
+       mov     (2 << 3),r0
+       mov     (8 >> 2),r0
+       mov     (1 << (0x3 + 2)),r0
+       mov     (1 + (2 + (3 + 4))),r0
+       mov     (4 >> (((((~5 | 0x21)))) | (~((10) & 2)))),r0
+
+
+; Some regression testing for the assembler follows
+       mov     2,off0                  ; test memory stuff
+       xor     0x124,r1,[0x0,off0]     ; test memory stuff
+       xor     0x124,r0,[0x0]          ; test memory stuff
+       mov     -34,r0                  ; negative dec numbers are supported
+       or      r0,r1,@BC2              ; We also support single raw operands
+       mov     0xEEEE,r0               ; MOV supports up to 16bit
+       jand    0x3800,r0,label         ; This is emulated by jnzx
+       jnand   0x3800,r0,label         ; This is emulated by jzx
+       or      spr06c,0,spr06c         ; Can have one spr input and one spr output
+       or      [0],0,[0]               ; Can have one mem input and one mem output
+       mov     testlabel, r0           ; Can use label as immediate value
+
+
+; The .initvals section generates an "Initial Values" file
+; with the name "foobar" in this example, which is uploaded
+; by the kernel driver on load. This is useful for writing ucode
+; specific values to the chip without bloating the small ucode
+; memory space with this initialization stuff.
+; Values are written in order they appear here.
+.initvals(foobar)
+       mmio16  0x1234, 0xABC                   ; Write 0x1234 to MMIO register 0xABC
+       mmio32  0x12345678, 0xABC               ; Write 0x12345678 to MMIO register 0xABC
+       phy     0x1234, 0xABC                   ; Write 0x1234 to PHY register 0xABC
+       radio   0x1234, 0xABC                   ; Write 0x1234 to RADIO register 0xABC
+       shm16   0x1234, 0x0001, 0x0002          ; Write 0x1234 to SHM routing 0x0001, register 0x0002
+       shm32   0x12345678, 0x0001, 0x0002      ; Write 0x12345678 to SHM routing 0x0001, register 0x0002
diff --git a/assembler/util.c b/assembler/util.c
new file mode 100644 (file)
index 0000000..2f4f7a0
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ *   Copyright (C) 2006  Michael Buesch <mb@bu3sch.de>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License version 2
+ *   as published by the Free Software Foundation.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ */
+
+#include "util.h"
+
+#include <stdio.h>
+#include <string.h>
+
+
+void dump(const char *data,
+         size_t size,
+         const char *description)
+{
+       size_t i;
+       char c;
+
+       fprintf(stderr, "Data dump (%s, %zd bytes):",
+               description, size);
+       for (i = 0; i < size; i++) {
+               c = data[i];
+               if (i % 8 == 0) {
+                       fprintf(stderr, "\n0x%08zx:  0x%02x, ",
+                               i, c & 0xff);
+               } else
+                       fprintf(stderr, "0x%02x, ", c & 0xff);
+       }
+       fprintf(stderr, "\n");
+}
+
+void * xmalloc(size_t size)
+{
+       void *p;
+
+       p = malloc(size);
+       if (!p) {
+               fprintf(stderr, "Out of memory\n");
+               exit(1);
+       }
+       memset(p, 0, size);
+
+       return p;
+}
+
+char * xstrdup(const char *str)
+{
+       char *c;
+
+       c = strdup(str);
+       if (!c) {
+               fprintf(stderr, "Out of memory\n");
+               exit(1);
+       }
+
+       return c;
+}
+
+be16_t cpu_to_be16(uint16_t x)
+{
+       be16_t ret;
+       uint8_t *tmp = (uint8_t *)(&ret);
+
+       tmp[0] = (x & 0xFF00) >> 8;
+       tmp[1] = (x & 0x00FF);
+
+       return ret;
+}
+
+be32_t cpu_to_be32(uint32_t x)
+{
+       be32_t ret;
+       uint8_t *tmp = (uint8_t *)(&ret);
+
+       tmp[0] = (x & 0xFF000000) >> 24;
+       tmp[1] = (x & 0x00FF0000) >> 16;
+       tmp[2] = (x & 0x0000FF00) >> 8;
+       tmp[3] = (x & 0x000000FF);
+
+       return ret;
+}
diff --git a/assembler/util.h b/assembler/util.h
new file mode 100644 (file)
index 0000000..a3d1a82
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef BCM43xx_ASM_UTIL_H_
+#define BCM43xx_ASM_UTIL_H_
+
+#include <stdlib.h>
+#include <stdint.h>
+
+
+#define ARRAY_SIZE(x)  (sizeof(x)/sizeof(x[0]))
+
+
+void dump(const char *data,
+         size_t size,
+         const char *description);
+
+
+void * xmalloc(size_t size);
+char * xstrdup(const char *str);
+
+
+typedef _Bool bool;
+
+typedef uint16_t be16_t;
+typedef uint32_t be32_t;
+
+be16_t cpu_to_be16(uint16_t x);
+be32_t cpu_to_be32(uint32_t x);
+
+#endif /* BCM43xx_ASM_UTIL_H_ */
diff --git a/disassembler/COPYING b/disassembler/COPYING
new file mode 100644 (file)
index 0000000..d511905
--- /dev/null
@@ -0,0 +1,339 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/disassembler/Makefile b/disassembler/Makefile
new file mode 100644 (file)
index 0000000..c58e3f9
--- /dev/null
@@ -0,0 +1,26 @@
+CC = gcc
+PREFIX = /usr/local
+CFLAGS = -std=gnu99 -O2 -fomit-frame-pointer -Wall -D_BSD_SOURCE -D_GNU_SOURCE
+LDFLAGS =
+
+BINARY = bcm43xx-dasm.bin
+OBJECTS = main.o util.o
+
+all: $(BINARY)
+
+main.o: util.h list.h
+
+util.o: util.h
+
+$(BINARY): $(OBJECTS)
+       $(CC) $(CFLAGS) -o $(BINARY) $(OBJECTS) $(LDFLAGS)
+
+install: all
+       -install -o 0 -g 0 -m 755 $(BINARY) $(PREFIX)/bin/
+       -cp bcm43xx-dasm bcm43xx-dasm.inst
+       -sed -i -e 's/installed=0/installed=1/' bcm43xx-dasm.inst
+       -install -o 0 -g 0 -m 755 bcm43xx-dasm.inst $(PREFIX)/bin/bcm43xx-dasm
+       -rm -f bcm43xx-dasm.inst
+
+clean:
+       -rm -f *~ *.o *.orig *.rej $(BINARY)
diff --git a/disassembler/bcm43xx-dasm b/disassembler/bcm43xx-dasm
new file mode 100755 (executable)
index 0000000..aad3321
--- /dev/null
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+installed=0
+
+if [ $# -ne 2 ]; then
+       echo "Usage: $0 input_file.fw output_file.asm"
+       exit 1
+fi
+
+if [ -z "$BCM43xx_DASM" ]; then
+       if [ $installed -eq 0 ] && [ -x "./bcm43xx-dasm.bin" ]; then
+               BCM43xx_DASM="./bcm43xx-dasm.bin"
+       else
+               BCM43xx_DASM="bcm43xx-dasm.bin"
+       fi
+fi
+
+infile="$1"
+outfile="$2"
+
+cat "$infile" | $BCM43xx_DASM "$outfile"
+
diff --git a/disassembler/list.h b/disassembler/list.h
new file mode 100644 (file)
index 0000000..ce26357
--- /dev/null
@@ -0,0 +1,296 @@
+/*
+ * Copied from the Linux kernel source tree, version 2.6.0-test1.
+ *
+ * Licensed under the GPL v2 as per the whole kernel source tree.
+ *
+ */
+
+#ifndef _LIST_H
+#define _LIST_H
+
+#ifndef __GNUC__
+# error "Need GNU GCC"
+#endif
+
+#define typeof         __typeof__
+#define offsetof(type, member)  __builtin_offsetof (type, member)
+
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ *
+ * @ptr:       the pointer to the member.
+ * @type:      the type of the container struct this is embedded in.
+ * @member:    the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({                     \
+       const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
+       (type *)( (char *)__mptr - offsetof(type,member) );})
+
+/*
+ * These are non-NULL pointers that will result in page faults
+ * under normal circumstances, used to verify that nobody uses
+ * non-initialized list entries.
+ */
+#define LIST_POISON1  ((void *) 0x00100100)
+#define LIST_POISON2  ((void *) 0x00200200)
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+       struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+       struct list_head name = LIST_HEAD_INIT(name)
+
+#define INIT_LIST_HEAD(ptr) do { \
+       (ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/*
+ * Insert a new entry between two known consecutive entries. 
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add(struct list_head *new,
+                             struct list_head *prev,
+                             struct list_head *next)
+{
+       next->prev = new;
+       new->next = next;
+       new->prev = prev;
+       prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+       __list_add(new, head, head->next);
+}
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+       __list_add(new, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+       next->prev = prev;
+       prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void list_del(struct list_head *entry)
+{
+       __list_del(entry->prev, entry->next);
+       entry->next = LIST_POISON1;
+       entry->prev = LIST_POISON2;
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static inline void list_del_init(struct list_head *entry)
+{
+       __list_del(entry->prev, entry->next);
+       INIT_LIST_HEAD(entry); 
+}
+
+/**
+ * list_move - delete from one list and add as another's head
+ * @list: the entry to move
+ * @head: the head that will precede our entry
+ */
+static inline void list_move(struct list_head *list, struct list_head *head)
+{
+       __list_del(list->prev, list->next);
+       list_add(list, head);
+}
+
+/**
+ * list_move_tail - delete from one list and add as another's tail
+ * @list: the entry to move
+ * @head: the head that will follow our entry
+ */
+static inline void list_move_tail(struct list_head *list,
+                                 struct list_head *head)
+{
+       __list_del(list->prev, list->next);
+       list_add_tail(list, head);
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static inline int list_empty(struct list_head *head)
+{
+       return head->next == head;
+}
+
+static inline void __list_splice(struct list_head *list,
+                                struct list_head *head)
+{
+       struct list_head *first = list->next;
+       struct list_head *last = list->prev;
+       struct list_head *at = head->next;
+
+       first->prev = head;
+       head->next = first;
+
+       last->next = at;
+       at->prev = last;
+}
+
+/**
+ * list_splice - join two lists
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static inline void list_splice(struct list_head *list, struct list_head *head)
+{
+       if (!list_empty(list))
+               __list_splice(list, head);
+}
+
+/**
+ * list_splice_init - join two lists and reinitialise the emptied list.
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ *
+ * The list at @list is reinitialised
+ */
+static inline void list_splice_init(struct list_head *list,
+                                   struct list_head *head)
+{
+       if (!list_empty(list)) {
+               __list_splice(list, head);
+               INIT_LIST_HEAD(list);
+       }
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr:       the &struct list_head pointer.
+ * @type:      the type of the struct this is embedded in.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+       container_of(ptr, type, member)
+
+/**
+ * list_for_each       -       iterate over a list
+ * @pos:       the &struct list_head to use as a loop counter.
+ * @head:      the head for your list.
+ */
+#define list_for_each(pos, head) \
+       for (pos = (head)->next; pos != (head); \
+               pos = pos->next)
+
+/**
+ * __list_for_each     -       iterate over a list
+ * @pos:       the &struct list_head to use as a loop counter.
+ * @head:      the head for your list.
+ *
+ * This variant differs from list_for_each() in that it's the
+ * simplest possible list iteration code.
+ * Use this for code that knows the list to be very short (empty
+ * or 1 entry) most of the time.
+ */
+#define __list_for_each(pos, head) \
+       for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_prev  -       iterate over a list backwards
+ * @pos:       the &struct list_head to use as a loop counter.
+ * @head:      the head for your list.
+ */
+#define list_for_each_prev(pos, head) \
+       for (pos = (head)->prev; pos != (head); pos = pos->prev)
+
+/**
+ * list_for_each_safe  -       iterate over a list safe against removal of list entry
+ * @pos:       the &struct list_head to use as a loop counter.
+ * @n:         another &struct list_head to use as temporary storage
+ * @head:      the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+       for (pos = (head)->next, n = pos->next; pos != (head); \
+               pos = n, n = pos->next)
+
+/**
+ * list_for_each_entry -       iterate over list of given type
+ * @pos:       the type * to use as a loop counter.
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_for_each_entry(pos, head, member)                         \
+       for (pos = list_entry((head)->next, typeof(*pos), member);      \
+            &pos->member != (head);                                    \
+            pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_reverse - iterate backwards over list of given type.
+ * @pos:       the type * to use as a loop counter.
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_reverse(pos, head, member)                 \
+       for (pos = list_entry((head)->prev, typeof(*pos), member);      \
+            &pos->member != (head);                                    \
+            pos = list_entry(pos->member.prev, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos:       the type * to use as a loop counter.
+ * @n:         another type * to use as temporary storage
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_safe(pos, n, head, member)                 \
+       for (pos = list_entry((head)->next, typeof(*pos), member),      \
+               n = list_entry(pos->member.next, typeof(*pos), member); \
+            &pos->member != (head);                                    \
+            pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+#endif /* _LIST_H */
diff --git a/disassembler/main.c b/disassembler/main.c
new file mode 100644 (file)
index 0000000..64e9975
--- /dev/null
@@ -0,0 +1,774 @@
+/*
+ *   Copyright (C) 2006  Michael Buesch <mb@bu3sch.de>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License version 2
+ *   as published by the Free Software Foundation.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ */
+
+#include "list.h"
+#include "util.h"
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define ARRAY_SIZE(x)  (sizeof(x)/sizeof(x[0]))
+
+/* The header that fwcutter puts in to every .fw file */
+struct fw_header {
+       /* Type of the firmware data */
+       uint8_t type;
+       /* Version number of the firmware data format */
+       uint8_t ver;
+       uint8_t __padding[2];
+       /* Size of the data. For ucode and PCM this is in bytes.
+        * For IV this is in number-of-ivs. */
+       uint32_t size;
+} __attribute__ ((__packed__));
+
+
+struct bin_instruction {
+       unsigned int opcode;
+       unsigned int operands[3];
+};
+
+struct statement {
+       enum {
+               STMT_INSN,
+               STMT_LABEL,
+       } type;
+
+       union {
+               struct {
+                       struct bin_instruction *bin;
+                       const char *name;
+                       const char *operands[5];
+
+                       int is_labelref;
+                       unsigned int labeladdr;
+                       struct statement *labelref;
+               } insn;
+               struct {
+                       char *name;
+               } label;
+       } u;
+
+       struct list_head list;
+};
+
+struct disassembler_context {
+       struct bin_instruction *code;
+       size_t nr_insns;
+
+       struct list_head stmt_list;
+};
+
+
+static FILE *infile;
+const char *outfile_name;
+
+
+static const char * gen_raw_code(unsigned int operand)
+{
+       char *ret;
+
+       ret = xmalloc(5);
+       snprintf(ret, 5, "@%03X", operand);
+
+       return ret;
+}
+
+static const char * disasm_mem_operand(unsigned int operand)
+{
+       char *ret;
+
+       ret = xmalloc(8);
+       snprintf(ret, 8, "[0x%03X]", operand);
+
+       return ret;
+}
+
+static const char * disasm_indirect_mem_operand(unsigned int operand)
+{
+       char *ret;
+
+       ret = xmalloc(12);
+       snprintf(ret, 12, "[0x%02X,off%u]",
+                (operand & 0x3F), ((operand >> 6) & 0x7));
+
+       return ret;
+}
+
+static const char * disasm_imm_operand(unsigned int operand)
+{
+       char *ret;
+
+       operand &= ~0xC00;
+
+       ret = xmalloc(7);
+       if (operand & (1 << 9))
+               snprintf(ret, 7, "0x%04X", (operand | 0xFC00));
+       else
+               snprintf(ret, 7, "0x%03X", operand);
+
+       return ret;
+}
+
+static const char * disasm_spr_operand(unsigned int operand)
+{
+       char *ret;
+
+       ret = xmalloc(7);
+       snprintf(ret, 7, "spr%03X", (operand & 0x1FF));
+
+       return ret;
+}
+
+static const char * disasm_gpr_operand(unsigned int operand)
+{
+       char *ret;
+
+       ret = xmalloc(4);
+       snprintf(ret, 4, "r%u", (operand & 0x3F));
+
+       return ret;
+}
+
+static const char * disasm_offr_operand(unsigned int operand)
+{
+       char *ret;
+
+       ret = xmalloc(5);
+       snprintf(ret, 5, "off%u", (operand & 0x7));
+
+       return ret;
+}
+
+static void disasm_std_operand(struct statement *stmt,
+                              int oper_idx,
+                              int out_idx,
+                              int forceraw)
+{
+       unsigned int operand = stmt->u.insn.bin->operands[oper_idx];
+
+       if (forceraw)
+               goto raw;
+
+       if (!(operand & 0x800)) {
+               stmt->u.insn.operands[out_idx] = disasm_mem_operand(operand);
+               return;
+       } else if ((operand & 0xC00) == 0xC00) { 
+               stmt->u.insn.operands[out_idx] = disasm_imm_operand(operand);
+               return;
+       } else if ((operand & 0xFC0) == 0xBC0) {
+               stmt->u.insn.operands[out_idx] = disasm_gpr_operand(operand);
+               return;
+       } else if ((operand & 0xE00) == 0x800) {
+               stmt->u.insn.operands[out_idx] = disasm_spr_operand(operand);
+               return;
+       } else if ((operand & 0xFF8) == 0x860) {
+               stmt->u.insn.operands[out_idx] = disasm_offr_operand(operand);
+               return;
+       } else if ((operand & 0xE00) == 0xA00) {
+               stmt->u.insn.operands[out_idx] = disasm_indirect_mem_operand(operand);
+               return;
+       }
+raw:
+       stmt->u.insn.operands[out_idx] = gen_raw_code(operand);
+}
+
+static void disasm_constant_opcodes(struct disassembler_context *ctx,
+                                   struct statement *stmt)
+{
+       struct bin_instruction *bin = stmt->u.insn.bin;
+
+       switch (bin->opcode) {
+       case 0x1C0:
+               stmt->u.insn.name = "add";
+               disasm_std_operand(stmt, 0, 0, 0);
+               disasm_std_operand(stmt, 1, 1, 0);
+               disasm_std_operand(stmt, 2, 2, 0);
+               break;
+       case 0x1C2:
+               stmt->u.insn.name = "add.";
+               disasm_std_operand(stmt, 0, 0, 0);
+               disasm_std_operand(stmt, 1, 1, 0);
+               disasm_std_operand(stmt, 2, 2, 0);
+               break;
+       case 0x1C1:
+               stmt->u.insn.name = "addc";
+               disasm_std_operand(stmt, 0, 0, 0);
+               disasm_std_operand(stmt, 1, 1, 0);
+               disasm_std_operand(stmt, 2, 2, 0);
+               break;
+       case 0x1C3:
+               stmt->u.insn.name = "addc.";
+               disasm_std_operand(stmt, 0, 0, 0);
+               disasm_std_operand(stmt, 1, 1, 0);
+               disasm_std_operand(stmt, 2, 2, 0);
+               break;
+       case 0x1D0:
+               stmt->u.insn.name = "sub";
+               disasm_std_operand(stmt, 0, 0, 0);
+               disasm_std_operand(stmt, 1, 1, 0);
+               disasm_std_operand(stmt, 2, 2, 0);
+               break;
+       case 0x1D2:
+               stmt->u.insn.name = "sub.";
+               disasm_std_operand(stmt, 0, 0, 0);
+               disasm_std_operand(stmt, 1, 1, 0);
+               disasm_std_operand(stmt, 2, 2, 0);
+               break;
+       case 0x1D1:
+               stmt->u.insn.name = "subc";
+               disasm_std_operand(stmt, 0, 0, 0);
+               disasm_std_operand(stmt, 1, 1, 0);
+               disasm_std_operand(stmt, 2, 2, 0);
+               break;
+       case 0x1D3:
+               stmt->u.insn.name = "subc.";
+               disasm_std_operand(stmt, 0, 0, 0);
+               disasm_std_operand(stmt, 1, 1, 0);
+               disasm_std_operand(stmt, 2, 2, 0);
+               break;
+       case 0x130:
+               stmt->u.insn.name = "sra";
+               disasm_std_operand(stmt, 0, 0, 0);
+               disasm_std_operand(stmt, 1, 1, 0);
+               disasm_std_operand(stmt, 2, 2, 0);
+               break;
+       case 0x160:
+               stmt->u.insn.name = "or";
+               disasm_std_operand(stmt, 0, 0, 0);
+               disasm_std_operand(stmt, 1, 1, 0);
+               disasm_std_operand(stmt, 2, 2, 0);
+               break;
+       case 0x140:
+               stmt->u.insn.name = "and";
+               disasm_std_operand(stmt, 0, 0, 0);
+               disasm_std_operand(stmt, 1, 1, 0);
+               disasm_std_operand(stmt, 2, 2, 0);
+               break;
+       case 0x170:
+               stmt->u.insn.name = "xor";
+               disasm_std_operand(stmt, 0, 0, 0);
+               disasm_std_operand(stmt, 1, 1, 0);
+               disasm_std_operand(stmt, 2, 2, 0);
+               break;
+       case 0x120:
+               stmt->u.insn.name = "sr";
+               disasm_std_operand(stmt, 0, 0, 0);
+               disasm_std_operand(stmt, 1, 1, 0);
+               disasm_std_operand(stmt, 2, 2, 0);
+               break;
+       case 0x110:
+               stmt->u.insn.name = "sl";
+               disasm_std_operand(stmt, 0, 0, 0);
+               disasm_std_operand(stmt, 1, 1, 0);
+               disasm_std_operand(stmt, 2, 2, 0);
+               break;
+       case 0x1A0:
+               stmt->u.insn.name = "rl";
+               disasm_std_operand(stmt, 0, 0, 0);
+               disasm_std_operand(stmt, 1, 1, 0);
+               disasm_std_operand(stmt, 2, 2, 0);
+               break;
+       case 0x1B0:
+               stmt->u.insn.name = "rr";
+               disasm_std_operand(stmt, 0, 0, 0);
+               disasm_std_operand(stmt, 1, 1, 0);
+               disasm_std_operand(stmt, 2, 2, 0);
+               break;
+       case 0x150:
+               stmt->u.insn.name = "nand";
+               disasm_std_operand(stmt, 0, 0, 0);
+               disasm_std_operand(stmt, 1, 1, 0);
+               disasm_std_operand(stmt, 2, 2, 0);
+               break;
+       case 0x040:
+               stmt->u.insn.name = "jand";
+               stmt->u.insn.is_labelref = 2;
+               stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2];
+               disasm_std_operand(stmt, 0, 0, 0);
+               disasm_std_operand(stmt, 1, 1, 0);
+               break;
+       case (0x040 | 0x1):
+               stmt->u.insn.name = "jnand";
+               stmt->u.insn.is_labelref = 2;
+               stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2];
+               disasm_std_operand(stmt, 0, 0, 0);
+               disasm_std_operand(stmt, 1, 1, 0);
+               break;
+       case 0x050:
+               stmt->u.insn.name = "js";
+               stmt->u.insn.is_labelref = 2;
+               stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2];
+               disasm_std_operand(stmt, 0, 0, 0);
+               disasm_std_operand(stmt, 1, 1, 0);
+               break;
+       case (0x050 | 0x1):
+               stmt->u.insn.name = "jns";
+               stmt->u.insn.is_labelref = 2;
+               stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2];
+               disasm_std_operand(stmt, 0, 0, 0);
+               disasm_std_operand(stmt, 1, 1, 0);
+               break;
+       case 0x0D0:
+               stmt->u.insn.name = "je";
+               stmt->u.insn.is_labelref = 2;
+               stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2];
+               disasm_std_operand(stmt, 0, 0, 0);
+               disasm_std_operand(stmt, 1, 1, 0);
+               break;
+       case (0x0D0 | 0x1):
+               stmt->u.insn.name = "jne";
+               stmt->u.insn.is_labelref = 2;
+               stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2];
+               disasm_std_operand(stmt, 0, 0, 0);
+               disasm_std_operand(stmt, 1, 1, 0);
+               break;
+       case 0x0D2:
+               stmt->u.insn.name = "jls";
+               stmt->u.insn.is_labelref = 2;
+               stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2];
+               disasm_std_operand(stmt, 0, 0, 0);
+               disasm_std_operand(stmt, 1, 1, 0);
+               break;
+       case (0x0D2 | 0x1):
+               stmt->u.insn.name = "jges";
+               stmt->u.insn.is_labelref = 2;
+               stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2];
+               disasm_std_operand(stmt, 0, 0, 0);
+               disasm_std_operand(stmt, 1, 1, 0);
+               break;
+       case 0x0D4:
+               stmt->u.insn.name = "jgs";
+               stmt->u.insn.is_labelref = 2;
+               stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2];
+               disasm_std_operand(stmt, 0, 0, 0);
+               disasm_std_operand(stmt, 1, 1, 0);
+               break;
+       case (0x0D4 | 0x1):
+               stmt->u.insn.name = "jles";
+               stmt->u.insn.is_labelref = 2;
+               stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2];
+               disasm_std_operand(stmt, 0, 0, 0);
+               disasm_std_operand(stmt, 1, 1, 0);
+               break;
+       case 0x0DA:
+               stmt->u.insn.name = "jl";
+               stmt->u.insn.is_labelref = 2;
+               stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2];
+               disasm_std_operand(stmt, 0, 0, 0);
+               disasm_std_operand(stmt, 1, 1, 0);
+               break;
+       case (0x0DA | 0x1):
+               stmt->u.insn.name = "jge";
+               stmt->u.insn.is_labelref = 2;
+               stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2];
+               disasm_std_operand(stmt, 0, 0, 0);
+               disasm_std_operand(stmt, 1, 1, 0);
+               break;
+       case 0x0DC:
+               stmt->u.insn.name = "jg";
+               stmt->u.insn.is_labelref = 2;
+               stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2];
+               disasm_std_operand(stmt, 0, 0, 0);
+               disasm_std_operand(stmt, 1, 1, 0);
+               break;
+       case (0x0DC | 0x1):
+               stmt->u.insn.name = "jle";
+               stmt->u.insn.is_labelref = 2;
+               stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2];
+               disasm_std_operand(stmt, 0, 0, 0);
+               disasm_std_operand(stmt, 1, 1, 0);
+               break;
+       case 0x002: {
+               char *str;
+
+               stmt->u.insn.name = "call";
+               stmt->u.insn.is_labelref = 1;
+               stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2];
+               str = xmalloc(4);
+               snprintf(str, 4, "lr%u", stmt->u.insn.bin->operands[0]);
+               stmt->u.insn.operands[0] = str;
+               break;
+       }
+       case 0x003: {
+               char *str;
+
+               stmt->u.insn.name = "ret";
+               str = xmalloc(4);
+               snprintf(str, 4, "lr%u", stmt->u.insn.bin->operands[0]);
+               stmt->u.insn.operands[0] = str;
+               str = xmalloc(4);
+               snprintf(str, 4, "lr%u", stmt->u.insn.bin->operands[2]);
+               stmt->u.insn.operands[2] = str;
+               break;
+       }
+       case 0x1E0: {
+               unsigned int flags;
+
+               flags = stmt->u.insn.bin->operands[1];
+               switch (flags & ~0xC00) {
+               case 0x1:
+                       stmt->u.insn.name = "tkiph";
+                       break;
+               case (0x1 | 0x2):
+                       stmt->u.insn.name = "tkiphs";
+                       break;
+               case 0x0:
+                       stmt->u.insn.name = "tkipl";
+                       break;
+               case (0x0 | 0x2):
+                       stmt->u.insn.name = "tkipls";
+                       break;
+               default:
+                       fprintf(stderr, "Invalid TKIP flags %X\n",
+                               flags);
+                       exit(1);
+               }
+               disasm_std_operand(stmt, 0, 0, 0);
+               disasm_std_operand(stmt, 2, 2, 0);
+               break;
+       }
+       case 0x001: {
+               stmt->u.insn.name = "nap";
+               if (stmt->u.insn.bin->operands[0] != 0xBC0) {
+                       fprintf(stderr, "NAP: invalid first argument 0x%03X\n",
+                               stmt->u.insn.bin->operands[0]);
+               }
+               if (stmt->u.insn.bin->operands[1] != 0xBC0) {
+                       fprintf(stderr, "NAP: invalid second argument 0x%03X\n",
+                               stmt->u.insn.bin->operands[1]);
+               }
+               if (stmt->u.insn.bin->operands[2] != 0x000) {
+                       fprintf(stderr, "NAP: invalid third argument 0x%03X\n",
+                               stmt->u.insn.bin->operands[2]);
+               }
+               break;
+       }
+       default:
+               stmt->u.insn.name = gen_raw_code(bin->opcode);
+               disasm_std_operand(stmt, 0, 0, 1);
+               disasm_std_operand(stmt, 1, 1, 1);
+               disasm_std_operand(stmt, 2, 2, 1);
+               break;
+       }
+}
+
+static void disasm_opcodes(struct disassembler_context *ctx)
+{
+       struct bin_instruction *bin;
+       size_t i;
+       struct statement *stmt;
+       char *str;
+
+       for (i = 0; i < ctx->nr_insns; i++) {
+               bin = &(ctx->code[i]);
+
+               stmt = xmalloc(sizeof(struct statement));
+               stmt->type = STMT_INSN;
+               INIT_LIST_HEAD(&stmt->list);
+               stmt->u.insn.bin = bin;
+               stmt->u.insn.is_labelref = -1;
+
+               switch (bin->opcode & 0xF00) {
+               case 0x200:
+                       stmt->u.insn.name = "srx";
+
+                       str = xmalloc(3);
+                       snprintf(str, 3, "%d", (bin->opcode & 0x0F0) >> 4);
+                       stmt->u.insn.operands[0] = str;
+                       str = xmalloc(3);
+                       snprintf(str, 3, "%d", (bin->opcode & 0x00F));
+                       stmt->u.insn.operands[1] = str;
+
+                       disasm_std_operand(stmt, 0, 2, 0);
+                       disasm_std_operand(stmt, 1, 3, 0);
+                       disasm_std_operand(stmt, 2, 4, 0);
+                       break;
+               case 0x300:
+                       stmt->u.insn.name = "orx";
+
+                       str = xmalloc(3);
+                       snprintf(str, 3, "%d", (bin->opcode & 0x0F0) >> 4);
+                       stmt->u.insn.operands[0] = str;
+                       str = xmalloc(3);
+                       snprintf(str, 3, "%d", (bin->opcode & 0x00F));
+                       stmt->u.insn.operands[1] = str;
+
+                       disasm_std_operand(stmt, 0, 2, 0);
+                       disasm_std_operand(stmt, 1, 3, 0);
+                       disasm_std_operand(stmt, 2, 4, 0);
+                       break;
+               case 0x400:
+                       stmt->u.insn.name = "jzx";
+
+                       str = xmalloc(3);
+                       snprintf(str, 3, "%d", (bin->opcode & 0x0F0) >> 4);
+                       stmt->u.insn.operands[0] = str;
+                       str = xmalloc(3);
+                       snprintf(str, 3, "%d", (bin->opcode & 0x00F));
+                       stmt->u.insn.operands[1] = str;
+
+                       disasm_std_operand(stmt, 0, 2, 0);
+                       disasm_std_operand(stmt, 1, 3, 0);
+                       stmt->u.insn.is_labelref = 4;
+                       stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2];
+                       break;
+               case 0x500:
+                       stmt->u.insn.name = "jnzx";
+
+                       str = xmalloc(3);
+                       snprintf(str, 3, "%d", (bin->opcode & 0x0F0) >> 4);
+                       stmt->u.insn.operands[0] = str;
+                       str = xmalloc(3);
+                       snprintf(str, 3, "%d", (bin->opcode & 0x00F));
+                       stmt->u.insn.operands[1] = str;
+
+                       disasm_std_operand(stmt, 0, 2, 0);
+                       disasm_std_operand(stmt, 1, 3, 0);
+                       stmt->u.insn.is_labelref = 4;
+                       stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2];
+                       break;
+               case 0x600:
+                       stmt->u.insn.name = "jnext";
+
+                       str = xmalloc(5);
+                       snprintf(str, 5, "0x%02X", (bin->opcode & 0x0FF));
+                       stmt->u.insn.operands[0] = str;
+
+                       disasm_std_operand(stmt, 0, 1, 0);
+                       disasm_std_operand(stmt, 1, 2, 0);
+                       stmt->u.insn.is_labelref = 3;
+                       stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2];
+                       break;
+               case 0x700:
+                       stmt->u.insn.name = "jext";
+
+                       str = xmalloc(5);
+                       snprintf(str, 5, "0x%02X", (bin->opcode & 0x0FF));
+                       stmt->u.insn.operands[0] = str;
+
+                       disasm_std_operand(stmt, 0, 1, 0);
+                       disasm_std_operand(stmt, 1, 2, 0);
+                       stmt->u.insn.is_labelref = 3;
+                       stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2];
+                       break;
+               default:
+                       disasm_constant_opcodes(ctx, stmt);
+                       break;
+               }
+
+               list_add_tail(&stmt->list, &ctx->stmt_list);
+       }
+}
+
+static struct statement * get_label_at(struct disassembler_context *ctx,
+                                      unsigned int addr)
+{
+       unsigned int addrcnt = 0;
+       struct statement *stmt, *ret, *prev;
+
+       list_for_each_entry(stmt, &ctx->stmt_list, list) {
+               if (stmt->type != STMT_INSN)
+                       continue;
+               if (addrcnt == addr) {
+                       prev = list_entry(stmt->list.prev, struct statement, list);
+                       if (prev->type == STMT_LABEL)
+                               return prev;
+                       ret = xmalloc(sizeof(struct statement));
+                       INIT_LIST_HEAD(&ret->list);
+                       ret->type = STMT_LABEL;
+                       list_add(&ret->list, &prev->list);
+
+                       return ret;
+               }
+               addrcnt++;
+       }
+
+       return NULL;
+}
+
+static void resolve_labels(struct disassembler_context *ctx)
+{
+       struct statement *stmt;
+       struct statement *label;
+       struct statement *n;
+       unsigned int labeladdr;
+       unsigned int namecnt = 0;
+
+       /* Resolve label references */
+       list_for_each_entry_safe(stmt, n, &ctx->stmt_list, list) {
+               if (stmt->type != STMT_INSN)
+                       continue;
+               if (stmt->u.insn.is_labelref == -1)
+                       continue;
+               labeladdr = stmt->u.insn.labeladdr;
+               label = get_label_at(ctx, labeladdr);
+               if (!label) {
+                       fprintf(stderr, "Labeladdress %X out of bounds\n",
+                               labeladdr);
+                       exit(1);
+               }
+               stmt->u.insn.labelref = label;
+       }
+
+       /* Name the labels */
+       list_for_each_entry(stmt, &ctx->stmt_list, list) {
+               if (stmt->type != STMT_LABEL)
+                       continue;
+               stmt->u.label.name = xmalloc(20);
+               snprintf(stmt->u.label.name, 20, "L%u", namecnt);
+               namecnt++;
+       }
+}
+
+static void emit_asm(struct disassembler_context *ctx)
+{
+       struct statement *stmt;
+       FILE *fd;
+       int first, i;
+
+       fd = fopen(outfile_name, "w+");
+       if (!fd) {
+               fprintf(stderr, "Could not open output file \"%s\"\n",
+                       outfile_name);
+               exit(1);
+       }
+       fprintf(fd, "%%arch bcm43xx_newworld\n\n");
+       list_for_each_entry(stmt, &ctx->stmt_list, list) {
+               switch (stmt->type) {
+               case STMT_INSN:
+                       fprintf(fd, "\t%s", stmt->u.insn.name);
+                       first = 1;
+                       for (i = 0; i < ARRAY_SIZE(stmt->u.insn.operands); i++) {
+                               if (stmt->u.insn.is_labelref == i) {
+                                       fprintf(fd, ",%s",
+                                               stmt->u.insn.labelref->u.label.name);
+                               }
+                               if (!stmt->u.insn.operands[i])
+                                       continue;
+                               if (first)
+                                       fprintf(fd, "\t");
+                               if (!first)
+                                       fprintf(fd, ",");
+                               first = 0;
+                               fprintf(fd, "%s",
+                                       stmt->u.insn.operands[i]);
+                       }
+                       fprintf(fd, "\n");
+                       break;
+               case STMT_LABEL:
+                       fprintf(fd, "%s:\n", stmt->u.label.name);
+                       break;
+               }
+       }
+       fclose(fd);
+}
+
+static void read_input(struct disassembler_context *ctx)
+{
+       size_t size = 0, pos = 0;
+       size_t ret;
+       struct bin_instruction *code = NULL;
+       unsigned char tmp[sizeof(uint64_t)];
+       uint64_t codeword;
+       struct fw_header hdr;
+
+       ret = fread(&hdr, 1, sizeof(hdr), infile);
+       if (!ret || ret != sizeof(hdr)) {
+               fprintf(stderr, "Corrupt input file (not fwcutter output)\n");
+               exit(1);
+       }       
+
+       if (hdr.ver != 1) {
+               fprintf(stderr, "Invalid fwcutter header version\n");
+               exit(1);
+       }
+
+       if (hdr.type != 'u') {
+               fprintf(stderr, "Not a microcode image\n");
+               exit(1);
+       }
+
+       while (1) {
+               if (pos >= size) {
+                       size += 512;
+                       code = xrealloc(code, size * sizeof(struct bin_instruction));
+               }
+               ret = fread(tmp, 1, sizeof(uint64_t), infile);
+               if (!ret)
+                       break;
+               if (ret != sizeof(uint64_t)) {
+                       fprintf(stderr, "Corrupt input file (not 8 byte aligned)\n");
+                       exit(1);
+               }
+
+               codeword = 0;
+               codeword |= ((uint64_t)tmp[0]) << 56;
+               codeword |= ((uint64_t)tmp[1]) << 48;
+               codeword |= ((uint64_t)tmp[2]) << 40;
+               codeword |= ((uint64_t)tmp[3]) << 32;
+               codeword |= ((uint64_t)tmp[4]) << 24;
+               codeword |= ((uint64_t)tmp[5]) << 16;
+               codeword |= ((uint64_t)tmp[6]) << 8;
+               codeword |= ((uint64_t)tmp[7]);
+
+               code[pos].opcode = (codeword >> 4) & 0xFFF;
+               code[pos].operands[0] = (codeword & 0xF) << 8;
+               code[pos].operands[0] |= (codeword >> 56) & 0xFF;
+               code[pos].operands[1] = (codeword >> 44) & 0xFFF;
+               code[pos].operands[2] = (codeword >> 32) & 0xFFF;
+
+               pos++;
+       }
+
+       ctx->code = code;
+       ctx->nr_insns = pos;
+}
+
+static void disassemble(void)
+{
+       struct disassembler_context ctx;
+
+       memset(&ctx, 0, sizeof(ctx));
+       INIT_LIST_HEAD(&ctx.stmt_list);
+
+       read_input(&ctx);
+       disasm_opcodes(&ctx);
+       resolve_labels(&ctx);
+       emit_asm(&ctx);
+}
+
+static void usage(int argc, char **argv)
+{
+       fprintf(stderr, "Usage: %s output_file\n", argv[0]);
+}
+
+static void parse_args(int argc, char **argv)
+{
+       if (argc != 2) {
+               usage(argc, argv);
+               exit(1);
+       }
+       outfile_name = argv[1];
+}
+
+int main(int argc, char **argv)
+{
+       infile = stdin;
+       parse_args(argc, argv);
+       disassemble();
+
+       /* Lazyman simply leaks all allocated memory. */
+       return 0;
+}
diff --git a/disassembler/util.c b/disassembler/util.c
new file mode 100644 (file)
index 0000000..1854faf
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ *   Copyright (C) 2006  Michael Buesch <mb@bu3sch.de>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License version 2
+ *   as published by the Free Software Foundation.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ */
+
+#include "util.h"
+
+#include <stdio.h>
+#include <string.h>
+
+
+void dump(const char *data,
+         size_t size,
+         const char *description)
+{
+       size_t i;
+       char c;
+
+       fprintf(stderr, "Data dump (%s, %zd bytes):",
+               description, size);
+       for (i = 0; i < size; i++) {
+               c = data[i];
+               if (i % 8 == 0) {
+                       fprintf(stderr, "\n0x%08zx:  0x%02x, ",
+                               i, c & 0xff);
+               } else
+                       fprintf(stderr, "0x%02x, ", c & 0xff);
+       }
+       fprintf(stderr, "\n");
+}
+
+void * xmalloc(size_t size)
+{
+       void *p;
+
+       p = malloc(size);
+       if (!p) {
+               fprintf(stderr, "Out of memory\n");
+               exit(1);
+       }
+       memset(p, 0, size);
+
+       return p;
+}
+
+char * xstrdup(const char *str)
+{
+       char *c;
+
+       c = strdup(str);
+       if (!c) {
+               fprintf(stderr, "Out of memory\n");
+               exit(1);
+       }
+
+       return c;
+}
+
+void * xrealloc(void *in, size_t size)
+{
+       void *out;
+
+       out = realloc(in, size);
+       if (!out) {
+               fprintf(stderr, "Out of memory\n");
+               exit(1);
+       }
+
+       return out;
+}
diff --git a/disassembler/util.h b/disassembler/util.h
new file mode 100644 (file)
index 0000000..843d347
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef BCM43xx_DASM_UTIL_H_
+#define BCM43xx_DASM_UTIL_H_
+
+#include <stdlib.h>
+
+
+void dump(const char *data,
+         size_t size,
+         const char *description);
+
+
+void * xmalloc(size_t size);
+char * xstrdup(const char *str);
+void * xrealloc(void *in, size_t size);
+
+#endif /* BCM43xx_DASM_UTIL_H_ */
diff --git a/fwcutter/Makefile b/fwcutter/Makefile
new file mode 100644 (file)
index 0000000..2ddd8bd
--- /dev/null
@@ -0,0 +1,34 @@
+VERSION = 008
+
+CC ?= cc
+PREFIX ?= /usr/local
+CFLAGS ?= -Os -fomit-frame-pointer
+CFLAGS += -std=c99 -Wall -pedantic -D_BSD_SOURCE
+LDFLAGS ?=
+
+
+OBJECTS = fwcutter.o md5.o
+BIN    = b43-fwcutter
+
+CFLAGS += -DFWCUTTER_VERSION_=$(VERSION)
+
+all: $(BIN)
+
+fwcutter.o: fwcutter.h fwcutter_list.h md5.h
+
+md5.o: md5.h
+
+$(BIN): $(OBJECTS)
+       $(CC) $(CFLAGS) -o $(BIN) $(OBJECTS) $(LDFLAGS)
+
+install: all
+       -install -d -o 0 -g 0 -m 755 $(PREFIX)/bin/
+       -install -o 0 -g 0 -m 755 $(BIN) $(PREFIX)/bin/
+       -install -d -o 0 -g 0 -m 755 $(PREFIX)/man/man1/
+       -install -o 0 -g 0 -m 644 $(BIN).1 $(PREFIX)/man/man1/
+
+clean:
+       -rm -f *.o $(BIN)
+
+distclean: clean
+       -rm -f *.fw *.orig *.rej *~
diff --git a/fwcutter/README b/fwcutter/README
new file mode 100644 (file)
index 0000000..91e63e5
--- /dev/null
@@ -0,0 +1,47 @@
+                       BCM43XX Linux Driver Project
+                       ============================
+
+
+About this software
+-------------------
+
+b43-fwcutter is a tool which can extract firmware from various source files.
+It's written for BCM43xx driver files.
+
+The project page is http://bcm43xx.berlios.de/
+
+
+Usage
+-----
+
+b43-fwcutter FILE              extracts the firmware from a source FILE to
+                               the current directory, creating a directory
+                               "b43" or "b43legacy" with files in it.
+b43-fwcutter -i FILE           identify the driver file; print information
+                               but don't extract.
+b43-fwcutter -w /tmp FILE      extract and write firmware to /tmp.
+b43-fwcutter -l                        prints a list of supported driver source files.
+
+After extraction, copy the "b43" or "b43legacy" directory to your firmware
+directory (usually /lib/firmware or similar, see below). Alternatively you
+can use the -w option to b43-fwcutter.
+
+Different distributions use different directories to load firmware from.
+If the driver complains about missing firmware files look for the correct
+directory. For some distributions you have to use /lib/hotplug/firmware
+or /usr/lib/hotplug/firmware but other directories are also possible.
+
+Because firmware file are stored in big endian, extraction doesn't depend
+on the system you're using. You can use extracted firmware from any driver
+on any system and also copy the extracted files to any other system (e.g.
+if extraction on the target system isn't feasible.)
+
+
+Where can I find some driver source files?
+------------------------------------------
+
+Please check the references at
+http://www.linuxwireless.org/en/users/Drivers/bcm43xx#devicefirmware
+
+It is recommended that you extract firmware for both b43 and b43legacy in
+order to support both types of cards on your system.
diff --git a/fwcutter/b43-fwcutter.1 b/fwcutter/b43-fwcutter.1
new file mode 100644 (file)
index 0000000..e55afe2
--- /dev/null
@@ -0,0 +1,41 @@
+.\" Initially generated by help2man 1.36.
+.TH B43-FWCUTTER "1" "2007" "b43-fwcutter" "User Commands"
+.SH NAME
+b43-fwcutter \- manual page for b43-fwcutter
+.SH SYNOPSIS
+.B b43-fwcutter
+[\fIOPTION\fR] [\fIDRIVER\fR]
+.SH DESCRIPTION
+b43-fwcutter can extract the firmware for your Broadcom 43xx hardware from different closed source drivers. The b43 driver depends on this firmware files and can't work without them.
+.PP
+Currently b43-fwcutter supports Apple MacOS X, Microsoft Windows 98/ME/2000/XP and Linux drivers, but keep in mind that b43-fwcutter doesn't support all driver versions.
+.PP
+Example:
+.IP
+b43-fwcutter bcmwl5.sys
+.PP
+to cut the firmware out of bcmwl5.sys
+.SH OPTIONS
+.TP
+\fB\-l\fR|\-\-list
+List supported driver versions
+.TP
+\fB\-i\fR|\-\-identify
+Only identify the driver file (don't extract)
+.TP
+\fB\-w\fR|\-\-target\-dir DIR
+Extract and write firmware to DIR
+.TP
+\fB\-a\fR|\-\-alt\-iv
+Extract alternative initvals (only 3.10.x.x)
+.TP
+\fB\-p\fR|\-\-postfix ".FOO"
+Postfix for firmware filenames (.FOO.fw)
+.TP
+\fB\-v\fR|\-\-version
+Print b43-fwcutter version
+.TP
+\fB\-h\fR|\-\-help
+Print help information
+.SH AUTHORS
+b43 development team, http://bcm43xx.berlios.de
diff --git a/fwcutter/fwcutter.c b/fwcutter/fwcutter.c
new file mode 100644 (file)
index 0000000..fef9099
--- /dev/null
@@ -0,0 +1,678 @@
+/*
+ * firmware cutter for broadcom 43xx wireless driver files
+ * 
+ * Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
+ *               2005-2007 Michael Buesch <mb@bu3sch.de>
+ *              2005 Alex Beregszaszi
+ *              2007 Johannes Berg <johannes@sipsolutions.net>
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     1. Redistributions of source code must retain the above copyright
+ *        notice, this list of conditions and the following disclaimer.
+ *     2. Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials provided
+ *        with the distribution.
+ *
+ *   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ *   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *   DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ *   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ *   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ *   GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ *   WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ *   OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ *   EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#ifdef __DragonFly__
+#include <sys/endian.h>
+#else
+#include <byteswap.h>
+#endif
+
+#include "md5.h"
+#include "fwcutter.h"
+#include "fwcutter_list.h"
+
+#ifdef __DragonFly__
+#define V3_FW_DIRNAME  "v3"
+#define V4_FW_DIRNAME  "v4"
+#else
+#define V3_FW_DIRNAME  "b43legacy"
+#define V4_FW_DIRNAME  "b43"
+#endif
+
+static struct cmdline_args cmdargs;
+
+
+/* Convert a CPU-endian 16bit integer to Big-Endian */
+static be16_t to_be16(uint16_t v)
+{
+       uint8_t ret[2];
+
+       ret[0] = (v & 0xFF00) >> 8;
+       ret[1] = (v & 0x00FF);
+
+       return *((be16_t *)ret);
+}
+
+#if 0
+/* Convert a Big-Endian 16bit integer to CPU-endian */
+static uint16_t from_be16(be16_t v)
+{
+       uint16_t ret = 0;
+
+       ret |= (uint16_t)(((uint8_t *)&v)[0]) << 8;
+       ret |= (uint16_t)(((uint8_t *)&v)[1]);
+
+       return ret;
+}
+#endif
+
+/* Convert a CPU-endian 32bit integer to Big-Endian */
+static be32_t to_be32(uint32_t v)
+{
+       uint8_t ret[4];
+
+       ret[0] = (v & 0xFF000000) >> 24;
+       ret[1] = (v & 0x00FF0000) >> 16;
+       ret[2] = (v & 0x0000FF00) >> 8;
+       ret[3] = (v & 0x000000FF);
+
+       return *((be32_t *)ret);
+}
+
+/* Convert a Big-Endian 32bit integer to CPU-endian */
+static uint32_t from_be32(be32_t v)
+{
+       uint32_t ret = 0;
+
+       ret |= (uint32_t)(((uint8_t *)&v)[0]) << 24;
+       ret |= (uint32_t)(((uint8_t *)&v)[1]) << 16;
+       ret |= (uint32_t)(((uint8_t *)&v)[2]) << 8;
+       ret |= (uint32_t)(((uint8_t *)&v)[3]);
+
+       return ret;
+}
+
+/* tiny disassembler */
+static void print_ucode_version(struct insn *insn)
+{
+       int val;
+
+       /*
+        * The instruction we're looking for is a store to memory
+        * offset insn->op3 of the constant formed like `val' below.
+        * 0x2de00 is the opcode for type 1, 0x378 is the opcode
+        * for type 2 and 3.
+        */
+       if (insn->opcode != 0x378 && insn->opcode != 0x2de00)
+               return;
+
+       val = ((0xFF & insn->op1) << 8) | (0xFF & insn->op2);
+
+       /*
+        * Memory offsets are word-offsets, for the meaning
+        * see http://bcm-v4.sipsolutions.net/802.11/ObjectMemory
+        */
+       switch (insn->op3) {
+       case 0:
+               printf("  ucode version:  %d\n", val);
+               break;
+       case 1:
+               printf("  ucode revision: %d\n", val);
+               break;
+       case 2:
+               printf("  ucode date:     %.4d-%.2d-%.2d\n",
+                      2000 + (val >> 12), (val >> 8) & 0xF, val & 0xFF);
+               break;
+       case 3:
+               printf("  ucode time:     %.2d:%.2d:%.2d\n",
+                      val >> 11, (val >> 5) & 0x3f, val & 0x1f);
+               break;
+       }
+}
+
+static void disasm_ucode_1(uint64_t in, struct insn *out)
+{
+       /* xxyyyzzz00oooooX -> ooooo Xxx yyy zzz
+        * if we swap the upper and lower 32-bits first it becomes easier:
+        * 00oooooxxxyyyzzz -> ooooo xxx yyy zzz
+        */
+       in = (in >> 32) | (in << 32);
+
+       out->op3        = in & 0xFFF;
+       out->op2        = (in >> 12) & 0xFFF;
+       out->op1        = (in >> 24) & 0xFFF;
+       out->opcode     = (in >> 36) & 0xFFFFF;
+       /* the rest of the in word should be zero */
+}
+
+static void disasm_ucode_2(uint64_t in, struct insn *out)
+{
+       /* xxyyyzzz0000oooX -> ooo Xxx yyy zzz
+        * if we swap the upper and lower 32-bits first it becomes easier:
+        * 0000oooxxxyyyzzz -> ooo xxx yyy zzz
+        */
+       in = (in >> 32) | (in << 32);
+
+       out->op3        = in & 0xFFF;
+       out->op2        = (in >> 12) & 0xFFF;
+       out->op1        = (in >> 24) & 0xFFF;
+       out->opcode     = (in >> 36) & 0xFFF;
+       /* the rest of the in word should be zero */
+}
+
+static void disasm_ucode_3(uint64_t in, struct insn *out)
+{
+       /*
+        * like 2, but each operand has one bit more; appears
+        * to use the same instruction set slightly extended
+        */
+       in = (in >> 32) | (in << 32);
+
+       out->op3        = in & 0x1FFF;
+       out->op2        = (in >> 13) & 0x1FFF;
+       out->op1        = (in >> 26) & 0x1FFF;
+       out->opcode     = (in >> 39) & 0xFFF;
+       /* the rest of the in word should be zero */
+}
+
+static void analyse_ucode(int ucode_rev, uint8_t *data, uint32_t len)
+{
+       uint64_t *insns = (uint64_t*)data;
+       struct insn insn;
+       uint32_t i;
+
+       for (i=0; i<len/sizeof(*insns); i++) {
+               switch (ucode_rev) {
+               case 1:
+                       disasm_ucode_1(insns[i], &insn);
+                       print_ucode_version(&insn);
+                       break;
+               case 2:
+                       disasm_ucode_2(insns[i], &insn);
+                       print_ucode_version(&insn);
+                       break;
+               case 3:
+                       disasm_ucode_3(insns[i], &insn);
+                       print_ucode_version(&insn);
+                       break;
+               }
+       }
+}
+
+static void swap_endianness_ucode(uint8_t *buf, uint32_t len)
+{
+       uint32_t *buf32 = (uint32_t*)buf;
+       uint32_t i;
+
+       for (i=0; i<len/4; i++)
+               buf32[i] = bswap_32(buf32[i]);
+}
+
+#define swap_endianness_pcm swap_endianness_ucode
+
+static void swap_endianness_iv(struct iv *iv)
+{
+       iv->reg = bswap_16(iv->reg);
+       iv->size = bswap_16(iv->size);
+       iv->val = bswap_32(iv->val);
+}
+
+static void build_ivs(struct b43_iv **_out, size_t *_out_size,
+                     struct iv *in, size_t in_size,
+                     struct fw_header *hdr,
+                     uint32_t flags)
+{
+       struct iv *iv;
+       struct b43_iv *out;
+       uint32_t i;
+       size_t out_size = 0;
+
+       if (sizeof(struct b43_iv) != 6) {
+               printf("sizeof(struct b43_iv) != 6\n");
+               exit(255);
+       }
+
+       out = malloc(in_size);
+       if (!out) {
+               perror("failed to allocate buffer");
+               exit(1);
+       }
+       *_out = out;
+       for (i = 0; i < in_size / sizeof(*iv); i++, in++) {
+               if (flags & FW_FLAG_LE)
+                       swap_endianness_iv(in);
+               /* input-IV is BigEndian */
+               if (in->reg & to_be16(~FW_IV_OFFSET_MASK)) {
+                       printf("Input file IV offset > 0x%X\n", FW_IV_OFFSET_MASK);
+                       exit(1);
+               }
+               out->offset_size = in->reg;
+               if (in->size == to_be16(4)) {
+                       out->offset_size |= to_be16(FW_IV_32BIT);
+                       out->data.d32 = in->val;
+
+                       out_size += sizeof(be16_t) + sizeof(be32_t);
+                       out = (struct b43_iv *)((uint8_t *)out + sizeof(be16_t) + sizeof(be32_t));
+               } else if (in->size == to_be16(2)) {
+                       if (in->val & to_be32(~0xFFFF)) {
+                               printf("Input file 16bit IV value overflow\n");
+                               exit(1);
+                       }
+                       out->data.d16 = to_be16(from_be32(in->val));
+
+                       out_size += sizeof(be16_t) + sizeof(be16_t);
+                       out = (struct b43_iv *)((uint8_t *)out + sizeof(be16_t) + sizeof(be16_t));
+               } else {
+                       printf("Input file IV size != 2|4\n");
+                       exit(1);
+               }
+       }
+       hdr->size = to_be32(i);
+       *_out_size = out_size;
+}
+
+static void write_file(const char *name, uint8_t *buf, uint32_t len,
+                      const struct fw_header *hdr, uint32_t flags)
+{
+       FILE *f;
+       char nbuf[4096];
+       const char *dir;
+       int r;
+
+       if (flags & FW_FLAG_V4)
+               dir = V4_FW_DIRNAME;
+       else
+               dir = V3_FW_DIRNAME;
+
+       r = snprintf(nbuf, sizeof(nbuf),
+                    "%s/%s", cmdargs.target_dir, dir);
+       if (r >= sizeof(nbuf)) {
+               fprintf(stderr, "name too long");
+               exit(2);
+       }
+
+       r = mkdir(nbuf, 0770);
+       if (r && errno != EEXIST) {
+               perror("failed to create output directory");
+               exit(2);
+       }
+
+       r = snprintf(nbuf, sizeof(nbuf),
+                    "%s/%s/%s.fw", cmdargs.target_dir, dir, name);
+       if (r >= sizeof(nbuf)) {
+               fprintf(stderr, "name too long");
+               exit(2);
+       }
+       f = fopen(nbuf, "w");
+       if (!f) {
+               perror("failed to open file");
+               exit(2);
+       }
+       if (fwrite(hdr, sizeof(*hdr), 1, f) != 1) {
+               perror("failed to write file");
+               exit(2);
+       }
+       if (fwrite(buf, 1, len, f) != len) {
+               perror("failed to write file");
+               exit(2);
+       }
+       fclose(f);
+}
+
+static void extract_or_identify(FILE *f, const struct extract *extract,
+                               uint32_t flags)
+{
+       uint8_t *buf;
+       size_t data_length;
+       int ucode_rev = 0;
+       struct fw_header hdr;
+
+       memset(&hdr, 0, sizeof(hdr));
+       hdr.ver = FW_HDR_VER;
+
+       if (fseek(f, extract->offset, SEEK_SET)) {
+               perror("failed to seek on file");
+               exit(2);
+       }
+
+       buf = malloc(extract->length);
+       if (!buf) {
+               perror("failed to allocate buffer");
+               exit(3);
+       }
+       if (fread(buf, 1, extract->length, f) != extract->length) {
+               perror("failed to read complete data");
+               exit(3);
+       }
+
+       switch (extract->type) {
+       case EXT_UCODE_3:
+               ucode_rev += 1;
+       case EXT_UCODE_2:
+               ucode_rev += 1;
+       case EXT_UCODE_1:
+               ucode_rev += 1;
+               data_length = extract->length;
+               if (flags & FW_FLAG_LE)
+                       swap_endianness_ucode(buf, data_length);
+               analyse_ucode(ucode_rev, buf, data_length);
+               hdr.type = FW_TYPE_UCODE;
+               hdr.size = to_be32(data_length);
+               break;
+       case EXT_PCM:
+               data_length = extract->length;
+               if (flags & FW_FLAG_LE)
+                       swap_endianness_pcm(buf, data_length);
+               hdr.type = FW_TYPE_PCM;
+               hdr.size = to_be32(data_length);
+               break;
+       case EXT_IV: {
+               struct b43_iv *ivs;
+
+               hdr.type = FW_TYPE_IV;
+               build_ivs(&ivs, &data_length,
+                         (struct iv *)buf, extract->length,
+                         &hdr, flags);
+               free(buf);
+               buf = (uint8_t *)ivs;
+               break;
+       }
+       default:
+               exit(255);
+       }
+
+       if (!cmdargs.identify_only)
+               write_file(extract->name, buf, data_length, &hdr, flags);
+
+       free(buf);
+}
+
+static void print_banner(void)
+{
+       printf("b43-fwcutter version " FWCUTTER_VERSION "\n");
+}
+
+static void print_file(const struct file *file)
+{
+       char filename[30];
+       char shortname[30];
+
+       if (file->flags & FW_FLAG_V4)
+               printf(V4_FW_DIRNAME "\t\t");
+       else
+               printf(V3_FW_DIRNAME "\t");
+
+       if (strlen(file->name) > 20) {
+               strncpy(shortname, file->name, 20);
+               shortname[20] = '\0';
+               snprintf(filename, sizeof(filename), "%s..", shortname);
+       } else
+               strcpy (filename, file->name);
+
+       printf("%s\t", filename);
+       if (strlen(filename) < 8) printf("\t");
+       if (strlen(filename) < 16) printf("\t");
+
+       printf("%s\t", file->ucode_version);
+       if (strlen(file->ucode_version) < 8) printf("\t");
+
+       printf("%s\n", file->md5);
+}
+
+static void print_supported_files(void)
+{
+       int i;
+
+       print_banner();
+       printf("\nExtracting firmware is possible "
+              "from these binary driver files:\n\n");
+       printf("<driver>\t"
+              "<filename>\t\t"
+              "<microcode>\t"
+              "<MD5 checksum>\n\n");
+       /* print for legacy driver first */
+       for (i = 0; i < FILES; i++)
+               if (!(files[i].flags & FW_FLAG_V4))
+                       print_file(&files[i]);
+       for (i = 0; i < FILES; i++)
+               if (files[i].flags & FW_FLAG_V4)
+                       print_file(&files[i]);
+       printf("\n");
+}
+
+static const struct file *find_file(FILE *fd)
+{
+       unsigned char buffer[16384], signature[16];
+       struct MD5Context md5c;
+       char md5sig[33];
+       int i;
+
+       MD5Init(&md5c);
+       while ((i = (int) fread(buffer, 1, sizeof(buffer), fd)) > 0)
+               MD5Update(&md5c, buffer, (unsigned) i);
+       MD5Final(signature, &md5c);
+
+       snprintf(md5sig, sizeof(md5sig),
+                "%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x"
+                "%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x",
+                signature[0], signature[1], signature[2], signature[3],
+                signature[4], signature[5], signature[6], signature[7],
+                signature[8], signature[9], signature[10], signature[11],
+                signature[12], signature[13], signature[14], signature[15]);
+
+       for (i = 0; i < FILES; ++i) {
+               if (strcasecmp(md5sig, files[i].md5) == 0) {
+                       printf("This file is recognised as:\n");
+                       printf("  filename   :  %s\n", files[i].name);
+                       printf("  version    :  %s\n", files[i].ucode_version);
+                       printf("  MD5        :  %s\n", files[i].md5);
+                       return &files[i];
+               }
+       }
+       printf("Sorry, the input file is either wrong or "
+              "not supported by b43-fwcutter.\n");
+       printf("This file has an unknown MD5sum %s.\n", md5sig);
+
+       return NULL;
+}
+
+static void print_usage(int argc, char *argv[])
+{
+       print_banner();
+       printf("\nUsage: %s [OPTION] [driver.sys]\n", argv[0]);
+       printf("  -l|--list             "
+              "List supported driver versions\n");
+       printf("  -i|--identify         "
+              "Only identify the driver file (don't extract)\n");
+       printf("  -w|--target-dir DIR   "
+              "Extract and write firmware to DIR\n");
+       printf("  -v|--version          "
+              "Print b43-fwcutter version\n");
+       printf("  -h|--help             "
+              "Print this help\n");
+       printf("\nExample: %s bcmwl5.sys\n"
+              "         to extract the firmware blobs from bcmwl5.sys\n", 
+              argv[0]);
+}
+
+static int do_cmp_arg(char **argv, int *pos,
+                     const char *template,
+                     int allow_merged,
+                     char **param)
+{
+       char *arg;
+       char *next_arg;
+       size_t arg_len, template_len;
+
+       arg = argv[*pos];
+       next_arg = argv[*pos + 1];
+       arg_len = strlen(arg);
+       template_len = strlen(template);
+
+       if (param) {
+               /* Maybe we have a merged parameter here.
+                * A merged parameter is "-pfoobar" for example.
+                */
+               if (allow_merged && arg_len > template_len) {
+                       if (memcmp(arg, template, template_len) == 0) {
+                               *param = arg + template_len;
+                               return ARG_MATCH;
+                       }
+                       return ARG_NOMATCH;
+               } else if (arg_len != template_len)
+                       return ARG_NOMATCH;
+               *param = next_arg;
+       }
+       if (strcmp(arg, template) == 0) {
+               if (param) {
+                       /* Skip the parameter on the next iteration. */
+                       (*pos)++;
+                       if (*param == 0) {
+                               printf("%s needs a parameter\n", arg);
+                               return ARG_ERROR;
+                       }
+               }
+               return ARG_MATCH;
+       }
+
+       return ARG_NOMATCH;
+}
+
+/* Simple and lean command line argument parsing. */
+static int cmp_arg(char **argv, int *pos,
+                  const char *long_template,
+                  const char *short_template,
+                  char **param)
+{
+       int err;
+
+       if (long_template) {
+               err = do_cmp_arg(argv, pos, long_template, 0, param);
+               if (err == ARG_MATCH || err == ARG_ERROR)
+                       return err;
+       }
+       err = ARG_NOMATCH;
+       if (short_template)
+               err = do_cmp_arg(argv, pos, short_template, 1, param);
+       return err;
+}
+
+static int parse_args(int argc, char *argv[])
+{
+       int i, res;
+       char *param;
+
+       if (argc < 2)
+               goto out_usage;
+       for (i = 1; i < argc; i++) {
+               res = cmp_arg(argv, &i, "--list", "-l", 0);
+               if (res == ARG_MATCH) {
+                       print_supported_files();
+                       return 1;
+               } else if (res == ARG_ERROR)
+                       goto out;
+
+               res = cmp_arg(argv, &i, "--version", "-v", 0);
+               if (res == ARG_MATCH) {
+                       print_banner();
+                       return 1;
+               } else if (res == ARG_ERROR)
+                       goto out;
+
+               res = cmp_arg(argv, &i, "--help", "-h", 0);
+               if (res == ARG_MATCH)
+                       goto out_usage;
+               else if (res == ARG_ERROR)
+                       goto out;
+
+               res = cmp_arg(argv, &i, "--identify", "-i", 0);
+               if (res == ARG_MATCH) {
+                       cmdargs.identify_only = 1;
+                       continue;
+               } else if (res == ARG_ERROR)
+                       goto out;
+
+               res = cmp_arg(argv, &i, "--target-dir", "-w", &param);
+               if (res == ARG_MATCH) {
+                       cmdargs.target_dir = param;
+                       continue;
+               } else if (res == ARG_ERROR)
+                       goto out;
+
+               cmdargs.infile = argv[i];
+               break;
+       }
+
+       if (!cmdargs.infile)
+               goto out_usage;
+       return 0;
+
+out_usage:
+       print_usage(argc, argv);
+out:
+       return -1;      
+}
+
+int main(int argc, char *argv[])
+{
+       FILE *fd;
+       const struct file *file;
+       const struct extract *extract;
+       int err;
+       const char *dir;
+
+       cmdargs.target_dir = ".";
+       err = parse_args(argc, argv);
+       if (err == 1)
+               return 0;
+       else if (err != 0)
+               return err;
+
+       fd = fopen(cmdargs.infile, "rb");
+       if (!fd) {
+               fprintf(stderr, "Cannot open input file %s\n", cmdargs.infile);
+               return 2;
+       }
+
+       err = -1;
+       file = find_file(fd);
+       if (!file)
+               goto out_close;
+
+       if (file->flags & FW_FLAG_V4)
+               dir = V4_FW_DIRNAME;
+       else
+               dir = V3_FW_DIRNAME;
+
+       extract = file->extract;
+       while (extract->name) {
+               printf("%s %s/%s.fw\n",
+                      cmdargs.identify_only ? "Contains" : "Extracting",
+                      dir, extract->name);
+               extract_or_identify(fd, extract, file->flags);
+               extract++;
+       }
+
+       err = 0;
+out_close:
+       fclose(fd);
+
+       return err;
+}
diff --git a/fwcutter/fwcutter.h b/fwcutter/fwcutter.h
new file mode 100644 (file)
index 0000000..2f23d81
--- /dev/null
@@ -0,0 +1,97 @@
+#ifndef _FWCUTTER_H_
+#define _FWCUTTER_H_
+
+#define FW_FLAG_LE     0x01    /* little endian? convert */
+#define FW_FLAG_V4     0x02    /* b43 vs. b43legacy */
+
+#define fwcutter_stringify_1(x)        #x
+#define fwcutter_stringify(x)  fwcutter_stringify_1(x)
+#define FWCUTTER_VERSION       fwcutter_stringify(FWCUTTER_VERSION_)
+
+typedef uint16_t be16_t; /* Big-endian 16bit */
+typedef uint32_t be32_t; /* Big-endian 32bit */
+
+#ifdef __DragonFly__
+#define bswap_16       bswap16
+#define bswap_32       bswap32
+#endif
+
+#define ARG_MATCH      0
+#define ARG_NOMATCH    1
+#define ARG_ERROR      -1
+
+struct cmdline_args {
+       const char *infile;
+       const char *target_dir;
+       int identify_only;
+};
+
+struct insn {
+       uint32_t opcode;
+       uint16_t op1, op2, op3;
+};
+
+/* The IV how it's done in the binary driver files. */
+struct iv {
+       be16_t reg, size;
+       be32_t val;
+} __attribute__((__packed__));
+
+enum extract_type {
+       EXT_UNDEFINED, /* error catcher  */
+       EXT_UCODE_1,   /* rev  <= 4 ucode */
+       EXT_UCODE_2,   /* rev 5..14 ucode */
+       EXT_UCODE_3,   /* rev >= 15 ucode */
+       EXT_PCM,       /* "pcm" values   */
+       EXT_IV,        /* initial values */
+};
+
+struct extract {
+       const char *name;
+       const uint32_t offset;
+       const uint32_t length;
+       const enum extract_type type;
+};
+
+#define EXTRACT_LIST_END { .name = NULL, }
+
+struct file {
+       const char *name;
+       const char *ucode_version;
+       const char *md5;
+       const struct extract *extract;
+       const uint32_t flags;
+};
+
+/* The header that's put in to every .fw file */
+struct fw_header {
+       /* Type of the firmware data */
+       uint8_t type;
+       /* Version number of the firmware data format */
+       uint8_t ver;
+       uint8_t __padding[2];
+       /* Size of the data. For ucode and PCM this is in bytes.
+        * For IV this is in number-of-ivs. */
+       be32_t size;
+} __attribute__((__packed__));
+
+#define FW_TYPE_UCODE  'u'
+#define FW_TYPE_PCM    'p'
+#define FW_TYPE_IV     'i'
+
+#define FW_HDR_VER     0x01
+
+/* The IV in the .fw file */
+struct b43_iv {
+       be16_t offset_size;
+       union {
+               be16_t d16;
+               be32_t d32;
+       } data __attribute__((__packed__));
+} __attribute__((__packed__));
+
+#define FW_IV_OFFSET_MASK      0x7FFF
+#define FW_IV_32BIT            0x8000
+
+
+#endif /* _FWCUTTER_H_ */
diff --git a/fwcutter/fwcutter_list.h b/fwcutter/fwcutter_list.h
new file mode 100644 (file)
index 0000000..c3631ce
--- /dev/null
@@ -0,0 +1,155 @@
+
+/* file member lists */
+struct extract _e08665c5c5b66beb9c3b2dd54aa80cb3[] =
+{
+       { .name = "ucode2", .offset = 0x59ca0, .length = 0x3fe0, .type = EXT_UCODE_1, },
+       { .name = "ucode4", .offset = 0x5dc84, .length = 0x4e78, .type = EXT_UCODE_1, },
+       { .name = "ucode5", .offset = 0x62b00, .length = 0x5700, .type = EXT_UCODE_2, },
+       { .name = "ucode11", .offset = 0x68204, .length = 0x54a8, .type = EXT_UCODE_2, },
+       { .name = "pcm4", .offset = 0x6d6b0, .length = 0x520, .type = EXT_PCM, },
+       { .name = "pcm5", .offset = 0x6dbd4, .length = 0x520, .type = EXT_PCM, },
+       { .name = "a0g0bsinitvals2", .offset = 0x54ad0 + 0x30d8, .length = 0x18 - 8, .type = EXT_IV, },
+       { .name = "b0g0bsinitvals5", .offset = 0x54ad0 + 0x3ae0, .length = 0x100 - 8, .type = EXT_IV, },
+       { .name = "a0g0initvals5", .offset = 0x54ad0 + 0x3be0, .length = 0x9f0 - 8, .type = EXT_IV, },
+       { .name = "a0g1bsinitvals5", .offset = 0x54ad0 + 0x50c0, .length = 0x100 - 8, .type = EXT_IV, },
+       { .name = "a0g0initvals2", .offset = 0x54ad0 + 0x2320, .length = 0xdb8 - 8, .type = EXT_IV, },
+       { .name = "a0g1initvals5", .offset = 0x54ad0 + 0x45d0, .length = 0x9f0 - 8, .type = EXT_IV, },
+       { .name = "b0g0bsinitvals2", .offset = 0x54ad0 + 0x2308, .length = 0x18 - 8, .type = EXT_IV, },
+       { .name = "b0g0initvals5", .offset = 0x54ad0 + 0x30f0, .length = 0x9f0 - 8, .type = EXT_IV, },
+       { .name = "b0g0initvals2", .offset = 0x54ad0 + 0x1550, .length = 0xdb8 - 8, .type = EXT_IV, },
+       { .name = "a0g0bsinitvals5", .offset = 0x54ad0 + 0x4fc0, .length = 0x100 - 8, .type = EXT_IV, },
+       EXTRACT_LIST_END
+};
+
+struct extract _9207bc565c2fc9fa1591f6c7911d3fc0[] =
+{
+       { .name = "ucode4",  .offset = 0x66220 +  0x7ad8, .length = 0x4e68, .type = EXT_UCODE_1, },
+       { .name = "ucode5",  .offset = 0x66220 +  0xc944, .length = 0x5640, .type = EXT_UCODE_2, },
+       { .name = "ucode11", .offset = 0x66220 + 0x11f90, .length = 0x67e0, .type = EXT_UCODE_2, },
+       { .name = "ucode13", .offset = 0x66220 + 0x18774, .length = 0x5f60, .type = EXT_UCODE_2, },
+       { .name = "pcm4", .offset = 0x66220 + 0x1e6d8, .length = 0x520, .type = EXT_PCM, },
+       { .name = "pcm5", .offset = 0x66220 + 0x1ebfc, .length = 0x520, .type = EXT_PCM, },
+       { .name = "b0g0initvals4",      .offset = 0x66220 + 0x1710, .length = 0xe90 - 8, .type = EXT_IV, },
+       { .name = "b0g0bsinitvals4",    .offset = 0x66220 + 0x25a0, .length = 0x18 - 8, .type = EXT_IV, },
+       { .name = "a0g0initvals4",      .offset = 0x66220 + 0x25b8, .length = 0xe90 - 8, .type = EXT_IV, },
+       { .name = "a0g0bsinitvals4",    .offset = 0x66220 + 0x3448, .length = 0x18 - 8, .type = EXT_IV, },
+       { .name = "b0g0initvals5",      .offset = 0x66220 + 0x3460, .length = 0xa28 - 8, .type = EXT_IV, },
+       { .name = "b0g0bsinitvals5",    .offset = 0x66220 + 0x3e88, .length = 0x100 - 8, .type = EXT_IV, },
+       { .name = "a0g0initvals5",      .offset = 0x66220 + 0x3f88, .length = 0xa28 - 8, .type = EXT_IV, },
+       { .name = "a0g1initvals5",      .offset = 0x66220 + 0x49b0, .length = 0xa28 - 8, .type = EXT_IV, },
+       { .name = "a0g0bsinitvals5",    .offset = 0x66220 + 0x53d8, .length = 0x100 - 8, .type = EXT_IV, },
+       { .name = "a0g1bsinitvals5",    .offset = 0x66220 + 0x54d8, .length = 0x100 - 8, .type = EXT_IV, },
+       { .name = "lp0initvals13",      .offset = 0x66220 + 0x5620, .length = 0xb38 - 8, .type = EXT_IV, },
+       { .name = "lp0bsinitvals13",    .offset = 0x66220 + 0x6158, .length = 0x100 - 8, .type = EXT_IV, },
+       { .name = "b0g0initvals13",     .offset = 0x66220 + 0x6258, .length = 0xb40 - 8, .type = EXT_IV, },
+       { .name = "b0g0bsinitvals13",.offset = 0x66220 + 0x6d98, .length = 0x100 - 8, .type = EXT_IV, },
+       { .name = "a0g1initvals13",     .offset = 0x66220 + 0x6e98, .length = 0xb40 - 8, .type = EXT_IV, },
+       { .name = "a0g1bsinitvals13",.offset = 0x66220 + 0x79d8, .length = 0x100 - 8, .type = EXT_IV, },
+       EXTRACT_LIST_END
+};
+
+struct extract _722e2e0d8cc04b8f118bb5afe6829ff9[] =
+{
+       { .name = "ucode4",  .offset = 0x76a10 +  0x8960, .length = 0x4e68, .type = EXT_UCODE_1, },
+       { .name = "ucode5",  .offset = 0x76a10 +  0xd7cc, .length = 0x5640, .type = EXT_UCODE_2, },
+       { .name = "ucode11", .offset = 0x76a10 + 0x12e18, .length = 0x67e0, .type = EXT_UCODE_2, },
+       { .name = "ucode13", .offset = 0x76a10 + 0x195fc, .length = 0x5f60, .type = EXT_UCODE_2, },
+       { .name = "pcm4", .offset = 0x76a10 + 0x1f560, .length = 0x520, .type = EXT_PCM, },
+       { .name = "pcm5", .offset = 0x76a10 + 0x1fa84, .length = 0x520, .type = EXT_PCM, },
+       { .name = "b0g0initvals4",      .offset = 0x76a10 + 0x1840, .length = 0xe90 - 8, .type = EXT_IV, },
+       { .name = "b0g0bsinitvals4",    .offset = 0x76a10 + 0x26d0, .length = 0x18 - 8, .type = EXT_IV, },
+       { .name = "a0g0initvals4",      .offset = 0x76a10 + 0x26e8, .length = 0xe90 - 8, .type = EXT_IV, },
+       { .name = "a0g0bsinitvals4",    .offset = 0x76a10 + 0x3578, .length = 0x18 - 8, .type = EXT_IV, },
+       { .name = "b0g0initvals5",      .offset = 0x76a10 + 0x3590, .length = 0xa28 - 8, .type = EXT_IV, },
+       { .name = "b0g0bsinitvals5",    .offset = 0x76a10 + 0x3fb8, .length = 0x100 - 8, .type = EXT_IV, },
+       { .name = "a0g0initvals5",      .offset = 0x76a10 + 0x40b8, .length = 0xa28 - 8, .type = EXT_IV, },
+       { .name = "a0g1initvals5",      .offset = 0x76a10 + 0x4ae0, .length = 0xa28 - 8, .type = EXT_IV, },
+       { .name = "a0g0bsinitvals5",    .offset = 0x76a10 + 0x5508, .length = 0x100 - 8, .type = EXT_IV, },
+       { .name = "a0g1bsinitvals5",    .offset = 0x76a10 + 0x5608, .length = 0x100 - 8, .type = EXT_IV, },
+       { .name = "lp0initvals13",      .offset = 0x76a10 + 0x64a8, .length = 0xb38 - 8, .type = EXT_IV, },
+       { .name = "lp0bsinitvals13",    .offset = 0x76a10 + 0x6fe0, .length = 0x100 - 8, .type = EXT_IV, },
+       { .name = "b0g0initvals13",     .offset = 0x76a10 + 0x70e0, .length = 0xb40 - 8, .type = EXT_IV, },
+       { .name = "b0g0bsinitvals13",.offset = 0x76a10 + 0x7c20, .length = 0x100 - 8, .type = EXT_IV, },
+       { .name = "a0g1initvals13",     .offset = 0x76a10 + 0x7d20, .length = 0xb40 - 8, .type = EXT_IV, },
+       { .name = "a0g1bsinitvals13",.offset = 0x76a10 + 0x8860, .length = 0x100 - 8, .type = EXT_IV, },
+       EXTRACT_LIST_END
+};
+
+struct extract _1e4763b4cb8cfbaae43e5c6d3d6b2ae7[] =
+{
+       { .name = "ucode5",  .offset = 0x71c80 +  0xacd0, .length = 0x5768, .type = EXT_UCODE_2, },
+       { .name = "ucode9",  .offset = 0x71c80 + 0x1043c, .length = 0x6240, .type = EXT_UCODE_2, },
+       { .name = "ucode11", .offset = 0x71c80 + 0x16680, .length = 0x74a0, .type = EXT_UCODE_2, },
+       { .name = "ucode13", .offset = 0x71c80 + 0x1db24, .length = 0x7de0, .type = EXT_UCODE_2, },
+       { .name = "ucode14", .offset = 0x71c80 + 0x25908, .length = 0x7a90, .type = EXT_UCODE_2, },
+       { .name = "ucode15", .offset = 0x71c80 + 0x2d39c, .length = 0x7710, .type = EXT_UCODE_3, },
+       { .name = "pcm5", .offset = 0x71c80 + 0x34ab0, .length = 0x520, .type = EXT_PCM, },
+       { .name = "b0g0initvals5",      .offset = 0x71c80 + 0x14d0, .length = 0xa10 - 8, .type = EXT_IV, },
+       { .name = "b0g0bsinitvals5",    .offset = 0x71c80 + 0x1ee0, .length = 0x100 - 8, .type = EXT_IV, },
+       { .name = "a0g0initvals5",      .offset = 0x71c80 + 0x1fe0, .length = 0xa10 - 8, .type = EXT_IV, },
+       { .name = "a0g1initvals5",      .offset = 0x71c80 + 0x29f0, .length = 0xa10 - 8, .type = EXT_IV, },
+       { .name = "a0g0bsinitvals5",    .offset = 0x71c80 + 0x3400, .length = 0x100 - 8, .type = EXT_IV, },
+       { .name = "a0g1bsinitvals5",    .offset = 0x71c80 + 0x3500, .length = 0x100 - 8, .type = EXT_IV, },
+       { .name = "b0g0initvals9",      .offset = 0x71c80 + 0x3600, .length = 0xae8 - 8, .type = EXT_IV, },
+       { .name = "b0g0bsinitvals9",    .offset = 0x71c80 + 0x40e8, .length = 0x100 - 8, .type = EXT_IV, },
+       { .name = "a0g0initvals9",      .offset = 0x71c80 + 0x41e8, .length = 0xae8 - 8, .type = EXT_IV, },
+       { .name = "a0g1initvals9",      .offset = 0x71c80 + 0x4cd0, .length = 0xae8 - 8, .type = EXT_IV, },
+       { .name = "a0g0bsinitvals9",    .offset = 0x71c80 + 0x57b8, .length = 0x100 - 8, .type = EXT_IV, },
+       { .name = "a0g1bsinitvals9",    .offset = 0x71c80 + 0x58b8, .length = 0x100 - 8, .type = EXT_IV, },
+       { .name = "n0initvals11",       .offset = 0x71c80 + 0x59b8, .length = 0xb78 - 8, .type = EXT_IV, },
+       { .name = "n0bsinitvals11",     .offset = 0x71c80 + 0x6530, .length = 0x100 - 8, .type = EXT_IV, },
+       { .name = "n0absinitvals11",    .offset = 0x71c80 + 0x6630, .length = 0x100 - 8, .type = EXT_IV, },
+       { .name = "lp0initvals13",      .offset = 0x71c80 + 0x6730, .length = 0x1360 - 8, .type = EXT_IV, },
+       { .name = "lp0bsinitvals13",    .offset = 0x71c80 + 0x7a90, .length = 0x100 - 8, .type = EXT_IV, },
+       { .name = "b0g0initvals13",     .offset = 0x71c80 + 0x7b90, .length = 0xb60 - 8, .type = EXT_IV, },
+       { .name = "b0g0bsinitvals13",.offset = 0x71c80 + 0x86f0, .length = 0x100 - 8, .type = EXT_IV, },
+       { .name = "a0g1initvals13",     .offset = 0x71c80 + 0x87f0, .length = 0xb60 - 8, .type = EXT_IV, },
+       { .name = "a0g1bsinitvals13",.offset = 0x71c80 + 0x9350, .length = 0x100 - 8, .type = EXT_IV, },
+       { .name = "lp0initvals14",      .offset = 0x71c80 + 0x9450, .length = 0xb48 - 8, .type = EXT_IV, },
+       { .name = "lp0bsinitvals14",    .offset = 0x71c80 + 0x9f98, .length = 0x100 - 8, .type = EXT_IV, },
+       { .name = "lp0initvals15",      .offset = 0x71c80 + 0xa098, .length = 0xb38 - 8, .type = EXT_IV, },
+       { .name = "lp0bsinitvals15",    .offset = 0x71c80 + 0xabd0, .length = 0x100 - 8, .type = EXT_IV, },
+       EXTRACT_LIST_END
+};
+
+
+/*
+ * Links change, so let's not put them into the README.
+ * I still put them here so we know where the file was obtained.
+ */
+static const struct file files[] = 
+{
+       {
+               .name           = "wl_apsta.o",
+               .ucode_version  = "295.14",
+               .md5            = "e08665c5c5b66beb9c3b2dd54aa80cb3",
+               .flags          = FW_FLAG_LE,
+               .extract        = _e08665c5c5b66beb9c3b2dd54aa80cb3,
+       },
+       {
+               /* http://downloads.openwrt.org/sources/broadcom-wl-4.80.53.0.tar.bz2 */
+               .name           = "wl_apsta.o",
+               .ucode_version  = "351.126",
+               .md5            = "9207bc565c2fc9fa1591f6c7911d3fc0",
+               .flags          = FW_FLAG_LE | FW_FLAG_V4,
+               .extract        = _9207bc565c2fc9fa1591f6c7911d3fc0,
+       },
+       {
+               /* http://downloads.openwrt.org/sources/broadcom-wl-4.80.53.0.tar.bz2 */
+               .name           = "wl_apsta_mimo.o",
+               .ucode_version  = "351.126",
+               .md5            = "722e2e0d8cc04b8f118bb5afe6829ff9",
+               .flags          = FW_FLAG_LE | FW_FLAG_V4,
+               .extract        = _722e2e0d8cc04b8f118bb5afe6829ff9,
+       },
+       {
+               /* ftp://ftp.linksys.com/opensourcecode/wrt150nv11/1.51.3/ */
+               .name           = "wl_ap.o",
+               .ucode_version  = "410.2160",
+               .md5            = "1e4763b4cb8cfbaae43e5c6d3d6b2ae7",
+               .flags          = FW_FLAG_LE | FW_FLAG_V4,
+               .extract        = _1e4763b4cb8cfbaae43e5c6d3d6b2ae7,
+       },
+};
+
+#define FILES (sizeof(files) / sizeof(files[0]))
diff --git a/fwcutter/md5.c b/fwcutter/md5.c
new file mode 100644 (file)
index 0000000..3b90942
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+/* Brutally hacked by John Walker back from ANSI C to K&R (no
+   prototypes) to maintain the tradition that Netfone will compile
+   with Sun's original "cc". */
+/* Ripped out ugly K&R again ;) --mbuesch */
+/* killed stupid endianness thing --jmberg */
+
+#include <memory.h>             /* for memcpy() */
+#include "md5.h"
+
+static void byteReverse(unsigned char *buf, unsigned longs)
+{
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+/* we need the value in big-endian */
+    uint32_t t;
+
+    do {
+       t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+           ((unsigned) buf[1] << 8 | buf[0]);
+       *(uint32_t *) buf = t;
+       buf += 4;
+    } while (--longs);
+#endif
+}
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+       ( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data.  MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+static void MD5Transform(uint32_t *buf, uint32_t *in)
+{
+    register uint32_t a, b, c, d;
+
+    a = buf[0];
+    b = buf[1];
+    c = buf[2];
+    d = buf[3];
+
+    MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+    MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+    MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+    MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+    MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+    MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+    MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+    MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+    MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+    MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+    MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+    MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+    MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+    MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+    MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+    MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+    MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+    MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+    MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+    MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+    MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+    MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+    MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+    MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+    MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+    MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+    MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+    MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+    MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+    MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+    MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+    MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+    MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+    MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+    MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+    MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+    MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+    MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+    MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+    MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+    MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+    MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+    MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+    MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+    MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+    MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+    MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+    MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+    MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+    MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+    MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+    MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+    MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+    MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+    MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+    MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+    MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+    MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+    MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+    MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+    MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+    MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+    MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+    MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+    buf[0] += a;
+    buf[1] += b;
+    buf[2] += c;
+    buf[3] += d;
+}
+
+/*
+ * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Init(struct MD5Context *ctx)
+{
+    ctx->buf[0] = 0x67452301;
+    ctx->buf[1] = 0xefcdab89;
+    ctx->buf[2] = 0x98badcfe;
+    ctx->buf[3] = 0x10325476;
+
+    ctx->bits[0] = 0;
+    ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void MD5Update(struct MD5Context *ctx, unsigned char *buf, unsigned len)
+{
+    uint32_t t;
+
+    /* Update bitcount */
+
+    t = ctx->bits[0];
+    if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
+       ctx->bits[1]++;         /* Carry from low to high */
+    ctx->bits[1] += len >> 29;
+
+    t = (t >> 3) & 0x3f;       /* Bytes already in shsInfo->data */
+
+    /* Handle any leading odd-sized chunks */
+
+    if (t) {
+       unsigned char *p = (unsigned char *) ctx->in + t;
+
+       t = 64 - t;
+       if (len < t) {
+           memcpy(p, buf, len);
+           return;
+       }
+       memcpy(p, buf, t);
+       byteReverse(ctx->in, 16);
+       MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+       buf += t;
+       len -= t;
+    }
+    /* Process data in 64-byte chunks */
+
+    while (len >= 64) {
+       memcpy(ctx->in, buf, 64);
+       byteReverse(ctx->in, 16);
+       MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+       buf += 64;
+       len -= 64;
+    }
+
+    /* Handle any remaining bytes of data. */
+
+    memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern 
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void MD5Final(unsigned char *digest, struct MD5Context *ctx)
+{
+    unsigned count;
+    unsigned char *p;
+
+    /* Compute number of bytes mod 64 */
+    count = (ctx->bits[0] >> 3) & 0x3F;
+
+    /* Set the first char of padding to 0x80.  This is safe since there is
+       always at least one byte free */
+    p = ctx->in + count;
+    *p++ = 0x80;
+
+    /* Bytes of padding needed to make 64 bytes */
+    count = 64 - 1 - count;
+
+    /* Pad out to 56 mod 64 */
+    if (count < 8) {
+       /* Two lots of padding:  Pad the first block to 64 bytes */
+       memset(p, 0, count);
+       byteReverse(ctx->in, 16);
+       MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+
+       /* Now fill the next block with 56 bytes */
+       memset(ctx->in, 0, 56);
+    } else {
+       /* Pad block to 56 bytes */
+       memset(p, 0, count - 8);
+    }
+    byteReverse(ctx->in, 14);
+
+    /* Append length in bits and transform */
+    ((uint32_t *) ctx->in)[14] = ctx->bits[0];
+    ((uint32_t *) ctx->in)[15] = ctx->bits[1];
+
+    MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+    byteReverse((unsigned char *) ctx->buf, 4);
+    memcpy(digest, ctx->buf, 16);
+    memset(ctx, 0, sizeof(ctx));        /* In case it's sensitive */
+}
diff --git a/fwcutter/md5.h b/fwcutter/md5.h
new file mode 100644 (file)
index 0000000..ee574e4
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef FWCUTTER_MD5_H_
+#define FWCUTTER_MD5_H_
+
+#include <stdint.h>
+
+struct MD5Context {
+        uint32_t buf[4];
+        uint32_t bits[2];
+        unsigned char in[64];
+};
+
+void MD5Init(struct MD5Context *ctx);
+void MD5Update(struct MD5Context *ctx, unsigned char *buf, unsigned len);
+void MD5Final(unsigned char *digest, struct MD5Context *ctx);
+
+#endif /* FWCUTTER_MD5_H_ */
diff --git a/ssb_sprom/COPYING b/ssb_sprom/COPYING
new file mode 100644 (file)
index 0000000..3912109
--- /dev/null
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/ssb_sprom/Makefile b/ssb_sprom/Makefile
new file mode 100644 (file)
index 0000000..d689120
--- /dev/null
@@ -0,0 +1,26 @@
+VERSION = 001
+
+CC = cc
+PREFIX = /usr/local
+CFLAGS = -std=c99 -O2 -fomit-frame-pointer -Wall -D_BSD_SOURCE
+LDFLAGS =
+
+
+OBJECTS = ssb_sprom.o utils.o
+
+CFLAGS += -DVERSION_=$(VERSION)
+
+all: ssb_sprom
+
+ssb_sprom: $(OBJECTS)
+       $(CC) $(CFLAGS) -o ssb-sprom $(OBJECTS) $(LDFLAGS)
+
+install: all
+       -install -o 0 -g 0 -m 755 ssb-sprom $(PREFIX)/bin/
+
+clean:
+       -rm -f *~ *.o *.orig *.rej ssb-sprom
+
+# dependencies
+ssb_sprom.o: ssb_sprom.h utils.h
+utils.o: utils.h ssb_sprom.h
diff --git a/ssb_sprom/README b/ssb_sprom/README
new file mode 100644 (file)
index 0000000..f1c2edf
--- /dev/null
@@ -0,0 +1,15 @@
+
+       Broadcom-SSB SPROM data modification tool
+       =========================================
+
+About this software
+-------------------
+
+A tool for the convenient modification of the
+Broadcom Sonics Silicon Backplane SPROM.
+This tool automatically adjusts the CRC after the modification.
+
+Requirements
+------------
+
+1)     C99 compatible compiler.
diff --git a/ssb_sprom/ssb_sprom.c b/ssb_sprom/ssb_sprom.c
new file mode 100644 (file)
index 0000000..51124f2
--- /dev/null
@@ -0,0 +1,1420 @@
+/*
+
+  Broadcom Sonics Silicon Backplane bus SPROM data modification tool
+
+  Copyright (c) 2006-2007 Michael Buesch <mb@bu3sch.de>
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; see the file COPYING.  If not, write to
+  the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
+  Boston, MA 02110-1301, USA.
+
+*/
+
+#include "ssb_sprom.h"
+#include "utils.h"
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+
+struct cmdline_args cmdargs;
+
+static int value_length_map[] = { /* value to number of bits */
+       [VALUE_RAW] = 8,
+       [VALUE_SUBP] = 16,
+       [VALUE_SUBV] = 16,
+       [VALUE_PPID] = 16,
+       [VALUE_BFLHI] = 16,
+       [VALUE_BFL] = 16,
+       [VALUE_BGMAC] = -1,
+       [VALUE_ETMAC] = -1,
+       [VALUE_AMAC] = -1,
+       [VALUE_ET0PHY] = 8,
+       [VALUE_ET1PHY] = 8,
+       [VALUE_ET0MDC] = 1,
+       [VALUE_ET1MDC] = 1,
+       [VALUE_BREV] = 8,
+       [VALUE_LOC] = 4,
+       [VALUE_ANTA0] = 1,
+       [VALUE_ANTA1] = 1,
+       [VALUE_ANTBG0] = 1,
+       [VALUE_ANTBG1] = 1,
+       [VALUE_ANTGA] = 8,
+       [VALUE_ANTGBG] = 8,
+       [VALUE_PA0B0] = 16,
+       [VALUE_PA0B1] = 16,
+       [VALUE_PA0B2] = 16,
+       [VALUE_PA1B0] = 16,
+       [VALUE_PA1B1] = 16,
+       [VALUE_PA1B2] = 16,
+       [VALUE_WL0GPIO0] = 8,
+       [VALUE_WL0GPIO1] = 8,
+       [VALUE_WL0GPIO2] = 8,
+       [VALUE_WL0GPIO3] = 8,
+       [VALUE_MAXPA] = 8,
+       [VALUE_MAXPBG] = 8,
+       [VALUE_ITSSIA] = 8,
+       [VALUE_ITSSIBG] = 8,
+       [VALUE_SVER] = 8,
+};
+
+
+static int hexdump_sprom(const uint8_t *sprom, char *buffer, size_t bsize)
+{
+       int i, pos = 0;
+
+       for (i = 0; i < SPROM_SIZE; i++) {
+               pos += snprintf(buffer + pos, bsize - pos - 1,
+                               "%02X", sprom[i] & 0xFF);
+       }
+
+       return pos + 1;
+}
+
+static uint8_t sprom_crc(const uint8_t *sprom)
+{
+       int i;
+       uint8_t crc = 0xFF;
+
+       for (i = 0; i < SPROM_SIZE - 1; i++)
+               crc = crc8(crc, sprom[i]);
+       crc ^= 0xFF;
+
+       return crc;
+}
+
+static int write_output_binary(int fd, const uint8_t *sprom)
+{
+       ssize_t w;
+
+       w = write(fd, sprom, SPROM_SIZE);
+       if (w < 0)
+               return -1;
+
+       return 0;
+}
+
+static int write_output_hex(int fd, const uint8_t *sprom)
+{
+       ssize_t w;
+       char tmp[SPROM_SIZE * 2 + 10] = { 0 };
+
+       hexdump_sprom(sprom, tmp, sizeof(tmp));
+       prinfo("Raw output:  %s\n", tmp);
+       w = write(fd, tmp, SPROM_SIZE * 2);
+       if (w < 0)
+               return -1;
+
+       return 0;
+}
+
+static int write_output(int fd, const uint8_t *sprom)
+{
+       int err;
+
+       if (cmdargs.outfile) {
+               err = ftruncate(fd, 0);
+               if (err) {
+                       prerror("Could not truncate --outfile %s\n",
+                               cmdargs.outfile);
+                       return -1;
+               }
+       }
+
+       if (cmdargs.bin_mode)
+               err = write_output_binary(fd, sprom);
+       else
+               err = write_output_hex(fd, sprom);
+       if (err)
+               prerror("Could not write output data.\n");
+
+       return err;
+}
+
+static int modify_value(uint8_t *sprom,
+                       struct cmdline_vparm *vparm)
+{
+       const uint16_t v = vparm->u.value;
+       uint16_t tmp = 0;
+
+       switch (vparm->type) {
+       case VALUE_RAW:
+               sprom[vparm->u.raw.offset] = vparm->u.raw.value;
+               break;
+       case VALUE_SUBP:
+               sprom[SPROM_SUBP + 0] = (v & 0x00FF);
+               sprom[SPROM_SUBP + 1] = (v & 0xFF00) >> 8;
+               break;
+       case VALUE_SUBV:
+               sprom[SPROM_SUBV + 0] = (v & 0x00FF);
+               sprom[SPROM_SUBV + 1] = (v & 0xFF00) >> 8;
+               break;
+       case VALUE_PPID:
+               sprom[SPROM_PPID + 0] = (v & 0x00FF);
+               sprom[SPROM_PPID + 1] = (v & 0xFF00) >> 8;
+               break;
+       case VALUE_BFLHI:
+               sprom[SPROM_BFLHI + 0] = (v & 0x00FF);
+               sprom[SPROM_BFLHI + 1] = (v & 0xFF00) >> 8;
+               break;
+       case VALUE_BFL:
+               sprom[SPROM_BOARDFLAGS + 0] = (v & 0x00FF);
+               sprom[SPROM_BOARDFLAGS + 1] = (v & 0xFF00) >> 8;
+               break;
+       case VALUE_BGMAC:
+               sprom[SPROM_IL0MACADDR + 1] = vparm->u.mac[0];
+               sprom[SPROM_IL0MACADDR + 0] = vparm->u.mac[1];
+               sprom[SPROM_IL0MACADDR + 3] = vparm->u.mac[2];
+               sprom[SPROM_IL0MACADDR + 2] = vparm->u.mac[3];
+               sprom[SPROM_IL0MACADDR + 5] = vparm->u.mac[4];
+               sprom[SPROM_IL0MACADDR + 4] = vparm->u.mac[5];
+               break;
+       case VALUE_ETMAC:
+               sprom[SPROM_ET0MACADDR + 1] = vparm->u.mac[0];
+               sprom[SPROM_ET0MACADDR + 0] = vparm->u.mac[1];
+               sprom[SPROM_ET0MACADDR + 3] = vparm->u.mac[2];
+               sprom[SPROM_ET0MACADDR + 2] = vparm->u.mac[3];
+               sprom[SPROM_ET0MACADDR + 5] = vparm->u.mac[4];
+               sprom[SPROM_ET0MACADDR + 4] = vparm->u.mac[5];
+               break;
+       case VALUE_AMAC:
+               sprom[SPROM_ET1MACADDR + 1] = vparm->u.mac[0];
+               sprom[SPROM_ET1MACADDR + 0] = vparm->u.mac[1];
+               sprom[SPROM_ET1MACADDR + 3] = vparm->u.mac[2];
+               sprom[SPROM_ET1MACADDR + 2] = vparm->u.mac[3];
+               sprom[SPROM_ET1MACADDR + 5] = vparm->u.mac[4];
+               sprom[SPROM_ET1MACADDR + 4] = vparm->u.mac[5];
+               break;
+       case VALUE_ET0PHY:
+               tmp |= sprom[SPROM_ETHPHY + 0];
+               tmp |= sprom[SPROM_ETHPHY + 1] << 8;
+               tmp = ((tmp & 0x001F) | (v & 0x1F));
+               sprom[SPROM_ETHPHY + 0] = (tmp & 0x00FF);
+               sprom[SPROM_ETHPHY + 1] = (tmp & 0xFF00) >> 8;
+               break;
+       case VALUE_ET1PHY:
+               tmp |= sprom[SPROM_ETHPHY + 0];
+               tmp |= sprom[SPROM_ETHPHY + 1] << 8;
+               tmp = ((tmp & 0x03E0) | ((v & 0x1F) << 5));
+               sprom[SPROM_ETHPHY + 0] = (tmp & 0x00FF);
+               sprom[SPROM_ETHPHY + 1] = (tmp & 0xFF00) >> 8;
+               break;
+       case VALUE_ET0MDC:
+               sprom[SPROM_ETHPHY + 1] &= ~(1 << 6);
+               if (v)
+                       sprom[SPROM_ETHPHY + 1] |= (1 << 6);
+               break;
+       case VALUE_ET1MDC:
+               sprom[SPROM_ETHPHY + 1] &= ~(1 << 7);
+               if (v)
+                       sprom[SPROM_ETHPHY + 1] |= (1 << 7);
+               break;
+       case VALUE_BREV:
+               sprom[SPROM_BOARDREV + 0] = v;
+               break;
+       case VALUE_LOC:
+               tmp = (sprom[SPROM_BOARDREV + 1] & 0xF0);
+               tmp |= (v & 0x0F);
+               sprom[SPROM_BOARDREV + 1] = (tmp & 0xFF);
+               break;
+       case VALUE_ANTA0:
+               sprom[SPROM_BOARDREV + 1] &= ~(1 << 4);
+               if (v)
+                       sprom[SPROM_BOARDREV + 1] |= (1 << 4);
+               break;
+       case VALUE_ANTA1:
+               sprom[SPROM_BOARDREV + 1] &= ~(1 << 5);
+               if (v)
+                       sprom[SPROM_BOARDREV + 1] |= (1 << 5);
+               break;
+       case VALUE_ANTBG0:
+               sprom[SPROM_BOARDREV + 1] &= ~(1 << 6);
+               if (v)
+                       sprom[SPROM_BOARDREV + 1] |= (1 << 6);
+               break;
+       case VALUE_ANTBG1:
+               sprom[SPROM_BOARDREV + 1] &= ~(1 << 7);
+               if (v)
+                       sprom[SPROM_BOARDREV + 1] |= (1 << 7);
+               break;
+       case VALUE_ANTGA:
+               sprom[SPROM_ANTENNA_GAIN + 0] = (v & 0xFF);
+               break;
+       case VALUE_ANTGBG:
+               sprom[SPROM_ANTENNA_GAIN + 1] = (v & 0xFF);
+               break;
+       case VALUE_PA0B0:
+               sprom[SPROM_PA0B0 + 0] = (v & 0x00FF);
+               sprom[SPROM_PA0B0 + 1] = (v & 0xFF00) >> 8;
+               break;
+       case VALUE_PA0B1:
+               sprom[SPROM_PA0B1 + 0] = (v & 0x00FF);
+               sprom[SPROM_PA0B1 + 1] = (v & 0xFF00) >> 8;
+               break;
+       case VALUE_PA0B2:
+               sprom[SPROM_PA0B2 + 0] = (v & 0x00FF);
+               sprom[SPROM_PA0B2 + 1] = (v & 0xFF00) >> 8;
+               break;
+       case VALUE_PA1B0:
+               sprom[SPROM_PA1B0 + 0] = (v & 0x00FF);
+               sprom[SPROM_PA1B0 + 1] = (v & 0xFF00) >> 8;
+               break;
+       case VALUE_PA1B1:
+               sprom[SPROM_PA1B1 + 0] = (v & 0x00FF);
+               sprom[SPROM_PA1B1 + 1] = (v & 0xFF00) >> 8;
+               break;
+       case VALUE_PA1B2:
+               sprom[SPROM_PA1B2 + 0] = (v & 0x00FF);
+               sprom[SPROM_PA1B2 + 1] = (v & 0xFF00) >> 8;
+               break;
+       case VALUE_WL0GPIO0:
+               sprom[SPROM_WL0GPIO0 + 0] = (v & 0xFF);
+               break;
+       case VALUE_WL0GPIO1:
+               sprom[SPROM_WL0GPIO0 + 1] = (v & 0xFF);
+               break;
+       case VALUE_WL0GPIO2:
+               sprom[SPROM_WL0GPIO2 + 0] = (v & 0xFF);
+               break;
+       case VALUE_WL0GPIO3:
+               sprom[SPROM_WL0GPIO2 + 1] = (v & 0xFF);
+               break;
+       case VALUE_MAXPA:
+               sprom[SPROM_MAXPWR + 0] = (v & 0xFF);
+               break;
+       case VALUE_MAXPBG:
+               sprom[SPROM_MAXPWR + 1] = (v & 0xFF);
+               break;
+       case VALUE_ITSSIA:
+               sprom[SPROM_IDL_TSSI_TGT + 0] = (v & 0xFF);
+               break;
+       case VALUE_ITSSIBG:
+               sprom[SPROM_IDL_TSSI_TGT + 1] = (v & 0xFF);
+               break;
+       case VALUE_SVER:
+               sprom[SPROM_VERSION + 0] = (v & 0xFF);
+               break;
+       default:
+               prerror("vparm->type internal error (0)\n");
+               exit(1);
+       }
+
+       return 0;
+}
+
+static int modify_sprom(uint8_t *sprom)
+{
+       struct cmdline_vparm *vparm;
+       int i;
+       int modified = 0;
+       uint8_t crc;
+
+       for (i = 0; i < cmdargs.nr_vparm; i++) {
+               vparm = &(cmdargs.vparm[i]);
+               if (!vparm->set)
+                       continue;
+               modify_value(sprom, vparm);
+               modified = 1;
+       }
+       if (modified) {
+               /* Recalculate the CRC. */
+               crc = sprom_crc(sprom);
+               sprom[SPROM_VERSION + 1] = crc;
+       }
+
+       return modified;
+}
+
+static void display_value(const uint8_t *sprom,
+                         struct cmdline_vparm *vparm)
+{
+       const char *desc;
+       uint8_t offset;
+       uint16_t value;
+       uint16_t tmp;
+
+       switch (vparm->type) {
+       case VALUE_RAW:
+               desc = "RAW";
+               offset = vparm->u.raw.offset;
+               value = sprom[offset];
+               break;
+       case VALUE_SUBP:
+               desc = "Subsytem product ID";
+               offset = SPROM_SUBP;
+               value = sprom[SPROM_SUBP + 0];
+               value |= sprom[SPROM_SUBP + 1] << 8;
+               break;
+       case VALUE_SUBV:
+               desc = "Subsystem vendor ID";
+               offset = SPROM_SUBV;
+               value = sprom[SPROM_SUBV + 0];
+               value |= sprom[SPROM_SUBV + 1] << 8;
+               break;
+       case VALUE_PPID:
+               desc = "PCI Product ID";
+               offset = SPROM_PPID;
+               value = sprom[SPROM_PPID + 0];
+               value |= sprom[SPROM_PPID + 1] << 8;
+               break;
+       case VALUE_BFLHI:
+               desc = "High 16 bits of Boardflags";
+               offset = SPROM_BFLHI;
+               value = sprom[SPROM_BFLHI + 0];
+               value |= sprom[SPROM_BFLHI + 1] << 8;
+               break;
+       case VALUE_BFL:
+               desc = "Low 16 bits of Boardflags";
+               offset = SPROM_BOARDFLAGS;
+               value = sprom[SPROM_BOARDFLAGS + 0];
+               value |= sprom[SPROM_BOARDFLAGS + 1] << 8;
+               break;
+       case VALUE_BGMAC:
+               desc = "MAC address for 802.11b/g";
+               offset = SPROM_IL0MACADDR;
+               value = 0;
+               break;
+       case VALUE_ETMAC:
+               desc = "MAC address for ethernet";
+               offset = SPROM_ET0MACADDR;
+               value = 0;
+               break;
+       case VALUE_AMAC:
+               desc = "MAC address for 802.11a";
+               offset = SPROM_ET1MACADDR;
+               value = 0;
+               break;
+       case VALUE_ET0PHY:
+               desc = "Ethernet phy settings (0)";
+               offset = SPROM_ETHPHY;
+               tmp = sprom[SPROM_ETHPHY + 0];
+               tmp |= sprom[SPROM_ETHPHY + 1] << 8;
+               value = (tmp & 0x001F);
+               break;
+       case VALUE_ET1PHY:
+               desc = "Ethernet phy settings (1)";
+               offset = SPROM_ETHPHY;
+               tmp = sprom[SPROM_ETHPHY + 0];
+               tmp |= sprom[SPROM_ETHPHY + 1] << 8;
+               value = (tmp & 0x03E0) >> 5;
+               break;
+       case VALUE_ET0MDC:
+               desc = "et0mdcport";
+               offset = SPROM_ETHPHY + 1;
+               value = 0;
+               if (sprom[SPROM_ETHPHY + 1] & (1 << 6))
+                       value = 1;
+               break;
+       case VALUE_ET1MDC:
+               desc = "et1mdcport";
+               offset = SPROM_ETHPHY + 1;
+               value = 0;
+               if (sprom[SPROM_ETHPHY + 1] & (1 << 7))
+                       value = 1;
+               break;
+       case VALUE_BREV:
+               desc = "Board revision";
+               offset = SPROM_BOARDREV;
+               value = sprom[SPROM_BOARDREV + 0];
+               break;
+       case VALUE_LOC:
+               desc = "Locale / Country Code";
+               offset = SPROM_BOARDREV + 1;
+               value = (sprom[SPROM_BOARDREV + 1] & 0x0F);
+               break;
+       case VALUE_ANTA0:
+               desc = "A PHY antenna 0 available";
+               offset = SPROM_BOARDREV + 1;
+               value = 0;
+               if (sprom[SPROM_BOARDREV + 1] & (1 << 4))
+                       value = 1;
+               break;
+       case VALUE_ANTA1:
+               desc = "A PHY antenna 1 available";
+               offset = SPROM_BOARDREV + 1;
+               value = 0;
+               if (sprom[SPROM_BOARDREV + 1] & (1 << 5))
+                       value = 1;
+               break;
+       case VALUE_ANTBG0:
+               desc = "B/G PHY antenna 0 available";
+               offset = SPROM_BOARDREV + 1;
+               value = 0;
+               if (sprom[SPROM_BOARDREV + 1] & (1 << 6))
+                       value = 1;
+               break;
+       case VALUE_ANTBG1:
+               desc = "B/G PHY antenna 1 available";
+               offset = SPROM_BOARDREV + 1;
+               value = 0;
+               if (sprom[SPROM_BOARDREV + 1] & (1 << 7))
+                       value = 1;
+               break;
+       case VALUE_ANTGA:
+               desc = "A PHY antenna gain";
+               offset = SPROM_ANTENNA_GAIN;
+               value = sprom[SPROM_ANTENNA_GAIN];
+               break;
+       case VALUE_ANTGBG:
+               desc = "B/G PHY antenna gain";
+               offset = SPROM_ANTENNA_GAIN + 1;
+               value = sprom[SPROM_ANTENNA_GAIN + 1];
+               break;
+       case VALUE_PA0B0:
+               desc = "pa0b0";
+               offset = SPROM_PA0B0;
+               value = sprom[offset + 0];
+               value |= sprom[offset + 1] << 8;
+               break;
+       case VALUE_PA0B1:
+               desc = "pa0b1";
+               offset = SPROM_PA0B1;
+               value = sprom[offset + 0];
+               value |= sprom[offset + 1] << 8;
+               break;
+       case VALUE_PA0B2:
+               desc = "pa0b2";
+               offset = SPROM_PA0B2;
+               value = sprom[offset + 0];
+               value |= sprom[offset + 1] << 8;
+               break;
+       case VALUE_PA1B0:
+               desc = "pa1b0";
+               offset = SPROM_PA1B0;
+               value = sprom[offset + 0];
+               value |= sprom[offset + 1] << 8;
+               break;
+       case VALUE_PA1B1:
+               desc = "pa1b1";
+               offset = SPROM_PA1B1;
+               value = sprom[offset + 0];
+               value |= sprom[offset + 1] << 8;
+               break;
+       case VALUE_PA1B2:
+               desc = "pa1b2";
+               offset = SPROM_PA1B2;
+               value = sprom[offset + 0];
+               value |= sprom[offset + 1] << 8;
+               break;
+       case VALUE_WL0GPIO0:
+               desc = "LED 0 behaviour";
+               offset = SPROM_WL0GPIO0 + 0;
+               value = sprom[offset];
+               break;
+       case VALUE_WL0GPIO1:
+               desc = "LED 1 behaviour";
+               offset = SPROM_WL0GPIO0 + 1;
+               value = sprom[offset];
+               break;
+       case VALUE_WL0GPIO2:
+               desc = "LED 2 behaviour";
+               offset = SPROM_WL0GPIO2 + 0;
+               value = sprom[offset];
+               break;
+       case VALUE_WL0GPIO3:
+               desc = "LED 3 behaviour";
+               offset = SPROM_WL0GPIO2 + 1;
+               value = sprom[offset];
+               break;
+       case VALUE_MAXPA:
+               desc = "A PHY max powerout";
+               offset = SPROM_MAXPWR + 0;
+               value = sprom[offset];
+               break;
+       case VALUE_MAXPBG:
+               desc = "B/G PHY max powerout";
+               offset = SPROM_MAXPWR + 1;
+               value = sprom[offset];
+               break;
+       case VALUE_ITSSIA:
+               desc = "A PHY idle TSSI target";
+               offset = SPROM_IDL_TSSI_TGT + 0;
+               value = sprom[offset];
+               break;
+       case VALUE_ITSSIBG:
+               desc = "B/G PHY idle TSSI target";
+               offset = SPROM_IDL_TSSI_TGT + 1;
+               value = sprom[offset];
+               break;
+       case VALUE_SVER:
+               desc = "SPROM version";
+               offset = SPROM_VERSION;
+               value = sprom[offset];
+               break;
+       default:
+               prerror("vparm->type internal error (1)\n");
+               exit(1);
+       }
+
+       switch (vparm->bits) {
+       case 1:
+               prdata("SPROM(0x%02X, %s) = %s\n",
+                      offset, desc, value ? "ON" : "OFF");
+               break;
+       case 4:
+               prdata("SPROM(0x%02X, %s) = 0x%01X\n",
+                      offset, desc, (value & 0xF));
+               break;
+       case 8:
+               prdata("SPROM(0x%02X, %s) = 0x%02X\n",
+                      offset, desc, (value & 0xFF));
+               break;
+       case 16:
+               prdata("SPROM(0x%02X, %s) = 0x%04X\n",
+                      offset, desc, (value & 0xFFFF));
+               break;
+       case -1: {
+               /* MAC address. */
+               const uint8_t *p = &(sprom[offset]);
+
+               prdata("SPROM(0x%02X, %s) = %02x:%02x:%02x:%02x:%02x:%02x\n",
+                      offset, desc,
+                      p[1], p[0], p[3], p[2], p[5], p[4]);
+               break;
+       }
+       default:
+               prerror("vparm->bits internal error (%d)\n",
+                       vparm->bits);
+               exit(1);
+       }
+}
+
+static int display_sprom(const uint8_t *sprom)
+{
+       struct cmdline_vparm *vparm;
+       int i;
+
+       for (i = 0; i < cmdargs.nr_vparm; i++) {
+               vparm = &(cmdargs.vparm[i]);
+               if (vparm->set)
+                       continue;
+               display_value(sprom, vparm);
+       }
+
+       return 0;
+}
+
+static int validate_input(const uint8_t *sprom)
+{
+       uint8_t crc, expected_crc;
+
+       crc = sprom_crc(sprom);
+       expected_crc = sprom[SPROM_VERSION + 1];
+       if (crc != expected_crc) {
+               prerror("Corrupt input data (crc: 0x%02X, expected: 0x%02X)\n",
+                       crc, expected_crc);
+               if (!cmdargs.force)
+                       return 1;
+       }
+
+       return 0;
+}
+
+static int parse_input(uint8_t *sprom, char *buffer, size_t bsize)
+{
+       char *input;
+       size_t inlen;
+       size_t cnt;
+       unsigned long parsed;
+       char tmp[SPROM_SIZE * 2 + 10] = { 0 };
+
+       if (cmdargs.bin_mode) {
+               /* The input buffer already contains
+                * the binary sprom data.
+                */
+               internal_error_on(bsize != SPROM_SIZE);
+               memcpy(sprom, buffer, SPROM_SIZE);
+               return 0;
+       }
+
+       inlen = bsize;
+       input = strchr(buffer, ':');
+       if (input) {
+               input++;
+               inlen -= input - buffer;
+       } else
+               input = buffer;
+
+       if (inlen < SPROM_SIZE * 2) {
+               prerror("Input data too short\n");
+               return -1;
+       }
+       for (cnt = 0; cnt < SPROM_SIZE; cnt++) {
+               memcpy(tmp, input + cnt * 2, 2);
+               parsed = strtoul(tmp, NULL, 16);
+               sprom[cnt] = parsed & 0xFF;
+       }
+
+       if (cmdargs.verbose) {
+               hexdump_sprom(sprom, tmp, sizeof(tmp));
+               prinfo("Raw input:  %s\n", tmp);
+       }
+
+       return 0;
+}
+
+static int read_infile(int fd, char **buffer, size_t *bsize)
+{
+       struct stat s;
+       int err;
+       ssize_t r;
+
+       err = fstat(fd, &s);
+       if (err) {
+               prerror("Could not stat input file.\n");
+               return err;
+       }
+       if (s.st_size == 0) {
+               prerror("No input data\n");
+               return -1;
+       }
+       if (cmdargs.bin_mode) {
+               if (s.st_size != SPROM_SIZE) {
+                       prerror("The input data is no SPROM Binary data. "
+                               "The size must be exactly %d bytes, "
+                               "but it is %u bytes\n",
+                               SPROM_SIZE, (unsigned int)(s.st_size));
+                       return -1;
+               }
+       } else {
+               if (s.st_size > 1024 * 1024) {
+                       prerror("The input data does not look "
+                               "like SPROM HEX data (too long).\n");
+                       return -1;
+               }
+       }
+
+       *bsize = s.st_size;
+       if (!cmdargs.bin_mode)
+               (*bsize)++;
+       *buffer = malloce(*bsize);
+       r = read(fd, *buffer, s.st_size);
+       if (r != s.st_size) {
+               prerror("Could not read input data.\n");
+               return -1;
+       }
+       if (!cmdargs.bin_mode)
+               (*buffer)[r] = '\0';
+
+       return 0;
+}
+
+static void close_infile(int fd)
+{
+       if (cmdargs.infile)
+               close(fd);
+}
+
+static void close_outfile(int fd)
+{
+       if (cmdargs.outfile)
+               close(fd);
+}
+
+static int open_infile(int *fd)
+{
+       *fd = STDIN_FILENO;
+       if (!cmdargs.infile)
+               return 0;
+       *fd = open(cmdargs.infile, O_RDONLY);
+       if (*fd < 0) {
+               prerror("Could not open --infile %s\n",
+                       cmdargs.infile);
+               return -1;
+       }
+
+       return 0;
+}
+
+static int open_outfile(int *fd)
+{
+       *fd = STDOUT_FILENO;
+       if (!cmdargs.outfile)
+               return 0;
+       *fd = open(cmdargs.outfile, O_RDWR | O_CREAT, 0644);
+       if (*fd < 0) {
+               prerror("Could not open --outfile %s\n",
+                       cmdargs.outfile);
+               return -1;
+       }
+
+       return 0;
+}
+
+static void print_banner(int forceprint)
+{
+       const char *str = "Broadcom-SSB SPROM data modification tool version " VERSION "\n";
+       if (forceprint)
+               prdata(str);
+       else
+               prinfo(str);
+}
+
+static void print_usage(int argc, char *argv[])
+{
+       print_banner(1);
+       prdata("\nUsage: %s [OPTION]\n", argv[0]);
+       prdata("  -i|--input FILE       Input file\n");
+       prdata("  -o|--output FILE      Output file\n");
+       prdata("  -b|--binmode          The Input data is plain binary data and Output will be binary\n");
+       prdata("  -V|--verbose          Be verbose\n");
+       prdata("  -f|--force            Override error checks\n");
+       prdata("  -v|--version          Print version\n");
+       prdata("  -h|--help             Print this help\n");
+       prdata("\n");
+       prdata("Value Parameters:\n");
+       prdata("\n");
+       prdata("  -s|--rawset OFF,VAL   Set a VALue at a byte-OFFset\n");
+       prdata("  -g|--rawget OFF       Get a value at a byte-OFFset\n");
+       prdata("\n");
+       prdata("Predefined values (for displaying (GET) or modification):\n");
+       prdata("  --subp [0xFFFF]       Subsytem product ID for PCI\n");
+       prdata("  --subv [0xFFFF]       Subsystem vendor ID for PCI\n");
+       prdata("  --ppid [0xFFFF]       Product ID for PCI\n");
+       prdata("  --bflhi [0xFFFF]      High 16 bits of boardflags (only if spromversion > 1)\n");
+       prdata("  --bfl [0xFFFF]        Low 16 bits of boardflags\n");
+       prdata("  --bgmac [MAC-ADDR]    MAC address for 802.11b/g\n");
+       prdata("  --etmac [MAC-ADDR]    MAC address for ethernet, see b44 driver\n");
+       prdata("  --amac [MAC-ADDR]     Mac address for 802.11a\n");
+       prdata("  --et0phy [0xFF]\n");
+       prdata("  --et1phy [0xFF]\n");
+       prdata("  --et0mdc [BOOL]\n");
+       prdata("  --et1mdc [BOOL]\n");
+       prdata("  --brev [0xFF]         Board revision\n");
+       prdata("  --loc [0xF]           Country code\n");
+       prdata("  --anta0 [BOOL]        Antenna 0 available for A PHY\n");
+       prdata("  --anta1 [BOOL]        Antenna 1 available for A PHY\n");
+       prdata("  --antbg0 [BOOL]       Antenna 0 available for B/G PHY\n");
+       prdata("  --antbg1 [BOOL]       Antenna 1 available for B/G PHY\n");
+       prdata("  --antga [0xFF]        Antenna gain for A PHY\n");
+       prdata("  --antgbg [0xFF]       Antenna gain for B/G PHY\n");
+       prdata("  --pa0b0 [0xFFFF]\n");
+       prdata("  --pa0b1 [0xFFFF]\n");
+       prdata("  --pa0b2 [0xFFFF]\n");
+       prdata("  --pa1b0 [0xFFFF]\n");
+       prdata("  --pa1b1 [0xFFFF]\n");
+       prdata("  --pa1b2 [0xFFFF]\n");
+       prdata("  --wl0gpio0 [0xFF]     LED 0 behaviour\n");
+       prdata("  --wl0gpio1 [0xFF]     LED 1 behaviour\n");
+       prdata("  --wl0gpio2 [0xFF]     LED 2 behaviour\n");
+       prdata("  --wl0gpio3 [0xFF]     LED 3 behaviour\n");
+       prdata("  --maxpa [0xFF]        A PHY max power\n");
+       prdata("  --maxpbg [0xFF]       B/G PHY max power\n");
+       prdata("  --itssia [0xFF]       Idle tssi target for A PHY\n");
+       prdata("  --itssibg [0xFF]      Idle tssi target for B/G PHY\n");
+       prdata("  --sver [0xFF]         SPROM-version\n");
+       prdata("\n");
+       prdata("  -P|--print-all        Display all values\n");
+       prdata("\n");
+       prdata(" BOOL      is a boolean value. Either 0 or 1\n");
+       prdata(" 0xF..     is a hexadecimal value\n");
+       prdata(" MAC-ADDR  is a MAC address in the format 00:00:00:00:00:00\n");
+       prdata(" If the value parameter is \"GET\", the value will be printed;\n");
+       prdata(" otherwise it is modified.\n");
+}
+
+#define ARG_MATCH              0
+#define ARG_NOMATCH            1
+#define ARG_ERROR              -1
+
+static int do_cmp_arg(char **argv, int *pos,
+                     const char *template,
+                     int allow_merged,
+                     char **param)
+{
+       char *arg;
+       char *next_arg;
+       size_t arg_len, template_len;
+
+       arg = argv[*pos];
+       next_arg = argv[*pos + 1];
+       arg_len = strlen(arg);
+       template_len = strlen(template);
+
+       if (param) {
+               /* Maybe we have a merged parameter here.
+                * A merged parameter is "-pfoobar" for example.
+                */
+               if (allow_merged && arg_len > template_len) {
+                       if (memcmp(arg, template, template_len) == 0) {
+                               *param = arg + template_len;
+                               return ARG_MATCH;
+                       }
+                       return ARG_NOMATCH;
+               } else if (arg_len != template_len)
+                       return ARG_NOMATCH;
+               *param = next_arg;
+       }
+       if (strcmp(arg, template) == 0) {
+               if (param) {
+                       /* Skip the parameter on the next iteration. */
+                       (*pos)++;
+                       if (*param == 0) {
+                               prerror("%s needs a parameter\n", arg);
+                               return ARG_ERROR;
+                       }
+               }
+               return ARG_MATCH;
+       }
+
+       return ARG_NOMATCH;
+}
+
+/* Simple and lean command line argument parsing. */
+static int cmp_arg(char **argv, int *pos,
+                  const char *long_template,
+                  const char *short_template,
+                  char **param)
+{
+       int err;
+
+       if (long_template) {
+               err = do_cmp_arg(argv, pos, long_template, 0, param);
+               if (err == ARG_MATCH || err == ARG_ERROR)
+                       return err;
+       }
+       err = ARG_NOMATCH;
+       if (short_template)
+               err = do_cmp_arg(argv, pos, short_template, 1, param);
+       return err;
+}
+
+static int parse_err;
+
+static int arg_match(char **argv, int *i,
+                    const char *long_template,
+                    const char *short_template,
+                    char **param)
+{
+       int res;
+
+       res = cmp_arg(argv, i, long_template,
+                     short_template, param);
+       if (res == ARG_ERROR) {
+               parse_err = 1;
+               return 0;
+       }
+       return (res == ARG_MATCH);
+}
+
+static int parse_value(const char *str,
+                      struct cmdline_vparm *vparm,
+                      const char *param)
+{
+       unsigned long v;
+       int i;
+
+       vparm->bits = value_length_map[vparm->type];
+       vparm->set = 1;
+       if (strcmp(str, "GET") == 0 || strcmp(str, "get") == 0) {
+               vparm->set = 0;
+               return 0;
+       }
+       if (vparm->bits == 1) {
+               /* This is a boolean value. */
+               if (strcmp(str, "0") == 0)
+                       vparm->u.value = 0;
+               else if (strcmp(str, "1") == 0)
+                       vparm->u.value = 1;
+               else
+                       goto error_bool;
+               return 1;
+       }
+
+       if (strncmp(str, "0x", 2) != 0)
+               goto error;
+       str += 2;
+       for (i = 0; i < vparm->bits / 4; i++) {
+               if (str[i] == '\0')
+                       goto error;
+       }
+       if (str[i] != '\0')
+               goto error;
+       errno = 0;
+       v = strtoul(str, NULL, 16);
+       if (errno)
+               goto error;
+       vparm->u.value = v;
+
+       return 1;
+error:
+       if (param) {
+               prerror("%s value parsing error. Format: 0x", param);
+               for (i = 0; i < vparm->bits / 4; i++)
+                       prerror("F");
+               prerror("\n");
+       }
+       return -1;
+
+error_bool:
+       if (param)
+               prerror("%s value parsing error. Format: 0 or 1 (boolean)\n", param);
+       return -1;
+}
+
+static int parse_mac(const char *str,
+                    struct cmdline_vparm *vparm,
+                    const char *param)
+{
+       int i;
+       char *delim;
+       const char *in = str;
+       uint8_t *out = vparm->u.mac;
+
+       vparm->bits = -1;
+       vparm->set = 1;
+       if (strcmp(str, "GET") == 0 || strcmp(str, "get") == 0) {
+               vparm->set = 0;
+               return 0;
+       }
+
+       for (i = 0; ; i++) {
+               errno = 0;
+               out[i] = strtoul(in, NULL, 16);
+               if (errno)
+                       goto error;
+               if (i == 5) {
+                       if (in[1] != '\0' && in[2] != '\0')
+                               goto error;
+                       break;
+               }
+               delim = strchr(in, ':');
+               if (!delim)
+                       goto error;
+               in = delim + 1;
+       }
+
+       return 1;
+error:
+       prerror("%s MAC parsing error. Format: 00:00:00:00:00:00\n", param);
+       return -1;
+}
+
+static int parse_rawset(const char *str,
+                       struct cmdline_vparm *vparm)
+{
+       char *delim;
+       uint8_t value;
+       uint8_t offset;
+       int err;
+
+       vparm->type = VALUE_RAW;
+
+       delim = strchr(str, ',');
+       if (!delim)
+               goto error;
+       *delim = '\0';
+       err = parse_value(str, vparm, NULL);
+       if (err != 1)
+               goto error;
+       offset = vparm->u.value;
+       if (offset >= SPROM_SIZE) {
+               prerror("--rawset offset too big (>= 0x%02X)\n",
+                       SPROM_SIZE);
+               return -1;
+       }
+       err = parse_value(delim + 1, vparm, NULL);
+       if (err != 1)
+               goto error;
+       value = vparm->u.value;
+
+       vparm->u.raw.value = value;
+       vparm->u.raw.offset = offset;
+       vparm->set = 1;
+
+       return 0;
+error:
+       prerror("--rawset value parsing error. Format: 0xFF,0xFF "
+               "(first Offset, second Value)\n");
+       return -1;
+}
+
+static int parse_rawget(const char *str,
+                       struct cmdline_vparm *vparm)
+{
+       int err;
+       uint8_t offset;
+
+       vparm->type = VALUE_RAW;
+
+       err = parse_value(str, vparm, "--rawget");
+       if (err != 1)
+               return -1;
+       offset = vparm->u.value;
+       if (offset >= SPROM_SIZE) {
+               prerror("--rawget offset too big (>= 0x%02X)\n",
+                       SPROM_SIZE);
+               return -1;
+       }
+
+       vparm->u.raw.offset = offset;
+       vparm->type = VALUE_RAW;
+       vparm->set = 0;
+
+       return 0;
+}
+
+static int generate_printall(void)
+{
+       struct cmdline_vparm *vparm;
+       int count, i;
+       enum valuetype vt = VALUE_FIRST;
+
+       count = VALUE_LAST - VALUE_FIRST + 1;
+       for (i = 0; i < count; i++, vt++) {
+               if (cmdargs.nr_vparm == MAX_VPARM) {
+                       prerror("Too many value parameters.\n");
+                       return -1;
+               }
+
+               vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]);
+               vparm->type = vt;
+               vparm->set = 0;
+               vparm->bits = value_length_map[vt];
+       }
+
+       return 0;
+}
+
+static int parse_args(int argc, char *argv[])
+{
+       struct cmdline_vparm *vparm;
+       int i, err;
+       char *param;
+
+       parse_err = 0;
+       for (i = 1; i < argc; i++) {
+               if (cmdargs.nr_vparm == MAX_VPARM) {
+                       prerror("Too many value parameters.\n");
+                       return -1;
+               }
+
+               if (arg_match(argv, &i, "--version", "-v", 0)) {
+                       print_banner(1);
+                       return 1;
+               } else if (arg_match(argv, &i, "--help", "-h", 0)) {
+                       goto out_usage;
+               } else if (arg_match(argv, &i, "--input", "-i", &param)) {
+                       cmdargs.infile = param;
+               } else if (arg_match(argv, &i, "--output", "-o", &param)) {
+                       cmdargs.outfile = param;
+               } else if (arg_match(argv, &i, "--verbose", "-V", 0)) {
+                       cmdargs.verbose = 1;
+               } else if (arg_match(argv, &i, "--force", "-n", 0)) {
+                       cmdargs.force = 1;
+               } else if (arg_match(argv, &i, "--binmode", "-b", 0)) {
+                       cmdargs.bin_mode = 1;
+
+
+               } else if (arg_match(argv, &i, "--rawset", "-s", &param)) {
+                       vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]);
+                       err = parse_rawset(param, vparm);
+                       if (err < 0)
+                               goto error;
+               } else if (arg_match(argv, &i, "--rawget", "-g", &param)) {
+                       vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]);
+                       err = parse_rawget(param, vparm);
+                       if (err < 0)
+                               goto error;
+
+
+               } else if (arg_match(argv, &i, "--subp", 0, &param)) {
+                       vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]);
+                       vparm->type = VALUE_SUBP;
+                       err = parse_value(param, vparm, "--subp");
+                       if (err < 0)
+                               goto error;
+               } else if (arg_match(argv, &i, "--subv", 0, &param)) {
+                       vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]);
+                       vparm->type = VALUE_SUBV;
+                       err = parse_value(param, vparm, "--subv");
+                       if (err < 0)
+                               goto error;
+               } else if (arg_match(argv, &i, "--ppid", 0, &param)) {
+                       vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]);
+                       vparm->type = VALUE_PPID;
+                       err = parse_value(param, vparm, "--ppid");
+                       if (err < 0)
+                               goto error;
+               } else if (arg_match(argv, &i, "--bflhi", 0, &param)) {
+                       vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]);
+                       vparm->type = VALUE_BFLHI;
+                       err = parse_value(param, vparm, "--bflhi");
+                       if (err < 0)
+                               goto error;
+               } else if (arg_match(argv, &i, "--bfl", 0, &param)) {
+                       vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]);
+                       vparm->type = VALUE_BFL;
+                       err = parse_value(param, vparm, "--bfl");
+                       if (err < 0)
+                               goto error;
+               } else if (arg_match(argv, &i, "--bgmac", 0, &param)) {
+                       vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]);
+                       vparm->type = VALUE_BGMAC;
+                       err = parse_mac(param, vparm, "--bgmac");
+                       if (err < 0)
+                               goto error;
+               } else if (arg_match(argv, &i, "--etmac", 0, &param)) {
+                       vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]);
+                       vparm->type = VALUE_ETMAC;
+                       err = parse_mac(param, vparm, "--etmac");
+                       if (err < 0)
+                               goto error;
+               } else if (arg_match(argv, &i, "--amac", 0, &param)) {
+                       vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]);
+                       vparm->type = VALUE_AMAC;
+                       err = parse_mac(param, vparm, "--amac");
+                       if (err < 0)
+                               goto error;
+               } else if (arg_match(argv, &i, "--et0phy", 0, &param)) {
+                       vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]);
+                       vparm->type = VALUE_ET0PHY;
+                       err = parse_value(param, vparm, "--et0phy");
+                       if (err < 0)
+                               goto error;
+               } else if (arg_match(argv, &i, "--et1phy", 0, &param)) {
+                       vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]);
+                       vparm->type = VALUE_ET1PHY;
+                       err = parse_value(param, vparm, "--et1phy");
+                       if (err < 0)
+                               goto error;
+               } else if (arg_match(argv, &i, "--et0mdc", 0, &param)) {
+                       vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]);
+                       vparm->type = VALUE_ET0MDC;
+                       err = parse_value(param, vparm, "--et0mdc");
+                       if (err < 0)
+                               goto error;
+               } else if (arg_match(argv, &i, "--et1mdc", 0, &param)) {
+                       vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]);
+                       vparm->type = VALUE_ET1MDC;
+                       err = parse_value(param, vparm, "--et1mdc");
+                       if (err < 0)
+                               goto error;
+               } else if (arg_match(argv, &i, "--brev", 0, &param)) {
+                       vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]);
+                       vparm->type = VALUE_BREV;
+                       err = parse_value(param, vparm, "--brev");
+                       if (err < 0)
+                               goto error;
+               } else if (arg_match(argv, &i, "--loc", 0, &param)) {
+                       vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]);
+                       vparm->type = VALUE_LOC;
+                       err = parse_value(param, vparm, "--loc");
+                       if (err < 0)
+                               goto error;
+               } else if (arg_match(argv, &i, "--anta0", 0, &param)) {
+                       vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]);
+                       vparm->type = VALUE_ANTA0;
+                       err = parse_value(param, vparm, "--anta0");
+                       if (err < 0)
+                               goto error;
+               } else if (arg_match(argv, &i, "--anta1", 0, &param)) {
+                       vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]);
+                       vparm->type = VALUE_ANTA1;
+                       err = parse_value(param, vparm, "--anta1");
+                       if (err < 0)
+                               goto error;
+               } else if (arg_match(argv, &i, "--antbg0", 0, &param)) {
+                       vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]);
+                       vparm->type = VALUE_ANTBG0;
+                       err = parse_value(param, vparm, "--antbg0");
+                       if (err < 0)
+                               goto error;
+               } else if (arg_match(argv, &i, "--antbg1", 0, &param)) {
+                       vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]);
+                       vparm->type = VALUE_ANTBG1;
+                       err = parse_value(param, vparm, "--antbg1");
+                       if (err < 0)
+                               goto error;
+               } else if (arg_match(argv, &i, "--antga", 0, &param)) {
+                       vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]);
+                       vparm->type = VALUE_ANTGA;
+                       err = parse_value(param, vparm, "--antga");
+                       if (err < 0)
+                               goto error;
+               } else if (arg_match(argv, &i, "--antgbg", 0, &param)) {
+                       vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]);
+                       vparm->type = VALUE_ANTGBG;
+                       err = parse_value(param, vparm, "--antgbg");
+                       if (err < 0)
+                               goto error;
+               } else if (arg_match(argv, &i, "--pa0b0", 0, &param)) {
+                       vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]);
+                       vparm->type = VALUE_PA0B0;
+                       err = parse_value(param, vparm, "--pa0b0");
+                       if (err < 0)
+                               goto error;
+               } else if (arg_match(argv, &i, "--pa0b1", 0, &param)) {
+                       vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]);
+                       vparm->type = VALUE_PA0B1;
+                       err = parse_value(param, vparm, "--pa0b1");
+                       if (err < 0)
+                               goto error;
+               } else if (arg_match(argv, &i, "--pa0b2", 0, &param)) {
+                       vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]);
+                       vparm->type = VALUE_PA0B2;
+                       err = parse_value(param, vparm, "--pa0b2");
+                       if (err < 0)
+                               goto error;
+               } else if (arg_match(argv, &i, "--pa1b0", 0, &param)) {
+                       vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]);
+                       vparm->type = VALUE_PA1B0;
+                       err = parse_value(param, vparm, "--pa1b0");
+                       if (err < 0)
+                               goto error;
+               } else if (arg_match(argv, &i, "--pa1b1", 0, &param)) {
+                       vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]);
+                       vparm->type = VALUE_PA1B1;
+                       err = parse_value(param, vparm, "--pa1b1");
+                       if (err < 0)
+                               goto error;
+               } else if (arg_match(argv, &i, "--pa1b2", 0, &param)) {
+                       vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]);
+                       vparm->type = VALUE_PA1B2;
+                       err = parse_value(param, vparm, "--pa1b2");
+                       if (err < 0)
+                               goto error;
+               } else if (arg_match(argv, &i, "--wl0gpio0", 0, &param)) {
+                       vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]);
+                       vparm->type = VALUE_WL0GPIO0;
+                       err = parse_value(param, vparm, "--wl0gpio0");
+                       if (err < 0)
+                               goto error;
+               } else if (arg_match(argv, &i, "--wl0gpio1", 0, &param)) {
+                       vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]);
+                       vparm->type = VALUE_WL0GPIO1;
+                       err = parse_value(param, vparm, "--wl0gpio1");
+                       if (err < 0)
+                               goto error;
+               } else if (arg_match(argv, &i, "--wl0gpio2", 0, &param)) {
+                       vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]);
+                       vparm->type = VALUE_WL0GPIO2;
+                       err = parse_value(param, vparm, "--wl0gpio2");
+                       if (err < 0)
+                               goto error;
+               } else if (arg_match(argv, &i, "--wl0gpio3", 0, &param)) {
+                       vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]);
+                       vparm->type = VALUE_WL0GPIO3;
+                       err = parse_value(param, vparm, "--wl0gpio3");
+                       if (err < 0)
+                               goto error;
+               } else if (arg_match(argv, &i, "--maxpa", 0, &param)) {
+                       vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]);
+                       vparm->type = VALUE_MAXPA;
+                       err = parse_value(param, vparm, "--maxpa");
+                       if (err < 0)
+                               goto error;
+               } else if (arg_match(argv, &i, "--maxpbg", 0, &param)) {
+                       vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]);
+                       vparm->type = VALUE_MAXPBG;
+                       err = parse_value(param, vparm, "--maxpbg");
+                       if (err < 0)
+                               goto error;
+               } else if (arg_match(argv, &i, "--itssia", 0, &param)) {
+                       vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]);
+                       vparm->type = VALUE_ITSSIA;
+                       err = parse_value(param, vparm, "--itssia");
+                       if (err < 0)
+                               goto error;
+               } else if (arg_match(argv, &i, "--itssibg", 0, &param)) {
+                       vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]);
+                       vparm->type = VALUE_ITSSIBG;
+                       err = parse_value(param, vparm, "--itssibg");
+                       if (err < 0)
+                               goto error;
+               } else if (arg_match(argv, &i, "--sver", 0, &param)) {
+                       vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]);
+                       vparm->type = VALUE_SVER;
+                       err = parse_value(param, vparm, "--sver");
+                       if (err < 0)
+                               goto error;
+               } else if (arg_match(argv, &i, "--print-all", "-P", 0)) {
+                       err = generate_printall();
+                       if (err)
+                               goto error;
+               } else {
+                       prerror("Unrecognized argument: %s\n", argv[i]);
+                       goto out_usage;
+               }
+               if (parse_err)
+                       goto out_usage;
+       }
+       if (cmdargs.nr_vparm == 0) {
+               prerror("No Value parameter given. See --help.\n");
+               return -1;
+       }
+       return 0;
+
+out_usage:
+       print_usage(argc, argv);
+error:
+       return -1;      
+}
+
+
+int main(int argc, char **argv)
+{
+       int err;
+       int fd;
+       uint8_t sprom[SPROM_SIZE];
+       char *buffer = NULL;
+       size_t buffer_size = 0;
+
+       err = parse_args(argc, argv);
+       if (err == 1)
+               return 0;
+       else if (err != 0)
+               goto out;
+
+       print_banner(0);
+       prinfo("\nReading input from \"%s\"...\n",
+              cmdargs.infile ? cmdargs.infile : "stdin");
+
+       err = open_infile(&fd);
+       if (err)
+               goto out;
+       err = read_infile(fd, &buffer, &buffer_size);
+       close_infile(fd);
+       if (err)
+               goto out;
+       err = parse_input(sprom, buffer, buffer_size);
+       free(buffer);
+       if (err)
+               goto out;
+       err = validate_input(sprom);
+       if (err)
+               goto out;
+
+       err = display_sprom(sprom);
+       if (err)
+               goto out;
+       err = modify_sprom(sprom);
+       if (err < 0)
+               goto out;
+       if (err) {
+               err = open_outfile(&fd);
+               if (err)
+                       goto out;
+               err = write_output(fd, sprom);
+               close_outfile(fd);
+               if (err)
+                       goto out;
+               prinfo("SPROM modified.\n");
+       }
+out:
+       return err;
+}
diff --git a/ssb_sprom/ssb_sprom.h b/ssb_sprom/ssb_sprom.h
new file mode 100644 (file)
index 0000000..37c97c8
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+
+  Broadcom Sonics Silicon Backplane bus SPROM data modification tool
+
+  Copyright (c) 2006-2007 Michael Buesch <mb@bu3sch.de>
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; see the file COPYING.  If not, write to
+  the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
+  Boston, MA 02110-1301, USA.
+
+*/
+
+#ifndef SSB_SPROMTOOL_H_
+#define SSB_SPROMTOOL_H_
+
+#include "utils.h"
+
+#define VERSION                        ssb_stringify(VERSION_)
+
+#define SPROM_SIZE             128 /* bytes */
+
+/* byte offsets */
+#define SPROM_SUBP             (0x02 * 2)
+#define SPROM_SUBV             (0x03 * 2)
+#define SPROM_PPID             (0x04 * 2)
+#define SPROM_BFLHI            (0x1C * 2)
+#define SPROM_IL0MACADDR       (0x24 * 2)
+#define SPROM_ET0MACADDR       (0x27 * 2)
+#define SPROM_ET1MACADDR       (0x2a * 2)
+#define SPROM_ETHPHY           (0x2d * 2)
+#define SPROM_BOARDREV         (0x2e * 2)
+#define SPROM_PA0B0            (0x2f * 2)
+#define SPROM_PA0B1            (0x30 * 2)
+#define SPROM_PA0B2            (0x31 * 2)
+#define SPROM_WL0GPIO0         (0x32 * 2)
+#define SPROM_WL0GPIO2         (0x33 * 2)
+#define SPROM_MAXPWR           (0x34 * 2)
+#define SPROM_PA1B0            (0x35 * 2)
+#define SPROM_PA1B1            (0x36 * 2)
+#define SPROM_PA1B2            (0x37 * 2)
+#define SPROM_IDL_TSSI_TGT     (0x38 * 2)
+#define SPROM_BOARDFLAGS       (0x39 * 2)
+#define SPROM_ANTENNA_GAIN     (0x3a * 2)
+#define SPROM_VERSION          (0x3f * 2)
+
+enum valuetype {
+       VALUE_RAW,
+
+       VALUE_SUBP,
+ VALUE_FIRST = VALUE_SUBP,
+       VALUE_SUBV,
+       VALUE_PPID,
+       VALUE_BFLHI,
+       VALUE_BFL,
+       VALUE_BGMAC,
+       VALUE_ETMAC,
+       VALUE_AMAC,
+       VALUE_ET0PHY,
+       VALUE_ET1PHY,
+       VALUE_ET0MDC,
+       VALUE_ET1MDC,
+       VALUE_BREV,
+       VALUE_LOC,
+       VALUE_ANTA0,
+       VALUE_ANTA1,
+       VALUE_ANTBG0,
+       VALUE_ANTBG1,
+       VALUE_ANTGA,
+       VALUE_ANTGBG,
+       VALUE_PA0B0,
+       VALUE_PA0B1,
+       VALUE_PA0B2,
+       VALUE_PA1B0,
+       VALUE_PA1B1,
+       VALUE_PA1B2,
+       VALUE_WL0GPIO0,
+       VALUE_WL0GPIO1,
+       VALUE_WL0GPIO2,
+       VALUE_WL0GPIO3,
+       VALUE_MAXPA,
+       VALUE_MAXPBG,
+       VALUE_ITSSIA,
+       VALUE_ITSSIBG,
+       VALUE_SVER,
+ VALUE_LAST = VALUE_SVER,
+};
+
+struct cmdline_vparm {
+       enum valuetype type;
+       int set;
+       int bits;
+       union {
+               uint16_t value;
+               uint8_t mac[6];
+               struct {
+                       uint8_t value;
+                       uint8_t offset;
+               } raw;
+       } u;
+};
+
+struct cmdline_args {
+       const char *infile;
+       const char *outfile;
+       int verbose;
+       int force;
+       int bin_mode;
+
+#define MAX_VPARM      512
+       struct cmdline_vparm vparm[MAX_VPARM];
+       int nr_vparm;
+};
+extern struct cmdline_args cmdargs;
+
+#endif /* SSB_SPROMTOOL_H_ */
diff --git a/ssb_sprom/utils.c b/ssb_sprom/utils.c
new file mode 100644 (file)
index 0000000..5d3189e
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+
+  Broadcom Sonics Silicon Backplane bus SPROM data modification tool
+
+  Copyright (c) 2006-2007 Michael Buesch <mb@bu3sch.de>
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; see the file COPYING.  If not, write to
+  the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
+  Boston, MA 02110-1301, USA.
+
+*/
+
+#include "utils.h"
+#include "ssb_sprom.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+
+int prinfo(const char *fmt, ...)
+{
+       int ret;
+       va_list va;
+
+       if (!cmdargs.verbose)
+               return 0;
+
+       va_start(va, fmt);
+       ret = vfprintf(stderr, fmt, va);
+       va_end(va);
+
+       return ret;
+}
+
+int prerror(const char *fmt, ...)
+{
+       int ret;
+       va_list va;
+
+       va_start(va, fmt);
+       ret = vfprintf(stderr, fmt, va);
+       va_end(va);
+
+       return ret;
+}
+
+int prdata(const char *fmt, ...)
+{
+       int ret;
+       va_list va;
+
+       va_start(va, fmt);
+       ret = vfprintf(stderr, fmt, va);
+       va_end(va);
+
+       return ret;
+}
+
+void internal_error(const char *message)
+{
+       prerror("Internal programming error: %s\n", message);
+       exit(1);
+}
+
+static void oom(void)
+{
+       prerror("ERROR: Out of memory!\n"
+               "Virtual memory exhausted. "
+               "Please close some applications, "
+               "add more RAM or SWAP space.\n");
+       exit(1);
+}
+
+void * malloce(size_t size)
+{
+       void *ret;
+
+       ret = malloc(size);
+       if (!ret)
+               oom();
+
+       return ret;
+}
+
+void * realloce(void *ptr, size_t newsize)
+{
+       void *ret;
+
+       ret = realloc(ptr, newsize);
+       if (!ret)
+               oom();
+
+       return ret;
+}
+
+uint8_t crc8(uint8_t crc, uint8_t data)
+{
+       static const uint8_t t[] = {
+               0x00, 0xF7, 0xB9, 0x4E, 0x25, 0xD2, 0x9C, 0x6B,
+               0x4A, 0xBD, 0xF3, 0x04, 0x6F, 0x98, 0xD6, 0x21,
+               0x94, 0x63, 0x2D, 0xDA, 0xB1, 0x46, 0x08, 0xFF,
+               0xDE, 0x29, 0x67, 0x90, 0xFB, 0x0C, 0x42, 0xB5,
+               0x7F, 0x88, 0xC6, 0x31, 0x5A, 0xAD, 0xE3, 0x14,
+               0x35, 0xC2, 0x8C, 0x7B, 0x10, 0xE7, 0xA9, 0x5E,
+               0xEB, 0x1C, 0x52, 0xA5, 0xCE, 0x39, 0x77, 0x80,
+               0xA1, 0x56, 0x18, 0xEF, 0x84, 0x73, 0x3D, 0xCA,
+               0xFE, 0x09, 0x47, 0xB0, 0xDB, 0x2C, 0x62, 0x95,
+               0xB4, 0x43, 0x0D, 0xFA, 0x91, 0x66, 0x28, 0xDF,
+               0x6A, 0x9D, 0xD3, 0x24, 0x4F, 0xB8, 0xF6, 0x01,
+               0x20, 0xD7, 0x99, 0x6E, 0x05, 0xF2, 0xBC, 0x4B,
+               0x81, 0x76, 0x38, 0xCF, 0xA4, 0x53, 0x1D, 0xEA,
+               0xCB, 0x3C, 0x72, 0x85, 0xEE, 0x19, 0x57, 0xA0,
+               0x15, 0xE2, 0xAC, 0x5B, 0x30, 0xC7, 0x89, 0x7E,
+               0x5F, 0xA8, 0xE6, 0x11, 0x7A, 0x8D, 0xC3, 0x34,
+               0xAB, 0x5C, 0x12, 0xE5, 0x8E, 0x79, 0x37, 0xC0,
+               0xE1, 0x16, 0x58, 0xAF, 0xC4, 0x33, 0x7D, 0x8A,
+               0x3F, 0xC8, 0x86, 0x71, 0x1A, 0xED, 0xA3, 0x54,
+               0x75, 0x82, 0xCC, 0x3B, 0x50, 0xA7, 0xE9, 0x1E,
+               0xD4, 0x23, 0x6D, 0x9A, 0xF1, 0x06, 0x48, 0xBF,
+               0x9E, 0x69, 0x27, 0xD0, 0xBB, 0x4C, 0x02, 0xF5,
+               0x40, 0xB7, 0xF9, 0x0E, 0x65, 0x92, 0xDC, 0x2B,
+               0x0A, 0xFD, 0xB3, 0x44, 0x2F, 0xD8, 0x96, 0x61,
+               0x55, 0xA2, 0xEC, 0x1B, 0x70, 0x87, 0xC9, 0x3E,
+               0x1F, 0xE8, 0xA6, 0x51, 0x3A, 0xCD, 0x83, 0x74,
+               0xC1, 0x36, 0x78, 0x8F, 0xE4, 0x13, 0x5D, 0xAA,
+               0x8B, 0x7C, 0x32, 0xC5, 0xAE, 0x59, 0x17, 0xE0,
+               0x2A, 0xDD, 0x93, 0x64, 0x0F, 0xF8, 0xB6, 0x41,
+               0x60, 0x97, 0xD9, 0x2E, 0x45, 0xB2, 0xFC, 0x0B,
+               0xBE, 0x49, 0x07, 0xF0, 0x9B, 0x6C, 0x22, 0xD5,
+               0xF4, 0x03, 0x4D, 0xBA, 0xD1, 0x26, 0x68, 0x9F,
+       };
+       return t[crc ^ data];
+}
diff --git a/ssb_sprom/utils.h b/ssb_sprom/utils.h
new file mode 100644 (file)
index 0000000..bd60f98
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef SSB_SPROMTOOL_UTILS_H_
+#define SSB_SPROMTOOL_UTILS_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#define ssb_stringify_1(x)     #x
+#define ssb_stringify(x)       ssb_stringify_1(x)
+
+#ifdef ATTRIBUTE_FORMAT
+# undef ATTRIBUTE_FORMAT
+#endif
+#ifdef __GNUC__
+# define ATTRIBUTE_FORMAT(t, a, b)     __attribute__((format(t, a, b)))
+#else
+# define ATTRIBUTE_FORMAT(t, a, b)     /* nothing */
+#endif
+
+int prinfo(const char *fmt, ...) ATTRIBUTE_FORMAT(printf, 1, 2);
+int prerror(const char *fmt, ...) ATTRIBUTE_FORMAT(printf, 1, 2);
+int prdata(const char *fmt, ...) ATTRIBUTE_FORMAT(printf, 1, 2);
+
+void internal_error(const char *message);
+#define internal_error_on(condition) \
+       do {                                                            \
+               if (condition)                                          \
+                       internal_error(ssb_stringify(condition));       \
+       } while (0)
+
+void * malloce(size_t size);
+void * realloce(void *ptr, size_t newsize);
+
+/* CRC-8 with polynomial x^8+x^7+x^6+x^4+x^2+1 */
+uint8_t crc8(uint8_t crc, uint8_t data);
+
+#endif /* SSB_SPROMTOOL_UTILS_H_ */